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