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