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