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.Map.Entry;
  40 import java.util.Set;
  41 import java.util.function.Consumer;
  42 import java.util.function.Predicate;
  43 import java.util.regex.Matcher;
  44 import java.util.regex.Pattern;
  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.tools.javac.code.ClassFinder;
  55 import com.sun.tools.javac.code.Directive;
  56 import com.sun.tools.javac.code.Directive.ExportsDirective;
  57 import com.sun.tools.javac.code.Directive.RequiresDirective;
  58 import com.sun.tools.javac.code.Directive.RequiresFlag;
  59 import com.sun.tools.javac.code.Directive.UsesDirective;
  60 import com.sun.tools.javac.code.Flags;
  61 import com.sun.tools.javac.code.Kinds;
  62 import com.sun.tools.javac.code.ModuleFinder;
  63 import com.sun.tools.javac.code.Source;
  64 import com.sun.tools.javac.code.Symbol;
  65 import com.sun.tools.javac.code.Symbol.ClassSymbol;
  66 import com.sun.tools.javac.code.Symbol.Completer;
  67 import com.sun.tools.javac.code.Symbol.CompletionFailure;
  68 import com.sun.tools.javac.code.Symbol.MethodSymbol;
  69 import com.sun.tools.javac.code.Symbol.ModuleSymbol;
  70 import com.sun.tools.javac.code.Symbol.PackageSymbol;
  71 import com.sun.tools.javac.code.Symtab;
  72 import com.sun.tools.javac.code.Type;
  73 import com.sun.tools.javac.code.Types;
  74 import com.sun.tools.javac.jvm.ClassWriter;
  75 import com.sun.tools.javac.jvm.JNIWriter;
  76 import com.sun.tools.javac.main.Option;
  77 import com.sun.tools.javac.resources.CompilerProperties.Errors;
  78 import com.sun.tools.javac.resources.CompilerProperties.Warnings;
  79 import com.sun.tools.javac.tree.JCTree;
  80 import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
  81 import com.sun.tools.javac.tree.JCTree.JCExports;
  82 import com.sun.tools.javac.tree.JCTree.JCExpression;
  83 import com.sun.tools.javac.tree.JCTree.JCModuleDecl;
  84 import com.sun.tools.javac.tree.JCTree.JCPackageDecl;
  85 import com.sun.tools.javac.tree.JCTree.JCProvides;
  86 import com.sun.tools.javac.tree.JCTree.JCRequires;
  87 import com.sun.tools.javac.tree.JCTree.JCUses;
  88 import com.sun.tools.javac.tree.TreeInfo;
  89 import com.sun.tools.javac.util.Assert;
  90 import com.sun.tools.javac.util.Context;
  91 import com.sun.tools.javac.util.JCDiagnostic;
  92 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
  93 import com.sun.tools.javac.util.List;
  94 import com.sun.tools.javac.util.ListBuffer;
  95 import com.sun.tools.javac.util.Log;
  96 import com.sun.tools.javac.util.Name;
  97 import com.sun.tools.javac.util.Names;
  98 import com.sun.tools.javac.util.Options;
  99 
 100 import static com.sun.tools.javac.code.Flags.UNATTRIBUTED;
 101 import static com.sun.tools.javac.code.Kinds.Kind.MDL;
 102 import static com.sun.tools.javac.code.TypeTag.CLASS;
 103 
 104 import com.sun.tools.javac.tree.JCTree.JCDirective;
 105 import com.sun.tools.javac.tree.JCTree.Tag;
 106 import com.sun.tools.javac.util.Abort;
 107 import com.sun.tools.javac.util.Position;
 108 
 109 import static com.sun.tools.javac.code.Flags.ABSTRACT;
 110 import static com.sun.tools.javac.code.Flags.ENUM;
 111 import static com.sun.tools.javac.code.Flags.PUBLIC;
 112 import static com.sun.tools.javac.tree.JCTree.Tag.MODULEDEF;
 113 
 114 /**
 115  *  TODO: fill in
 116  *
 117  *  <p><b>This is NOT part of any supported API.
 118  *  If you write code that depends on this, you do so at your own risk.
 119  *  This code and its internal interfaces are subject to change or
 120  *  deletion without notice.</b>
 121  */
 122 public class Modules extends JCTree.Visitor {
 123     private static final String ALL_SYSTEM = "ALL-SYSTEM";
 124     private static final String ALL_MODULE_PATH = "ALL-MODULE-PATH";
 125 
 126     private final Log log;
 127     private final Names names;
 128     private final Symtab syms;
 129     private final Attr attr;
 130     private final TypeEnvs typeEnvs;
 131     private final Types types;
 132     private final JavaFileManager fileManager;
 133     private final ModuleFinder moduleFinder;
 134     private final boolean allowModules;
 135 
 136     public final boolean multiModuleMode;
 137 
 138     private final String moduleOverride;
 139 
 140     private final Name java_se;
 141     private final Name java_;
 142 
 143     ModuleSymbol defaultModule;
 144 
 145     private final String addExportsOpt;
 146     private Map<ModuleSymbol, Set<ExportsDirective>> addExports;
 147     private final String addReadsOpt;
 148     private Map<ModuleSymbol, Set<RequiresDirective>> addReads;
 149     private final String addModsOpt;
 150     private final Set<String> extraAddMods = new HashSet<>();
 151     private final String limitModsOpt;
 152     private final Set<String> extraLimitMods = new HashSet<>();
 153 
 154     private Set<ModuleSymbol> rootModules = null;
 155 
 156     public static Modules instance(Context context) {
 157         Modules instance = context.get(Modules.class);
 158         if (instance == null)
 159             instance = new Modules(context);
 160         return instance;
 161     }
 162 
 163     protected Modules(Context context) {
 164         context.put(Modules.class, this);
 165         log = Log.instance(context);
 166         names = Names.instance(context);
 167         syms = Symtab.instance(context);
 168         attr = Attr.instance(context);
 169         typeEnvs = TypeEnvs.instance(context);
 170         moduleFinder = ModuleFinder.instance(context);
 171         types = Types.instance(context);
 172         fileManager = context.get(JavaFileManager.class);
 173         allowModules = Source.instance(context).allowModules();
 174         Options options = Options.instance(context);
 175 
 176         moduleOverride = options.get(Option.XMODULE);
 177 
 178         multiModuleMode = fileManager.hasLocation(StandardLocation.MODULE_SOURCE_PATH);
 179         ClassWriter classWriter = ClassWriter.instance(context);
 180         classWriter.multiModuleMode = multiModuleMode;
 181         JNIWriter jniWriter = JNIWriter.instance(context);
 182         jniWriter.multiModuleMode = multiModuleMode;
 183 
 184         java_se = names.fromString("java.se");
 185         java_ = names.fromString("java.");
 186 
 187         addExportsOpt = options.get(Option.ADD_EXPORTS);
 188         addReadsOpt = options.get(Option.ADD_READS);
 189         addModsOpt = options.get(Option.ADD_MODULES);
 190         limitModsOpt = options.get(Option.LIMIT_MODULES);
 191     }
 192 
 193     int depth = -1;
 194     private void dprintln(String msg) {
 195         for (int i = 0; i < depth; i++)
 196             System.err.print("  ");
 197         System.err.println(msg);
 198     }
 199 
 200     public void addExtraAddModules(String... extras) {
 201         extraAddMods.addAll(Arrays.asList(extras));
 202     }
 203 
 204     public void addExtraLimitModules(String... extras) {
 205         extraLimitMods.addAll(Arrays.asList(extras));
 206     }
 207 
 208     boolean inInitModules;
 209     public void initModules(List<JCCompilationUnit> trees) {
 210         Assert.check(!inInitModules);
 211         try {
 212             inInitModules = true;
 213             Assert.checkNull(rootModules);
 214             enter(trees, modules -> {
 215                 Assert.checkNull(rootModules);
 216                 Assert.checkNull(allModules);
 217                 this.rootModules = modules;
 218                 setupAllModules(); //initialize the module graph
 219                 Assert.checkNonNull(allModules);
 220                 inInitModules = false;
 221             }, null);
 222         } finally {
 223             inInitModules = false;
 224         }
 225     }
 226 
 227     public boolean enter(List<JCCompilationUnit> trees, ClassSymbol c) {
 228         Assert.check(rootModules != null || inInitModules || !allowModules);
 229         return enter(trees, modules -> {}, c);
 230     }
 231 
 232     private boolean enter(List<JCCompilationUnit> trees, Consumer<Set<ModuleSymbol>> init, ClassSymbol c) {
 233         if (!allowModules) {
 234             for (JCCompilationUnit tree: trees) {
 235                 tree.modle = syms.noModule;
 236             }
 237             defaultModule = syms.noModule;
 238             return true;
 239         }
 240 
 241         int startErrors = log.nerrors;
 242 
 243         depth++;
 244         try {
 245             // scan trees for module defs
 246             Set<ModuleSymbol> roots = enterModules(trees, c);
 247 
 248             setCompilationUnitModules(trees, roots);
 249 
 250             init.accept(roots);
 251 
 252             for (ModuleSymbol msym: roots) {
 253                 msym.complete();
 254             }
 255         } catch (CompletionFailure ex) {
 256             log.error(JCDiagnostic.DiagnosticFlag.NON_DEFERRABLE, Position.NOPOS, "cant.access", ex.sym, ex.getDetailValue());
 257             if (ex instanceof ClassFinder.BadClassFile) throw new Abort();
 258         } finally {
 259             depth--;
 260         }
 261 
 262         return (log.nerrors == startErrors);
 263     }
 264 
 265     public Completer getCompleter() {
 266         return mainCompleter;
 267     }
 268 
 269     public ModuleSymbol getDefaultModule() {
 270         return defaultModule;
 271     }
 272 
 273     private Set<ModuleSymbol> enterModules(List<JCCompilationUnit> trees, ClassSymbol c) {
 274         Set<ModuleSymbol> modules = new LinkedHashSet<>();
 275         for (JCCompilationUnit tree : trees) {
 276             JavaFileObject prev = log.useSource(tree.sourcefile);
 277             try {
 278                 enterModule(tree, c, modules);
 279             } finally {
 280                 log.useSource(prev);
 281             }
 282         }
 283         return modules;
 284     }
 285 
 286 
 287     private void enterModule(JCCompilationUnit toplevel, ClassSymbol c, Set<ModuleSymbol> modules) {
 288         boolean isModuleInfo = toplevel.sourcefile.isNameCompatible("module-info", Kind.SOURCE);
 289         boolean isModuleDecl = toplevel.defs.nonEmpty() && toplevel.defs.head.hasTag(MODULEDEF);
 290         if (isModuleInfo && isModuleDecl) {
 291             JCModuleDecl decl = (JCModuleDecl) toplevel.defs.head;
 292             Name name = TreeInfo.fullName(decl.qualId);
 293             ModuleSymbol sym;
 294             if (c != null) {
 295                sym = (ModuleSymbol) c.owner;
 296                if (sym.name == null) {
 297                    //ModuleFinder.findSingleModule creates a stub of a ModuleSymbol without a name,
 298                    //fill the name here after the module-info.java has been parsed
 299                    //also enter the ModuleSymbol among modules:
 300                    syms.enterModule(sym, name);
 301                } else {
 302                    // TODO: validate name
 303                }
 304             } else {
 305                 sym = syms.enterModule(name);
 306                 if (sym.module_info.sourcefile != null && sym.module_info.sourcefile != toplevel.sourcefile) {
 307                     log.error(decl.pos(), Errors.DuplicateModule(sym));
 308                     return;
 309                 }
 310             }
 311             sym.completer = getSourceCompleter(toplevel);
 312             sym.module_info.sourcefile = toplevel.sourcefile;
 313             decl.sym = sym;
 314 
 315             if (multiModuleMode || modules.isEmpty()) {
 316                 modules.add(sym);
 317             } else {
 318                 log.error(toplevel.pos(), Errors.TooManyModules);
 319             }
 320 
 321             Env<AttrContext> provisionalEnv = new Env<>(decl, null);
 322 
 323             provisionalEnv.toplevel = toplevel;
 324             typeEnvs.put(sym, provisionalEnv);
 325         } else if (isModuleInfo) {
 326             if (multiModuleMode) {
 327                 JCTree tree = toplevel.defs.isEmpty() ? toplevel : toplevel.defs.head;
 328                 log.error(tree.pos(), Errors.ExpectedModule);
 329             }
 330         } else if (isModuleDecl) {
 331             JCTree tree = toplevel.defs.head;
 332             log.error(tree.pos(), Errors.ModuleDeclSbInModuleInfoJava);
 333         }
 334     }
 335 
 336     private void setCompilationUnitModules(List<JCCompilationUnit> trees, Set<ModuleSymbol> rootModules) {
 337         // update the module for each compilation unit
 338         if (multiModuleMode) {
 339             checkNoAllModulePath();
 340             for (JCCompilationUnit tree: trees) {
 341                 if (tree.defs.isEmpty()) {
 342                     tree.modle = syms.unnamedModule;
 343                     continue;
 344                 }
 345 
 346                 JavaFileObject prev = log.useSource(tree.sourcefile);
 347                 try {
 348                     Location locn = getModuleLocation(tree);
 349                     if (locn != null) {
 350                         Name name = names.fromString(fileManager.inferModuleName(locn));
 351                         ModuleSymbol msym;
 352                         if (tree.defs.head.hasTag(MODULEDEF)) {
 353                             JCModuleDecl decl = (JCModuleDecl) tree.defs.head;
 354                             msym = decl.sym;
 355                             if (msym.name != name) {
 356                                 log.error(decl.qualId, Errors.ModuleNameMismatch(msym.name, name));
 357                             }
 358                         } else {
 359                             msym = syms.enterModule(name);
 360                         }
 361                         if (msym.sourceLocation == null) {
 362                             msym.sourceLocation = locn;
 363                             if (fileManager.hasLocation(StandardLocation.CLASS_OUTPUT)) {
 364                                 msym.classLocation = fileManager.getModuleLocation(
 365                                         StandardLocation.CLASS_OUTPUT, msym.name.toString());
 366                             }
 367                         }
 368                         tree.modle = msym;
 369                         rootModules.add(msym);
 370                     } else {
 371                         log.error(tree.pos(), Errors.UnnamedPkgNotAllowedNamedModules);
 372                         tree.modle = syms.errModule;
 373                     }
 374                 } catch (IOException e) {
 375                     throw new Error(e); // FIXME
 376                 } finally {
 377                     log.useSource(prev);
 378                 }
 379             }
 380             if (syms.unnamedModule.sourceLocation == null) {
 381                 syms.unnamedModule.completer = getUnnamedModuleCompleter();
 382                 syms.unnamedModule.sourceLocation = StandardLocation.SOURCE_PATH;
 383                 syms.unnamedModule.classLocation = StandardLocation.CLASS_PATH;
 384             }
 385             defaultModule = syms.unnamedModule;
 386         } else {
 387             if (defaultModule == null) {
 388                 switch (rootModules.size()) {
 389                     case 0:
 390                         defaultModule = moduleFinder.findSingleModule();
 391                         if (defaultModule == syms.unnamedModule) {
 392                             if (moduleOverride != null) {
 393                                 checkNoAllModulePath();
 394                                 defaultModule = moduleFinder.findModule(names.fromString(moduleOverride));
 395                             } else {
 396                                 // Question: why not do findAllModules and initVisiblePackages here?
 397                                 // i.e. body of unnamedModuleCompleter
 398                                 defaultModule.completer = getUnnamedModuleCompleter();
 399                                 defaultModule.classLocation = StandardLocation.CLASS_PATH;
 400                             }
 401                         } else {
 402                             checkSpecifiedModule(trees, Errors.ModuleInfoWithXmoduleClasspath);
 403                             checkNoAllModulePath();
 404                             defaultModule.complete();
 405                             // Question: why not do completeModule here?
 406                             defaultModule.completer = new Completer() {
 407                                 @Override
 408                                 public void complete(Symbol sym) throws CompletionFailure {
 409                                     completeModule((ModuleSymbol) sym);
 410                                 }
 411                             };
 412                         }
 413                         rootModules.add(defaultModule);
 414                         break;
 415                     case 1:
 416                         checkSpecifiedModule(trees, Errors.ModuleInfoWithXmoduleSourcepath);
 417                         checkNoAllModulePath();
 418                         defaultModule = rootModules.iterator().next();
 419                         defaultModule.classLocation = StandardLocation.CLASS_OUTPUT;
 420                         break;
 421                     default:
 422                         Assert.error("too many modules");
 423                 }
 424                 defaultModule.sourceLocation = StandardLocation.SOURCE_PATH;
 425             } else if (rootModules.size() == 1 && defaultModule == rootModules.iterator().next()) {
 426                 defaultModule.complete();
 427                 defaultModule.completer = sym -> completeModule((ModuleSymbol) sym);
 428             } else {
 429                 Assert.check(rootModules.isEmpty());
 430                 rootModules.add(defaultModule);
 431             }
 432 
 433             if (defaultModule != syms.unnamedModule) {
 434                 syms.unnamedModule.completer = getUnnamedModuleCompleter();
 435                 syms.unnamedModule.sourceLocation = StandardLocation.SOURCE_PATH;
 436                 syms.unnamedModule.classLocation = StandardLocation.CLASS_PATH;
 437             }
 438 
 439             for (JCCompilationUnit tree: trees) {
 440                 tree.modle = defaultModule;
 441             }
 442         }
 443     }
 444 
 445     private Location getModuleLocation(JCCompilationUnit tree) throws IOException {
 446         switch (tree.defs.head.getTag()) {
 447             case MODULEDEF:
 448                 return getModuleLocation(tree.sourcefile, null);
 449 
 450             case PACKAGEDEF:
 451                 JCPackageDecl pkg = (JCPackageDecl) tree.defs.head;
 452                 return getModuleLocation(tree.sourcefile, TreeInfo.fullName(pkg.pid));
 453 
 454             default:
 455                 // code in unnamed module
 456                 return null;
 457         }
 458     }
 459 
 460     private Location getModuleLocation(JavaFileObject fo, Name pkgName) throws IOException {
 461         // For now, just check module source path.
 462         // We may want to check source path as well.
 463         return fileManager.getModuleLocation(StandardLocation.MODULE_SOURCE_PATH,
 464                 fo, (pkgName == null) ? null : pkgName.toString());
 465     }
 466 
 467     private void checkSpecifiedModule(List<JCCompilationUnit> trees, JCDiagnostic.Error error) {
 468         if (moduleOverride != null) {
 469             JavaFileObject prev = log.useSource(trees.head.sourcefile);
 470             try {
 471                 log.error(trees.head.pos(), error);
 472             } finally {
 473                 log.useSource(prev);
 474             }
 475         }
 476     }
 477 
 478     private void checkNoAllModulePath() {
 479         if (addModsOpt != null && Arrays.asList(addModsOpt.split(",")).contains(ALL_MODULE_PATH)) {
 480             log.error(Errors.AddmodsAllModulePathInvalid);
 481         }
 482     }
 483 
 484     private final Completer mainCompleter = new Completer() {
 485         @Override
 486         public void complete(Symbol sym) throws CompletionFailure {
 487             ModuleSymbol msym = moduleFinder.findModule((ModuleSymbol) sym);
 488 
 489             if (msym.kind == Kinds.Kind.ERR) {
 490                 log.error(Errors.CantFindModule(msym));
 491                 //make sure the module is initialized:
 492                 msym.directives = List.nil();
 493                 msym.exports = List.nil();
 494                 msym.provides = List.nil();
 495                 msym.requires = List.nil();
 496                 msym.uses = List.nil();
 497             } else if ((msym.flags_field & Flags.AUTOMATIC_MODULE) != 0) {
 498                 setupAutomaticModule(msym);
 499             } else {
 500                 msym.module_info.complete();
 501             }
 502 
 503             // If module-info comes from a .java file, the underlying
 504             // call of classFinder.fillIn will have called through the
 505             // source completer, to Enter, and then to Modules.enter,
 506             // which will call completeModule.
 507             // But, if module-info comes from a .class file, the underlying
 508             // call of classFinder.fillIn will just call ClassReader to read
 509             // the .class file, and so we call completeModule here.
 510             if (msym.module_info.classfile == null || msym.module_info.classfile.getKind() == Kind.CLASS) {
 511                 completeModule(msym);
 512             }
 513         }
 514 
 515         @Override
 516         public String toString() {
 517             return "mainCompleter";
 518         }
 519     };
 520 
 521     private void setupAutomaticModule(ModuleSymbol msym) throws CompletionFailure {
 522         try {
 523             ListBuffer<Directive> directives = new ListBuffer<>();
 524             ListBuffer<ExportsDirective> exports = new ListBuffer<>();
 525             Set<String> seenPackages = new HashSet<>();
 526 
 527             for (JavaFileObject clazz : fileManager.list(msym.classLocation, "", EnumSet.of(Kind.CLASS), true)) {
 528                 String binName = fileManager.inferBinaryName(msym.classLocation, clazz);
 529                 String pack = binName.lastIndexOf('.') != (-1) ? binName.substring(0, binName.lastIndexOf('.')) : ""; //unnamed package????
 530                 if (seenPackages.add(pack)) {
 531                     ExportsDirective d = new ExportsDirective(syms.enterPackage(msym, names.fromString(pack)), null);
 532                     directives.add(d);
 533                     exports.add(d);
 534                 }
 535             }
 536 
 537             msym.exports = exports.toList();
 538             msym.provides = List.nil();
 539             msym.requires = List.nil();
 540             msym.uses = List.nil();
 541             msym.directives = directives.toList();
 542             msym.flags_field |= Flags.ACYCLIC;
 543         } catch (IOException ex) {
 544             throw new IllegalStateException(ex);
 545         }
 546     }
 547 
 548     private void completeAutomaticModule(ModuleSymbol msym) throws CompletionFailure {
 549         ListBuffer<Directive> directives = new ListBuffer<>();
 550 
 551         directives.addAll(msym.directives);
 552 
 553         ListBuffer<RequiresDirective> requires = new ListBuffer<>();
 554 
 555         for (ModuleSymbol ms : allModules()) {
 556             if (ms == syms.unnamedModule || ms == msym)
 557                 continue;
 558             Set<RequiresFlag> flags = (ms.flags_field & Flags.AUTOMATIC_MODULE) != 0 ?
 559                     EnumSet.of(RequiresFlag.PUBLIC) : EnumSet.noneOf(RequiresFlag.class);
 560             RequiresDirective d = new RequiresDirective(ms, flags);
 561             directives.add(d);
 562             requires.add(d);
 563         }
 564 
 565         RequiresDirective requiresUnnamed = new RequiresDirective(syms.unnamedModule);
 566         directives.add(requiresUnnamed);
 567         requires.add(requiresUnnamed);
 568 
 569         msym.requires = requires.toList();
 570         msym.directives = directives.toList();
 571     }
 572 
 573     private Completer getSourceCompleter(JCCompilationUnit tree) {
 574         return new Completer() {
 575             @Override
 576             public void complete(Symbol sym) throws CompletionFailure {
 577                 ModuleSymbol msym = (ModuleSymbol) sym;
 578                 msym.flags_field |= UNATTRIBUTED;
 579                 ModuleVisitor v = new ModuleVisitor();
 580                 JavaFileObject prev = log.useSource(tree.sourcefile);
 581                 try {
 582                     tree.defs.head.accept(v);
 583                     checkCyclicDependencies((JCModuleDecl) tree.defs.head);
 584                     completeModule(msym);
 585                 } finally {
 586                     log.useSource(prev);
 587                     msym.flags_field &= ~UNATTRIBUTED;
 588                 }
 589             }
 590 
 591             @Override
 592             public String toString() {
 593                 return "SourceCompleter: " + tree.sourcefile.getName();
 594             }
 595 
 596         };
 597     }
 598 
 599     class ModuleVisitor extends JCTree.Visitor {
 600         private ModuleSymbol sym;
 601         private final Set<ModuleSymbol> allRequires = new HashSet<>();
 602         private final Set<PackageSymbol> allExports = new HashSet<>();
 603 
 604         @Override
 605         public void visitModuleDef(JCModuleDecl tree) {
 606             sym = Assert.checkNonNull(tree.sym);
 607 
 608             sym.requires = List.nil();
 609             sym.exports = List.nil();
 610             tree.directives.forEach(t -> t.accept(this));
 611             sym.requires = sym.requires.reverse();
 612             sym.exports = sym.exports.reverse();
 613             ensureJavaBase();
 614         }
 615 
 616         @Override
 617         public void visitRequires(JCRequires tree) {
 618             ModuleSymbol msym = lookupModule(tree.moduleName);
 619             if (msym.kind != MDL) {
 620                 log.error(tree.moduleName.pos(), Errors.ModuleNotFound(msym));
 621             } else if (allRequires.contains(msym)) {
 622                 log.error(tree.moduleName.pos(), Errors.DuplicateRequires(msym));
 623             } else {
 624                 allRequires.add(msym);
 625                 Set<RequiresFlag> flags = EnumSet.noneOf(RequiresFlag.class);
 626                 if (tree.isPublic)
 627                     flags.add(RequiresFlag.PUBLIC);
 628                 RequiresDirective d = new RequiresDirective(msym, flags);
 629                 tree.directive = d;
 630                 sym.requires = sym.requires.prepend(d);
 631             }
 632         }
 633 
 634         @Override
 635         public void visitExports(JCExports tree) {
 636             Name name = TreeInfo.fullName(tree.qualid);
 637             PackageSymbol packge = syms.enterPackage(sym, name);
 638             attr.setPackageSymbols(tree.qualid, packge);
 639             if (!allExports.add(packge)) {
 640                 log.error(tree.qualid.pos(), Errors.DuplicateExports(packge));
 641             }
 642 
 643             List<ModuleSymbol> toModules = null;
 644             if (tree.moduleNames != null) {
 645                 Set<ModuleSymbol> to = new HashSet<>();
 646                 for (JCExpression n: tree.moduleNames) {
 647                     ModuleSymbol msym = lookupModule(n);
 648                     if (msym.kind != MDL) {
 649                         log.error(n.pos(), Errors.ModuleNotFound(msym));
 650                     } else if (!to.add(msym)) {
 651                         log.error(n.pos(), Errors.DuplicateExports(msym));
 652                     }
 653                 }
 654                 toModules = List.from(to);
 655             }
 656 
 657             if (toModules == null || !toModules.isEmpty()) {
 658                 ExportsDirective d = new ExportsDirective(packge, toModules);
 659                 tree.directive = d;
 660                 sym.exports = sym.exports.prepend(d);
 661             }
 662         }
 663 
 664         @Override
 665         public void visitProvides(JCProvides tree) { }
 666 
 667         @Override
 668         public void visitUses(JCUses tree) { }
 669 
 670         private void ensureJavaBase() {
 671             if (sym.name == names.java_base)
 672                 return;
 673 
 674             for (RequiresDirective d: sym.requires) {
 675                 if (d.module.name == names.java_base)
 676                     return;
 677             }
 678 
 679             ModuleSymbol java_base = syms.enterModule(names.java_base);
 680             Directive.RequiresDirective d =
 681                     new Directive.RequiresDirective(java_base,
 682                             EnumSet.of(Directive.RequiresFlag.MANDATED));
 683             sym.requires = sym.requires.prepend(d);
 684         }
 685 
 686         private ModuleSymbol lookupModule(JCExpression moduleName) {
 687             try {
 688             Name name = TreeInfo.fullName(moduleName);
 689             ModuleSymbol msym = moduleFinder.findModule(name);
 690             TreeInfo.setSymbol(moduleName, msym);
 691             return msym;
 692             } catch (Throwable t) {
 693                 System.err.println("Module " + sym + "; lookup export " + moduleName);
 694                 throw t;
 695             }
 696         }
 697     }
 698 
 699     public Completer getUsesProvidesCompleter() {
 700         return sym -> {
 701             ModuleSymbol msym = (ModuleSymbol) sym;
 702 
 703             msym.complete();
 704 
 705             Env<AttrContext> env = typeEnvs.get(msym);
 706             UsesProvidesVisitor v = new UsesProvidesVisitor(msym, env);
 707             JavaFileObject prev = log.useSource(env.toplevel.sourcefile);
 708             try {
 709                 env.toplevel.defs.head.accept(v);
 710             } finally {
 711                 log.useSource(prev);
 712             }
 713         };
 714     }
 715 
 716     class UsesProvidesVisitor extends JCTree.Visitor {
 717         private final ModuleSymbol msym;
 718         private final Env<AttrContext> env;
 719 
 720         private final Set<Directive.UsesDirective> allUses = new HashSet<>();
 721         private final Set<Directive.ProvidesDirective> allProvides = new HashSet<>();
 722 
 723         public UsesProvidesVisitor(ModuleSymbol msym, Env<AttrContext> env) {
 724             this.msym = msym;
 725             this.env = env;
 726         }
 727 
 728         @Override @SuppressWarnings("unchecked")
 729         public void visitModuleDef(JCModuleDecl tree) {
 730             msym.directives = List.nil();
 731             msym.provides = List.nil();
 732             msym.uses = List.nil();
 733             tree.directives.forEach(t -> t.accept(this));
 734             msym.directives = msym.directives.reverse();
 735             msym.provides = msym.provides.reverse();
 736             msym.uses = msym.uses.reverse();
 737 
 738             if (msym.requires.nonEmpty() && msym.requires.head.flags.contains(RequiresFlag.MANDATED))
 739                 msym.directives = msym.directives.prepend(msym.requires.head);
 740 
 741             msym.directives = msym.directives.appendList(List.from(addReads.getOrDefault(msym, Collections.emptySet())));
 742 
 743             checkForCorrectness();
 744         }
 745 
 746         @Override
 747         public void visitExports(JCExports tree) {
 748             if (tree.directive.packge.members().isEmpty()) {
 749                 log.error(tree.qualid.pos(), Errors.PackageEmptyOrNotFound(tree.directive.packge));
 750             }
 751             msym.directives = msym.directives.prepend(tree.directive);
 752         }
 753 
 754         MethodSymbol noArgsConstructor(ClassSymbol tsym) {
 755             for (Symbol sym : tsym.members().getSymbolsByName(names.init)) {
 756                 MethodSymbol mSym = (MethodSymbol)sym;
 757                 if (mSym.params().isEmpty()) {
 758                     return mSym;
 759                 }
 760             }
 761             return null;
 762         }
 763 
 764         Map<Directive.ProvidesDirective, JCProvides> directiveToTreeMap = new HashMap<>();
 765 
 766         @Override
 767         public void visitProvides(JCProvides tree) {
 768             Type st = attr.attribType(tree.serviceName, env, syms.objectType);
 769             Type it = attr.attribType(tree.implName, env, syms.objectType);
 770             ClassSymbol service = (ClassSymbol) st.tsym;
 771             ClassSymbol impl = (ClassSymbol) it.tsym;
 772             if (!types.isSubtype(it, st)) {
 773                 log.error(tree.implName.pos(), Errors.ServiceImplementationMustBeSubtypeOfServiceInterface);
 774             }
 775             if ((impl.flags() & ABSTRACT) != 0) {
 776                 log.error(tree.implName.pos(), Errors.ServiceImplementationIsAbstract(impl));
 777             } else if (impl.isInner()) {
 778                 log.error(tree.implName.pos(), Errors.ServiceImplementationIsInner(impl));
 779             } else if (service.isInner()) {
 780                 log.error(tree.serviceName.pos(), Errors.ServiceDefinitionIsInner(service));
 781             } else {
 782                 MethodSymbol constr = noArgsConstructor(impl);
 783                 if (constr == null) {
 784                     log.error(tree.implName.pos(), Errors.ServiceImplementationDoesntHaveANoArgsConstructor(impl));
 785                 } else if ((constr.flags() & PUBLIC) == 0) {
 786                     log.error(tree.implName.pos(), Errors.ServiceImplementationNoArgsConstructorNotPublic(impl));
 787                 }
 788             }
 789             if (st.hasTag(CLASS) && it.hasTag(CLASS)) {
 790                 Directive.ProvidesDirective d = new Directive.ProvidesDirective(service, impl);
 791                 if (!allProvides.add(d)) {
 792                     log.error(tree.pos(), Errors.DuplicateProvides(service, impl));
 793                 }
 794                 msym.provides = msym.provides.prepend(d);
 795                 msym.directives = msym.directives.prepend(d);
 796                 directiveToTreeMap.put(d, tree);
 797             }
 798         }
 799 
 800         @Override
 801         public void visitRequires(JCRequires tree) {
 802             if (tree.directive != null) {
 803                 msym.directives = msym.directives.prepend(tree.directive);
 804             }
 805         }
 806 
 807         @Override
 808         public void visitUses(JCUses tree) {
 809             Type st = attr.attribType(tree.qualid, env, syms.objectType);
 810             Symbol sym = TreeInfo.symbol(tree.qualid);
 811             if ((sym.flags() & ENUM) != 0) {
 812                 log.error(tree.qualid.pos(), Errors.ServiceDefinitionIsEnum(st.tsym));
 813             } else if (st.hasTag(CLASS)) {
 814                 ClassSymbol service = (ClassSymbol) st.tsym;
 815                 Directive.UsesDirective d = new Directive.UsesDirective(service);
 816                 if (!allUses.add(d)) {
 817                     log.error(tree.pos(), Errors.DuplicateUses(service));
 818                 }
 819                 msym.uses = msym.uses.prepend(d);
 820                 msym.directives = msym.directives.prepend(d);
 821             }
 822         }
 823 
 824         private void checkForCorrectness() {
 825             for (Directive.ProvidesDirective provides : allProvides) {
 826                 JCProvides tree = directiveToTreeMap.get(provides);
 827                 /* The implementation must be defined in the same module as the provides directive
 828                  * (else, error)
 829                  */
 830                 PackageSymbol implementationDefiningPackage = provides.impl.packge();
 831                 if (implementationDefiningPackage.modle != msym) {
 832                     log.error(tree.pos(), Errors.ServiceImplementationNotInRightModule(implementationDefiningPackage.modle));
 833                 }
 834 
 835                 /* There is no inherent requirement that module that provides a service should actually
 836                  * use it itself. However, it is a pointless declaration if the service package is not
 837                  * exported and there is no uses for the service.
 838                  */
 839                 PackageSymbol interfaceDeclaringPackage = provides.service.packge();
 840                 boolean isInterfaceDeclaredInCurrentModule = interfaceDeclaringPackage.modle == msym;
 841                 boolean isInterfaceExportedFromAReadableModule =
 842                         msym.visiblePackages.get(interfaceDeclaringPackage.fullname) == interfaceDeclaringPackage;
 843                 if (isInterfaceDeclaredInCurrentModule && !isInterfaceExportedFromAReadableModule) {
 844                     // ok the interface is declared in this module. Let's check if it's exported
 845                     boolean warn = true;
 846                     for (ExportsDirective export : msym.exports) {
 847                         if (interfaceDeclaringPackage == export.packge) {
 848                             warn = false;
 849                             break;
 850                         }
 851                     }
 852                     if (warn) {
 853                         for (UsesDirective uses : msym.uses) {
 854                             if (provides.service == uses.service) {
 855                                 warn = false;
 856                                 break;
 857                             }
 858                         }
 859                     }
 860                     if (warn) {
 861                         log.warning(tree.pos(), Warnings.ServiceProvidedButNotExportedOrUsed(provides.service));
 862                     }
 863                 }
 864             }
 865         }
 866     }
 867 
 868     private Set<ModuleSymbol> allModules;
 869 
 870     public Set<ModuleSymbol> allModules() {
 871         Assert.checkNonNull(allModules);
 872         return allModules;
 873     }
 874 
 875     private void setupAllModules() {
 876         Assert.checkNonNull(rootModules);
 877         Assert.checkNull(allModules);
 878 
 879         Set<ModuleSymbol> observable;
 880 
 881         if (limitModsOpt == null && extraLimitMods.isEmpty()) {
 882             observable = null;
 883         } else {
 884             Set<ModuleSymbol> limitMods = new HashSet<>();
 885             if (limitModsOpt != null) {
 886                 for (String limit : limitModsOpt.split(",")) {
 887                     limitMods.add(syms.enterModule(names.fromString(limit)));
 888                 }
 889             }
 890             for (String limit : extraLimitMods) {
 891                 limitMods.add(syms.enterModule(names.fromString(limit)));
 892             }
 893             observable = computeTransitiveClosure(limitMods, null);
 894             observable.addAll(rootModules);
 895         }
 896 
 897         Predicate<ModuleSymbol> observablePred = sym -> observable == null || observable.contains(sym);
 898         Predicate<ModuleSymbol> systemModulePred = sym -> (sym.flags() & Flags.SYSTEM_MODULE) != 0;
 899         Set<ModuleSymbol> enabledRoot = new LinkedHashSet<>();
 900 
 901         if (rootModules.contains(syms.unnamedModule)) {
 902             ModuleSymbol javaSE = syms.getModule(java_se);
 903             Predicate<ModuleSymbol> jdkModulePred;
 904 
 905             if (javaSE != null && (observable == null || observable.contains(javaSE))) {
 906                 jdkModulePred = sym -> {
 907                     sym.complete();
 908                     return   !sym.name.startsWith(java_)
 909                            && sym.exports.stream().anyMatch(e -> e.modules == null);
 910                 };
 911                 enabledRoot.add(javaSE);
 912             } else {
 913                 jdkModulePred = sym -> true;
 914             }
 915 
 916             for (ModuleSymbol sym : new HashSet<>(syms.getAllModules())) {
 917                 if (systemModulePred.test(sym) && observablePred.test(sym) && jdkModulePred.test(sym)) {
 918                     enabledRoot.add(sym);
 919                 }
 920             }
 921         }
 922 
 923         enabledRoot.addAll(rootModules);
 924 
 925         if (addModsOpt != null || !extraAddMods.isEmpty()) {
 926             Set<String> fullAddMods = new HashSet<>();
 927             fullAddMods.addAll(extraAddMods);
 928 
 929             if (addModsOpt != null) {
 930                 fullAddMods.addAll(Arrays.asList(addModsOpt.split(",")));
 931             }
 932 
 933             for (String added : fullAddMods) {
 934                 Stream<ModuleSymbol> modules;
 935                 switch (added) {
 936                     case ALL_SYSTEM:
 937                         modules = syms.getAllModules()
 938                                       .stream()
 939                                       .filter(systemModulePred.and(observablePred));
 940                         break;
 941                     case ALL_MODULE_PATH:
 942                         modules = syms.getAllModules()
 943                                       .stream()
 944                                       .filter(systemModulePred.negate().and(observablePred));
 945                         break;
 946                     default:
 947                         modules = Stream.of(syms.enterModule(names.fromString(added)));
 948                         break;
 949                 }
 950                 modules.forEach(sym -> {
 951                     enabledRoot.add(sym);
 952                     if (observable != null)
 953                         observable.add(sym);
 954                 });
 955             }
 956         }
 957 
 958         Set<ModuleSymbol> result = computeTransitiveClosure(enabledRoot, observable);
 959 
 960         result.add(syms.unnamedModule);
 961 
 962         allModules = result;
 963     }
 964 
 965     private Set<ModuleSymbol> computeTransitiveClosure(Iterable<? extends ModuleSymbol> base, Set<ModuleSymbol> observable) {
 966         List<ModuleSymbol> todo = List.nil();
 967 
 968         for (ModuleSymbol ms : base) {
 969             todo = todo.prepend(ms);
 970         }
 971 
 972         Set<ModuleSymbol> result = new LinkedHashSet<>();
 973         result.add(syms.java_base);
 974 
 975         while (todo.nonEmpty()) {
 976             ModuleSymbol current = todo.head;
 977             todo = todo.tail;
 978             if (observable != null && !observable.contains(current))
 979                 continue;
 980             if (!result.add(current) || current == syms.unnamedModule || ((current.flags_field & Flags.AUTOMATIC_MODULE) != 0))
 981                 continue;
 982             current.complete();
 983             for (RequiresDirective rd : current.requires) {
 984                 todo = todo.prepend(rd.module);
 985             }
 986         }
 987 
 988         return result;
 989     }
 990 
 991     public ModuleSymbol getObservableModule(Name name) {
 992         ModuleSymbol mod = syms.getModule(name);
 993 
 994         if (allModules().contains(mod)) {
 995             return mod;
 996         }
 997 
 998         return null;
 999     }
1000 
1001     private Completer getUnnamedModuleCompleter() {
1002         moduleFinder.findAllModules();
1003         return new Symbol.Completer() {
1004             @Override
1005             public void complete(Symbol sym) throws CompletionFailure {
1006                 ModuleSymbol msym = (ModuleSymbol) sym;
1007                 Set<ModuleSymbol> allModules = allModules();
1008                 for (ModuleSymbol m : allModules) {
1009                     m.complete();
1010                 }
1011                 initVisiblePackages(msym, allModules);
1012             }
1013 
1014             @Override
1015             public String toString() {
1016                 return "unnamedModule Completer";
1017             }
1018         };
1019     }
1020 
1021     private final Map<ModuleSymbol, Set<ModuleSymbol>> requiresPublicCache = new HashMap<>();
1022 
1023     private void completeModule(ModuleSymbol msym) {
1024         if (inInitModules) {
1025             msym.completer = sym -> completeModule(msym);
1026             return ;
1027         }
1028 
1029         if ((msym.flags_field & Flags.AUTOMATIC_MODULE) != 0) {
1030             completeAutomaticModule(msym);
1031         }
1032 
1033         Assert.checkNonNull(msym.requires);
1034 
1035         initAddReads();
1036 
1037         msym.requires = msym.requires.appendList(List.from(addReads.getOrDefault(msym, Collections.emptySet())));
1038 
1039         List<RequiresDirective> requires = msym.requires;
1040         List<RequiresDirective> previous = null;
1041 
1042         while (requires.nonEmpty()) {
1043             if (!allModules().contains(requires.head.module)) {
1044                 Env<AttrContext> env = typeEnvs.get(msym);
1045                 if (env != null) {
1046                     JavaFileObject origSource = log.useSource(env.toplevel.sourcefile);
1047                     try {
1048                         log.error(/*XXX*/env.tree, Errors.ModuleNotFound(requires.head.module));
1049                     } finally {
1050                         log.useSource(origSource);
1051                     }
1052                 } else {
1053                     Assert.check((msym.flags() & Flags.AUTOMATIC_MODULE) == 0);
1054                 }
1055                 if (previous != null) {
1056                     previous.tail = requires.tail;
1057                 } else {
1058                     msym.requires.tail = requires.tail;
1059                 }
1060             } else {
1061                 previous = requires;
1062             }
1063             requires = requires.tail;
1064         }
1065 
1066         Set<ModuleSymbol> readable = new LinkedHashSet<>();
1067         Set<ModuleSymbol> requiresPublic = new HashSet<>();
1068 
1069         for (RequiresDirective d : msym.requires) {
1070             d.module.complete();
1071             readable.add(d.module);
1072             Set<ModuleSymbol> s = retrieveRequiresPublic(d.module);
1073             Assert.checkNonNull(s, () -> "no entry in cache for " + d.module);
1074             readable.addAll(s);
1075             if (d.flags.contains(RequiresFlag.PUBLIC)) {
1076                 requiresPublic.add(d.module);
1077                 requiresPublic.addAll(s);
1078             }
1079         }
1080 
1081         requiresPublicCache.put(msym, requiresPublic);
1082         initVisiblePackages(msym, readable);
1083         for (ExportsDirective d: msym.exports) {
1084             d.packge.modle = msym;
1085         }
1086 
1087     }
1088 
1089     private Set<ModuleSymbol> retrieveRequiresPublic(ModuleSymbol msym) {
1090         Set<ModuleSymbol> requiresPublic = requiresPublicCache.get(msym);
1091 
1092         if (requiresPublic == null) {
1093             //the module graph may contain cycles involving automatic modules or --add-reads edges
1094             requiresPublic = new HashSet<>();
1095 
1096             Set<ModuleSymbol> seen = new HashSet<>();
1097             List<ModuleSymbol> todo = List.of(msym);
1098 
1099             while (todo.nonEmpty()) {
1100                 ModuleSymbol current = todo.head;
1101                 todo = todo.tail;
1102                 if (!seen.add(current))
1103                     continue;
1104                 requiresPublic.add(current);
1105                 current.complete();
1106                 Iterable<? extends RequiresDirective> requires;
1107                 if (current != syms.unnamedModule) {
1108                     Assert.checkNonNull(current.requires, () -> current + ".requires == null; " + msym);
1109                     requires = current.requires;
1110                     for (RequiresDirective rd : requires) {
1111                         if (rd.isPublic())
1112                             todo = todo.prepend(rd.module);
1113                     }
1114                 } else {
1115                     for (ModuleSymbol mod : allModules()) {
1116                         todo = todo.prepend(mod);
1117                     }
1118                 }
1119             }
1120 
1121             requiresPublic.remove(msym);
1122         }
1123 
1124         return requiresPublic;
1125     }
1126 
1127     private void initVisiblePackages(ModuleSymbol msym, Collection<ModuleSymbol> readable) {
1128         initAddExports();
1129 
1130         msym.visiblePackages = new LinkedHashMap<>();
1131 
1132         Map<Name, ModuleSymbol> seen = new HashMap<>();
1133 
1134         for (ModuleSymbol rm : readable) {
1135             if (rm == syms.unnamedModule)
1136                 continue;
1137             addVisiblePackages(msym, seen, rm, rm.exports);
1138         }
1139 
1140         for (Entry<ModuleSymbol, Set<ExportsDirective>> addExportsEntry : addExports.entrySet())
1141             addVisiblePackages(msym, seen, addExportsEntry.getKey(), addExportsEntry.getValue());
1142     }
1143 
1144     private void addVisiblePackages(ModuleSymbol msym,
1145                                     Map<Name, ModuleSymbol> seenPackages,
1146                                     ModuleSymbol exportsFrom,
1147                                     Collection<ExportsDirective> exports) {
1148         for (ExportsDirective d : exports) {
1149             if (d.modules == null || d.modules.contains(msym)) {
1150                 Name packageName = d.packge.fullname;
1151                 ModuleSymbol previousModule = seenPackages.get(packageName);
1152 
1153                 if (previousModule != null && previousModule != exportsFrom) {
1154                     Env<AttrContext> env = typeEnvs.get(msym);
1155                     JavaFileObject origSource = env != null ? log.useSource(env.toplevel.sourcefile)
1156                                                             : null;
1157                     DiagnosticPosition pos = env != null ? env.tree.pos() : null;
1158                     try {
1159                         log.error(pos, Errors.PackageClashFromRequires(msym, packageName,
1160                                                                       previousModule, exportsFrom));
1161                     } finally {
1162                         if (env != null)
1163                             log.useSource(origSource);
1164                     }
1165                     continue;
1166                 }
1167 
1168                 seenPackages.put(packageName, exportsFrom);
1169                 msym.visiblePackages.put(d.packge.fullname, d.packge);
1170             }
1171         }
1172     }
1173 
1174     private void initAddExports() {
1175         if (addExports != null)
1176             return;
1177 
1178         addExports = new LinkedHashMap<>();
1179 
1180         if (addExportsOpt == null)
1181             return;
1182 
1183 //        System.err.println("Modules.addExports:\n   " + addExportsOpt.replace("\0", "\n   "));
1184 
1185         Pattern ep = Pattern.compile("([^/]+)/([^=]+)=(.*)");
1186         for (String s: addExportsOpt.split("\0+")) {
1187             if (s.isEmpty())
1188                 continue;
1189             Matcher em = ep.matcher(s);
1190             if (!em.matches()) {
1191                 continue;
1192             }
1193 
1194             // Terminology comes from
1195             //  --add-exports module/package=target,...
1196             // Compare to
1197             //  module module { exports package to target, ... }
1198             String moduleName = em.group(1);
1199             String packageName = em.group(2);
1200             String targetNames = em.group(3);
1201 
1202             ModuleSymbol msym = syms.enterModule(names.fromString(moduleName));
1203             PackageSymbol p = syms.enterPackage(msym, names.fromString(packageName));
1204             p.modle = msym;  // TODO: do we need this?
1205 
1206             List<ModuleSymbol> targetModules = List.nil();
1207             for (String toModule : targetNames.split("[ ,]+")) {
1208                 ModuleSymbol m;
1209                 if (toModule.equals("ALL-UNNAMED")) {
1210                     m = syms.unnamedModule;
1211                 } else {
1212                     if (!SourceVersion.isName(toModule)) {
1213                         // TODO: error: invalid module name
1214                         continue;
1215                     }
1216                     m = syms.enterModule(names.fromString(toModule));
1217                 }
1218                 targetModules = targetModules.prepend(m);
1219             }
1220 
1221             Set<ExportsDirective> extra = addExports.computeIfAbsent(msym, _x -> new LinkedHashSet<>());
1222             ExportsDirective d = new ExportsDirective(p, targetModules);
1223             extra.add(d);
1224         }
1225     }
1226 
1227     private void initAddReads() {
1228         if (addReads != null)
1229             return;
1230 
1231         addReads = new LinkedHashMap<>();
1232 
1233         if (addReadsOpt == null)
1234             return;
1235 
1236 //        System.err.println("Modules.addReads:\n   " + addReadsOpt.replace("\0", "\n   "));
1237 
1238         Pattern rp = Pattern.compile("([^=]+)=(.*)");
1239         for (String s : addReadsOpt.split("\0+")) {
1240             if (s.isEmpty())
1241                 continue;
1242             Matcher rm = rp.matcher(s);
1243             if (!rm.matches()) {
1244                 continue;
1245             }
1246 
1247             // Terminology comes from
1248             //  --add-reads target-module=source-module,...
1249             // Compare to
1250             //  module target-module { requires source-module; ... }
1251             String targetName = rm.group(1);
1252             String sources = rm.group(2);
1253 
1254             ModuleSymbol msym = syms.enterModule(names.fromString(targetName));
1255             for (String source : sources.split("[ ,]+")) {
1256                 ModuleSymbol sourceModule;
1257                 if (source.equals("ALL-UNNAMED")) {
1258                     sourceModule = syms.unnamedModule;
1259                 } else {
1260                     if (!SourceVersion.isName(source)) {
1261                         // TODO: error: invalid module name
1262                         continue;
1263                     }
1264                     sourceModule = syms.enterModule(names.fromString(source));
1265                 }
1266                 addReads.computeIfAbsent(msym, m -> new HashSet<>())
1267                         .add(new RequiresDirective(sourceModule, EnumSet.of(RequiresFlag.EXTRA)));
1268             }
1269         }
1270     }
1271 
1272     private void checkCyclicDependencies(JCModuleDecl mod) {
1273         for (JCDirective d : mod.directives) {
1274             JCRequires rd;
1275             if (!d.hasTag(Tag.REQUIRES) || (rd = (JCRequires) d).directive == null)
1276                 continue;
1277             Set<ModuleSymbol> nonSyntheticDeps = new HashSet<>();
1278             List<ModuleSymbol> queue = List.of(rd.directive.module);
1279             while (queue.nonEmpty()) {
1280                 ModuleSymbol current = queue.head;
1281                 queue = queue.tail;
1282                 if (!nonSyntheticDeps.add(current))
1283                     continue;
1284                 current.complete();
1285                 if ((current.flags() & Flags.ACYCLIC) != 0)
1286                     continue;
1287                 Assert.checkNonNull(current.requires, () -> current.toString());
1288                 for (RequiresDirective dep : current.requires) {
1289                     if (!dep.flags.contains(RequiresFlag.EXTRA))
1290                         queue = queue.prepend(dep.module);
1291                 }
1292             }
1293             if (nonSyntheticDeps.contains(mod.sym)) {
1294                 log.error(rd.moduleName.pos(), Errors.CyclicRequires(rd.directive.module));
1295             }
1296             mod.sym.flags_field |= Flags.ACYCLIC;
1297         }
1298     }
1299 
1300     // DEBUG
1301     private String toString(ModuleSymbol msym) {
1302         return msym.name + "["
1303                 + "kind:" + msym.kind + ";"
1304                 + "locn:" + toString(msym.sourceLocation) + "," + toString(msym.classLocation) + ";"
1305                 + "info:" + toString(msym.module_info.sourcefile) + ","
1306                             + toString(msym.module_info.classfile) + ","
1307                             + msym.module_info.completer
1308                 + "]";
1309     }
1310 
1311     // DEBUG
1312     String toString(Location locn) {
1313         return (locn == null) ? "--" : locn.getName();
1314     }
1315 
1316     // DEBUG
1317     String toString(JavaFileObject fo) {
1318         return (fo == null) ? "--" : fo.getName();
1319     }
1320 
1321     public void newRound() {
1322         rootModules = null;
1323         allModules = null;
1324     }
1325 }