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