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