1 /*
2 * Copyright (c) 2009, 2021, 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 private final boolean sourceLauncher;
169
170 private final boolean lintOptions;
171
172 private Set<ModuleSymbol> rootModules = null;
173 private final Set<ModuleSymbol> warnedMissing = new HashSet<>();
174
175 public PackageNameFinder findPackageInFile;
176
177 public static Modules instance(Context context) {
178 Modules instance = context.get(Modules.class);
179 if (instance == null)
180 instance = new Modules(context);
181 return instance;
182 }
183
184 protected Modules(Context context) {
185 context.put(Modules.class, this);
186 log = Log.instance(context);
187 names = Names.instance(context);
188 syms = Symtab.instance(context);
189 attr = Attr.instance(context);
190 chk = Check.instance(context);
191 deferredLintHandler = DeferredLintHandler.instance(context);
192 typeEnvs = TypeEnvs.instance(context);
193 moduleFinder = ModuleFinder.instance(context);
194 types = Types.instance(context);
195 fileManager = context.get(JavaFileManager.class);
196 source = Source.instance(context);
197 target = Target.instance(context);
198 allowModules = Feature.MODULES.allowedInSource(source);
199 Options options = Options.instance(context);
200
201 allowAccessIntoSystem = options.isUnset(Option.RELEASE);
202 lintOptions = options.isUnset(Option.XLINT_CUSTOM, "-" + LintCategory.OPTIONS.option);
203
204 multiModuleMode = fileManager.hasLocation(StandardLocation.MODULE_SOURCE_PATH);
205 ClassWriter classWriter = ClassWriter.instance(context);
206 classWriter.multiModuleMode = multiModuleMode;
207 JNIWriter jniWriter = JNIWriter.instance(context);
208 jniWriter.multiModuleMode = multiModuleMode;
209
210 java_se = names.fromString("java.se");
211 java_ = names.fromString("java.");
212
213 addExportsOpt = options.get(Option.ADD_EXPORTS);
214 addReadsOpt = options.get(Option.ADD_READS);
215 addModsOpt = options.get(Option.ADD_MODULES);
216 limitModsOpt = options.get(Option.LIMIT_MODULES);
217 moduleVersionOpt = options.get(Option.MODULE_VERSION);
218 sourceLauncher = options.isSet("sourceLauncher");
219 }
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.classfile = 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 boolean patchesAutomaticModules = false;
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 patchesAutomaticModules |= (msym.flags_field & Flags.AUTOMATIC_MODULE) != 0;
379
380 if (msplocn != null) {
381 Name mspname = names.fromString(fileManager.inferModuleName(msplocn));
382 if (name != mspname) {
383 log.error(tree.pos(), Errors.FilePatchedAndMsp(name, mspname));
384 }
385 }
386 } else if (msplocn != null) {
387 if (tree.getModuleDecl() != null) {
388 JavaFileObject canonical =
389 fileManager.getJavaFileForInput(msplocn, "module-info", Kind.SOURCE);
390 if (canonical == null || !fileManager.isSameFile(canonical, tree.sourcefile)) {
391 log.error(tree.pos(), Errors.ModuleNotFoundOnModuleSourcePath);
392 }
393 }
394 Name name = names.fromString(fileManager.inferModuleName(msplocn));
395 ModuleSymbol msym;
396 JCModuleDecl decl = tree.getModuleDecl();
397 if (decl != null) {
398 msym = decl.sym;
399 if (msym.name != name) {
400 log.error(decl.qualId, Errors.ModuleNameMismatch(msym.name, name));
401 }
402 } else {
403 if (tree.getPackage() == null) {
404 log.error(tree.pos(), Errors.UnnamedPkgNotAllowedNamedModules);
405 }
406 msym = syms.enterModule(name);
407 }
408 if (msym.sourceLocation == null) {
409 msym.sourceLocation = msplocn;
410 if (fileManager.hasLocation(StandardLocation.PATCH_MODULE_PATH)) {
411 msym.patchLocation = fileManager.getLocationForModule(
412 StandardLocation.PATCH_MODULE_PATH, msym.name.toString());
413 }
414 if (fileManager.hasLocation(StandardLocation.CLASS_OUTPUT)) {
415 Location outputLocn = fileManager.getLocationForModule(
416 StandardLocation.CLASS_OUTPUT, msym.name.toString());
417 if (msym.patchLocation == null) {
418 msym.classLocation = outputLocn;
419 } else {
420 msym.patchOutputLocation = outputLocn;
421 }
422 }
423 }
424 tree.modle = msym;
425 rootModules.add(msym);
426 } else if (c != null && c.packge().modle == syms.unnamedModule) {
427 tree.modle = syms.unnamedModule;
428 } else {
429 if (tree.getModuleDecl() != null) {
430 log.error(tree.pos(), Errors.ModuleNotFoundOnModuleSourcePath);
431 } else {
432 log.error(tree.pos(), Errors.NotInModuleOnModuleSourcePath);
433 }
434 tree.modle = syms.errModule;
435 }
436 } catch (IOException e) {
437 throw new Error(e); // FIXME
438 } finally {
439 log.useSource(prev);
440 }
441 }
442 if (!patchesAutomaticModules) {
443 checkNoAllModulePath();
444 }
445 if (syms.unnamedModule.sourceLocation == null) {
446 syms.unnamedModule.completer = getUnnamedModuleCompleter();
447 syms.unnamedModule.sourceLocation = StandardLocation.SOURCE_PATH;
448 syms.unnamedModule.classLocation = StandardLocation.CLASS_PATH;
449 }
450 defaultModule = syms.unnamedModule;
451 } else {
452 ModuleSymbol module = null;
453 if (defaultModule == null) {
454 String moduleOverride = singleModuleOverride(trees);
455 switch (rootModules.size()) {
456 case 0:
457 try {
458 defaultModule = moduleFinder.findSingleModule();
459 } catch (CompletionFailure cf) {
460 chk.completionError(null, cf);
461 defaultModule = syms.unnamedModule;
462 }
463 if (defaultModule == syms.unnamedModule) {
464 if (moduleOverride != null) {
465 defaultModule = moduleFinder.findModule(names.fromString(moduleOverride));
466 defaultModule.patchOutputLocation = StandardLocation.CLASS_OUTPUT;
467 if ((defaultModule.flags_field & Flags.AUTOMATIC_MODULE) == 0) {
468 checkNoAllModulePath();
469 }
470 } else {
471 // Question: why not do findAllModules and initVisiblePackages here?
472 // i.e. body of unnamedModuleCompleter
473 defaultModule.completer = getUnnamedModuleCompleter();
474 defaultModule.sourceLocation = StandardLocation.SOURCE_PATH;
475 defaultModule.classLocation = StandardLocation.CLASS_PATH;
476 }
477 } else {
478 checkNoAllModulePath();
479 defaultModule.complete();
480 // Question: why not do completeModule here?
481 defaultModule.completer = sym -> completeModule((ModuleSymbol) sym);
482 defaultModule.sourceLocation = StandardLocation.SOURCE_PATH;
483 }
484 rootModules.add(defaultModule);
485 break;
486 case 1:
487 checkNoAllModulePath();
488 defaultModule = rootModules.iterator().next();
489 defaultModule.sourceLocation = StandardLocation.SOURCE_PATH;
490 if (fileManager.hasLocation(StandardLocation.PATCH_MODULE_PATH)) {
491 try {
492 defaultModule.patchLocation = fileManager.getLocationForModule(
493 StandardLocation.PATCH_MODULE_PATH, defaultModule.name.toString());
494 } catch (IOException ex) {
495 throw new Error(ex);
496 }
497 }
498 if (defaultModule.patchLocation == null) {
499 defaultModule.classLocation = StandardLocation.CLASS_OUTPUT;
500 } else {
501 defaultModule.patchOutputLocation = StandardLocation.CLASS_OUTPUT;
502 }
503 break;
504 default:
505 Assert.error("too many modules");
506 }
507 } else if (rootModules.size() == 1) {
508 module = rootModules.iterator().next();
509 module.complete();
510 module.completer = sym -> completeModule((ModuleSymbol) sym);
511 } else {
512 Assert.check(rootModules.isEmpty());
513 Assert.checkNonNull(c);
514 module = c.packge().modle;
515 rootModules.add(module);
516 }
517
518 if (defaultModule != syms.unnamedModule) {
519 syms.unnamedModule.completer = getUnnamedModuleCompleter();
520 syms.unnamedModule.classLocation = StandardLocation.CLASS_PATH;
521 }
522
523 if (module == null) {
524 module = defaultModule;
525 }
526
527 for (JCCompilationUnit tree : trees) {
528 if (defaultModule != syms.unnamedModule
529 && defaultModule.sourceLocation == StandardLocation.SOURCE_PATH
530 && fileManager.hasLocation(StandardLocation.SOURCE_PATH)) {
531 checkSourceLocation(tree, module);
532 }
533 tree.modle = module;
534 }
535 }
536 }
537
538 private void checkSourceLocation(JCCompilationUnit tree, ModuleSymbol msym) {
539 try {
540 JavaFileObject fo = tree.sourcefile;
541 if (fileManager.contains(msym.sourceLocation, fo)) {
542 return;
543 }
544 if (msym.patchLocation != null && fileManager.contains(msym.patchLocation, fo)) {
545 return;
546 }
547 if (fileManager.hasLocation(StandardLocation.SOURCE_OUTPUT)) {
548 if (fileManager.contains(StandardLocation.SOURCE_OUTPUT, fo)) {
549 return;
550 }
551 } else {
552 if (fileManager.contains(StandardLocation.CLASS_OUTPUT, fo)) {
553 return;
554 }
555 }
556 } catch (IOException e) {
557 throw new Error(e);
558 }
559
560 JavaFileObject prev = log.useSource(tree.sourcefile);
561 try {
562 log.error(tree.pos(), Errors.FileSbOnSourceOrPatchPathForModule);
563 } finally {
564 log.useSource(prev);
565 }
566 }
567
568 private String singleModuleOverride(List<JCCompilationUnit> trees) {
569 if (!fileManager.hasLocation(StandardLocation.PATCH_MODULE_PATH)) {
570 return null;
571 }
572
573 Set<String> override = new LinkedHashSet<>();
574 for (JCCompilationUnit tree : trees) {
575 JavaFileObject fo = tree.sourcefile;
576
577 try {
578 Location loc =
579 fileManager.getLocationForModule(StandardLocation.PATCH_MODULE_PATH, fo);
580
581 if (loc != null) {
582 override.add(fileManager.inferModuleName(loc));
583 }
584 } catch (IOException ex) {
585 throw new Error(ex);
586 }
587 }
588
589 switch (override.size()) {
590 case 0: return null;
591 case 1: return override.iterator().next();
592 default:
593 log.error(Errors.TooManyPatchedModules(override));
594 return null;
595 }
596 }
597
598 /**
599 * Determine the location for the module on the module source path
600 * or source output directory which contains a given CompilationUnit.
601 * If the source output directory is unset, the class output directory
602 * will be checked instead.
603 * {@code null} is returned if no such module can be found.
604 * @param tree the compilation unit tree
605 * @return the location for the enclosing module
606 * @throws IOException if there is a problem while searching for the module.
607 */
608 private Location getModuleLocation(JCCompilationUnit tree) throws IOException {
609 JavaFileObject fo = tree.sourcefile;
610
611 Location loc =
612 fileManager.getLocationForModule(StandardLocation.MODULE_SOURCE_PATH, fo);
613 if (loc == null) {
614 Location sourceOutput = fileManager.hasLocation(StandardLocation.SOURCE_OUTPUT) ?
615 StandardLocation.SOURCE_OUTPUT : StandardLocation.CLASS_OUTPUT;
616 loc =
617 fileManager.getLocationForModule(sourceOutput, fo);
618 }
619 return loc;
620 }
621
622 private void checkNoAllModulePath() {
623 if (addModsOpt != null && Arrays.asList(addModsOpt.split(",")).contains(ALL_MODULE_PATH)) {
624 log.error(Errors.AddmodsAllModulePathInvalid);
625 }
626 }
627
628 private final Completer mainCompleter = new Completer() {
629 @Override
630 public void complete(Symbol sym) throws CompletionFailure {
631 ModuleSymbol msym = moduleFinder.findModule((ModuleSymbol) sym);
632
633 if (msym.kind == ERR) {
634 //make sure the module is initialized:
635 initErrModule(msym);
636 } else if ((msym.flags_field & Flags.AUTOMATIC_MODULE) != 0) {
637 setupAutomaticModule(msym);
638 } else {
639 try {
640 msym.module_info.complete();
641 } catch (CompletionFailure cf) {
642 msym.kind = ERR;
643 //make sure the module is initialized:
644 initErrModule(msym);
645 completeModule(msym);
646 throw cf;
647 }
648 }
649
650 // If module-info comes from a .java file, the underlying
651 // call of classFinder.fillIn will have called through the
652 // source completer, to Enter, and then to Modules.enter,
653 // which will call completeModule.
654 // But, if module-info comes from a .class file, the underlying
655 // call of classFinder.fillIn will just call ClassReader to read
656 // the .class file, and so we call completeModule here.
657 if (msym.module_info.classfile == null || msym.module_info.classfile.getKind() == Kind.CLASS) {
658 completeModule(msym);
659 }
660 }
661
662 private void initErrModule(ModuleSymbol msym) {
663 msym.directives = List.nil();
664 msym.exports = List.nil();
665 msym.provides = List.nil();
666 msym.requires = List.nil();
667 msym.uses = List.nil();
668 }
669
670 @Override
671 public String toString() {
672 return "mainCompleter";
673 }
674 };
675
676 private void setupAutomaticModule(ModuleSymbol msym) throws CompletionFailure {
677 try {
678 ListBuffer<Directive> directives = new ListBuffer<>();
679 ListBuffer<ExportsDirective> exports = new ListBuffer<>();
680 Set<String> seenPackages = new HashSet<>();
681
682 for (JavaFileObject clazz : fileManager.list(msym.classLocation, "", EnumSet.of(Kind.CLASS), true)) {
683 String binName = fileManager.inferBinaryName(msym.classLocation, clazz);
684 String pack = binName.lastIndexOf('.') != (-1) ? binName.substring(0, binName.lastIndexOf('.')) : ""; //unnamed package????
685 if (seenPackages.add(pack)) {
686 ExportsDirective d = new ExportsDirective(syms.enterPackage(msym, names.fromString(pack)), null);
687 //TODO: opens?
688 directives.add(d);
689 exports.add(d);
690 }
691 }
692
693 msym.exports = exports.toList();
694 msym.provides = List.nil();
695 msym.requires = List.nil();
696 msym.uses = List.nil();
697 msym.directives = directives.toList();
698 } catch (IOException ex) {
699 throw new IllegalStateException(ex);
700 }
701 }
702
703 private void completeAutomaticModule(ModuleSymbol msym) throws CompletionFailure {
704 ListBuffer<Directive> directives = new ListBuffer<>();
705
706 directives.addAll(msym.directives);
707
708 ListBuffer<RequiresDirective> requires = new ListBuffer<>();
709
710 for (ModuleSymbol ms : allModules()) {
711 if (ms == syms.unnamedModule || ms == msym)
712 continue;
713 Set<RequiresFlag> flags = (ms.flags_field & Flags.AUTOMATIC_MODULE) != 0 ?
714 EnumSet.of(RequiresFlag.TRANSITIVE) : EnumSet.noneOf(RequiresFlag.class);
715 RequiresDirective d = new RequiresDirective(ms, flags);
716 directives.add(d);
717 requires.add(d);
718 }
719
720 RequiresDirective requiresUnnamed = new RequiresDirective(syms.unnamedModule);
721 directives.add(requiresUnnamed);
722 requires.add(requiresUnnamed);
723
724 msym.requires = requires.toList();
725 msym.directives = directives.toList();
726 }
727
728 private Completer getSourceCompleter(JCCompilationUnit tree) {
729 return new Completer() {
730 @Override
731 public void complete(Symbol sym) throws CompletionFailure {
732 ModuleSymbol msym = (ModuleSymbol) sym;
733 msym.flags_field |= UNATTRIBUTED;
734 ModuleVisitor v = new ModuleVisitor();
735 JavaFileObject prev = log.useSource(tree.sourcefile);
736 JCModuleDecl moduleDecl = tree.getModuleDecl();
737 DiagnosticPosition prevLintPos = deferredLintHandler.setPos(moduleDecl.pos());
738
739 try {
740 moduleDecl.accept(v);
741 completeModule(msym);
742 checkCyclicDependencies(moduleDecl);
743 } finally {
744 log.useSource(prev);
745 deferredLintHandler.setPos(prevLintPos);
746 msym.flags_field &= ~UNATTRIBUTED;
747 }
748 }
749
750 @Override
751 public String toString() {
752 return "SourceCompleter: " + tree.sourcefile.getName();
753 }
754
755 };
756 }
757
758 public boolean isRootModule(ModuleSymbol module) {
759 Assert.checkNonNull(rootModules);
760 return rootModules.contains(module);
761 }
762
763 public Set<ModuleSymbol> getRootModules() {
764 Assert.checkNonNull(rootModules);
765 return rootModules;
766 }
767
768 class ModuleVisitor extends JCTree.Visitor {
769 private ModuleSymbol sym;
770 private final Set<ModuleSymbol> allRequires = new HashSet<>();
771 private final Map<PackageSymbol,List<ExportsDirective>> allExports = new HashMap<>();
772 private final Map<PackageSymbol,List<OpensDirective>> allOpens = new HashMap<>();
773
774 @Override
775 public void visitModuleDef(JCModuleDecl tree) {
776 sym = Assert.checkNonNull(tree.sym);
777
778 if (tree.getModuleType() == ModuleKind.OPEN) {
779 sym.flags.add(ModuleFlags.OPEN);
780 }
781 sym.flags_field |= (tree.mods.flags & Flags.DEPRECATED);
782
783 sym.requires = List.nil();
784 sym.exports = List.nil();
785 sym.opens = List.nil();
786 tree.directives.forEach(t -> t.accept(this));
787 sym.requires = sym.requires.reverse();
788 sym.exports = sym.exports.reverse();
789 sym.opens = sym.opens.reverse();
790 ensureJavaBase();
791 }
792
793 @Override
794 public void visitRequires(JCRequires tree) {
795 ModuleSymbol msym = lookupModule(tree.moduleName);
796 if (msym.kind != MDL) {
797 log.error(tree.moduleName.pos(), Errors.ModuleNotFound(msym));
798 warnedMissing.add(msym);
799 } else if (allRequires.contains(msym)) {
800 log.error(tree.moduleName.pos(), Errors.DuplicateRequires(msym));
801 } else {
802 allRequires.add(msym);
803 Set<RequiresFlag> flags = EnumSet.noneOf(RequiresFlag.class);
804 if (tree.isTransitive) {
805 if (msym == syms.java_base && source.compareTo(Source.JDK10) >= 0) {
806 log.error(tree.pos(), Errors.ModifierNotAllowedHere(names.transitive));
807 } else {
808 flags.add(RequiresFlag.TRANSITIVE);
809 }
810 }
811 if (tree.isStaticPhase) {
812 if (msym == syms.java_base && source.compareTo(Source.JDK10) >= 0) {
813 log.error(tree.pos(), Errors.ModNotAllowedHere(EnumSet.of(Flag.STATIC)));
814 } else {
815 flags.add(RequiresFlag.STATIC_PHASE);
816 }
817 }
818 RequiresDirective d = new RequiresDirective(msym, flags);
819 tree.directive = d;
820 sym.requires = sym.requires.prepend(d);
821 }
822 }
823
824 @Override
825 public void visitExports(JCExports tree) {
826 Name name = TreeInfo.fullName(tree.qualid);
827 PackageSymbol packge = syms.enterPackage(sym, name);
828 attr.setPackageSymbols(tree.qualid, packge);
829
830 List<ExportsDirective> exportsForPackage = allExports.computeIfAbsent(packge, p -> List.nil());
831 for (ExportsDirective d : exportsForPackage) {
832 reportExportsConflict(tree, packge);
833 }
834
835 List<ModuleSymbol> toModules = null;
836 if (tree.moduleNames != null) {
837 Set<ModuleSymbol> to = new LinkedHashSet<>();
838 for (JCExpression n: tree.moduleNames) {
839 ModuleSymbol msym = lookupModule(n);
840 chk.checkModuleExists(n.pos(), msym);
841 for (ExportsDirective d : exportsForPackage) {
842 checkDuplicateExportsToModule(n, msym, d);
843 }
844 if (!to.add(msym)) {
845 reportExportsConflictToModule(n, msym);
846 }
847 }
848 toModules = List.from(to);
849 }
850
851 if (toModules == null || !toModules.isEmpty()) {
852 Set<ExportsFlag> flags = EnumSet.noneOf(ExportsFlag.class);
853 ExportsDirective d = new ExportsDirective(packge, toModules, flags);
854 sym.exports = sym.exports.prepend(d);
855 tree.directive = d;
856
857 allExports.put(packge, exportsForPackage.prepend(d));
858 }
859 }
860
861 private void reportExportsConflict(JCExports tree, PackageSymbol packge) {
862 log.error(tree.qualid.pos(), Errors.ConflictingExports(packge));
863 }
864
865 private void checkDuplicateExportsToModule(JCExpression name, ModuleSymbol msym,
866 ExportsDirective d) {
867 if (d.modules != null) {
868 for (ModuleSymbol other : d.modules) {
869 if (msym == other) {
870 reportExportsConflictToModule(name, msym);
871 }
872 }
873 }
874 }
875
876 private void reportExportsConflictToModule(JCExpression name, ModuleSymbol msym) {
877 log.error(name.pos(), Errors.ConflictingExportsToModule(msym));
878 }
879
880 @Override
881 public void visitOpens(JCOpens tree) {
882 Name name = TreeInfo.fullName(tree.qualid);
883 PackageSymbol packge = syms.enterPackage(sym, name);
884 attr.setPackageSymbols(tree.qualid, packge);
885
886 if (sym.flags.contains(ModuleFlags.OPEN)) {
887 log.error(tree.pos(), Errors.NoOpensUnlessStrong);
888 }
889 List<OpensDirective> opensForPackage = allOpens.computeIfAbsent(packge, p -> List.nil());
890 for (OpensDirective d : opensForPackage) {
891 reportOpensConflict(tree, packge);
892 }
893
894 List<ModuleSymbol> toModules = null;
895 if (tree.moduleNames != null) {
896 Set<ModuleSymbol> to = new LinkedHashSet<>();
897 for (JCExpression n: tree.moduleNames) {
898 ModuleSymbol msym = lookupModule(n);
899 chk.checkModuleExists(n.pos(), msym);
900 for (OpensDirective d : opensForPackage) {
901 checkDuplicateOpensToModule(n, msym, d);
902 }
903 if (!to.add(msym)) {
904 reportOpensConflictToModule(n, msym);
905 }
906 }
907 toModules = List.from(to);
908 }
909
910 if (toModules == null || !toModules.isEmpty()) {
911 Set<OpensFlag> flags = EnumSet.noneOf(OpensFlag.class);
912 OpensDirective d = new OpensDirective(packge, toModules, flags);
913 sym.opens = sym.opens.prepend(d);
914 tree.directive = d;
915
916 allOpens.put(packge, opensForPackage.prepend(d));
917 }
918 }
919
920 private void reportOpensConflict(JCOpens tree, PackageSymbol packge) {
921 log.error(tree.qualid.pos(), Errors.ConflictingOpens(packge));
922 }
923
924 private void checkDuplicateOpensToModule(JCExpression name, ModuleSymbol msym,
925 OpensDirective d) {
926 if (d.modules != null) {
927 for (ModuleSymbol other : d.modules) {
928 if (msym == other) {
929 reportOpensConflictToModule(name, msym);
930 }
931 }
932 }
933 }
934
935 private void reportOpensConflictToModule(JCExpression name, ModuleSymbol msym) {
936 log.error(name.pos(), Errors.ConflictingOpensToModule(msym));
937 }
938
939 @Override
940 public void visitProvides(JCProvides tree) { }
941
942 @Override
943 public void visitUses(JCUses tree) { }
944
945 private void ensureJavaBase() {
946 if (sym.name == names.java_base)
947 return;
948
949 for (RequiresDirective d: sym.requires) {
950 if (d.module.name == names.java_base)
951 return;
952 }
953
954 ModuleSymbol java_base = syms.enterModule(names.java_base);
955 Directive.RequiresDirective d =
956 new Directive.RequiresDirective(java_base,
957 EnumSet.of(Directive.RequiresFlag.MANDATED));
958 sym.requires = sym.requires.prepend(d);
959 }
960
961 private ModuleSymbol lookupModule(JCExpression moduleName) {
962 Name name = TreeInfo.fullName(moduleName);
963 ModuleSymbol msym = moduleFinder.findModule(name);
964 TreeInfo.setSymbol(moduleName, msym);
965 return msym;
966 }
967 }
968
969 public Completer getUsesProvidesCompleter() {
970 return sym -> {
971 ModuleSymbol msym = (ModuleSymbol) sym;
972
973 msym.complete();
974
975 Env<AttrContext> env = typeEnvs.get(msym);
976 UsesProvidesVisitor v = new UsesProvidesVisitor(msym, env);
977 JavaFileObject prev = log.useSource(env.toplevel.sourcefile);
978 JCModuleDecl decl = env.toplevel.getModuleDecl();
979 DiagnosticPosition prevLintPos = deferredLintHandler.setPos(decl.pos());
980
981 try {
982 decl.accept(v);
983 } finally {
984 log.useSource(prev);
985 deferredLintHandler.setPos(prevLintPos);
986 }
987 };
988 }
989
990 class UsesProvidesVisitor extends JCTree.Visitor {
991 private final ModuleSymbol msym;
992 private final Env<AttrContext> env;
993
994 private final Set<ClassSymbol> allUses = new HashSet<>();
995 private final Map<ClassSymbol, Set<ClassSymbol>> allProvides = new HashMap<>();
996
997 public UsesProvidesVisitor(ModuleSymbol msym, Env<AttrContext> env) {
998 this.msym = msym;
999 this.env = env;
1000 }
1001
1002 @Override @SuppressWarnings("unchecked")
1003 public void visitModuleDef(JCModuleDecl tree) {
1004 msym.directives = List.nil();
1005 msym.provides = List.nil();
1006 msym.uses = List.nil();
1007 tree.directives.forEach(t -> t.accept(this));
1008 msym.directives = msym.directives.reverse();
1009 msym.provides = msym.provides.reverse();
1010 msym.uses = msym.uses.reverse();
1011
1012 if (msym.requires.nonEmpty() && msym.requires.head.flags.contains(RequiresFlag.MANDATED))
1013 msym.directives = msym.directives.prepend(msym.requires.head);
1014
1015 msym.directives = msym.directives.appendList(List.from(addReads.getOrDefault(msym, Collections.emptySet())));
1016
1017 checkForCorrectness();
1018 }
1019
1020 @Override
1021 public void visitExports(JCExports tree) {
1022 Iterable<Symbol> packageContent = tree.directive.packge.members().getSymbols();
1023 List<JavaFileObject> filesToCheck = List.nil();
1024 boolean packageNotEmpty = false;
1025 for (Symbol sym : packageContent) {
1026 if (sym.kind != Kinds.Kind.TYP)
1027 continue;
1028 ClassSymbol csym = (ClassSymbol) sym;
1029 if (sym.completer.isTerminal() ||
1030 csym.classfile.getKind() == Kind.CLASS) {
1031 packageNotEmpty = true;
1032 filesToCheck = List.nil();
1033 break;
1034 }
1035 if (csym.classfile.getKind() == Kind.SOURCE) {
1036 filesToCheck = filesToCheck.prepend(csym.classfile);
1037 }
1038 }
1039 for (JavaFileObject jfo : filesToCheck) {
1040 if (findPackageInFile.findPackageNameOf(jfo) == tree.directive.packge.fullname) {
1041 packageNotEmpty = true;
1042 break;
1043 }
1044 }
1045 if (!packageNotEmpty) {
1046 log.error(tree.qualid.pos(), Errors.PackageEmptyOrNotFound(tree.directive.packge));
1047 }
1048 msym.directives = msym.directives.prepend(tree.directive);
1049 }
1050
1051 @Override
1052 public void visitOpens(JCOpens tree) {
1053 chk.checkPackageExistsForOpens(tree.qualid, tree.directive.packge);
1054 msym.directives = msym.directives.prepend(tree.directive);
1055 }
1056
1057 MethodSymbol noArgsConstructor(ClassSymbol tsym) {
1058 for (Symbol sym : tsym.members().getSymbolsByName(names.init)) {
1059 MethodSymbol mSym = (MethodSymbol)sym;
1060 if (mSym.params().isEmpty()) {
1061 return mSym;
1062 }
1063 }
1064 return null;
1065 }
1066
1067 MethodSymbol factoryMethod(ClassSymbol tsym) {
1068 for (Symbol sym : tsym.members().getSymbolsByName(names.provider, sym -> sym.kind == MTH)) {
1069 MethodSymbol mSym = (MethodSymbol)sym;
1070 if (mSym.isStatic() && (mSym.flags() & Flags.PUBLIC) != 0 && mSym.params().isEmpty()) {
1071 return mSym;
1072 }
1073 }
1074 return null;
1075 }
1076
1077 Map<Directive.ProvidesDirective, JCProvides> directiveToTreeMap = new HashMap<>();
1078
1079 @Override
1080 public void visitProvides(JCProvides tree) {
1081 Type st = attr.attribType(tree.serviceName, env, syms.objectType);
1082 ClassSymbol service = (ClassSymbol) st.tsym;
1083 if (allProvides.containsKey(service)) {
1084 log.error(tree.serviceName.pos(), Errors.RepeatedProvidesForService(service));
1085 }
1086 ListBuffer<ClassSymbol> impls = new ListBuffer<>();
1087 for (JCExpression implName : tree.implNames) {
1088 Type it;
1089 boolean prevVisitingServiceImplementation = env.info.visitingServiceImplementation;
1090 try {
1091 env.info.visitingServiceImplementation = true;
1092 it = attr.attribType(implName, env, syms.objectType);
1093 } finally {
1094 env.info.visitingServiceImplementation = prevVisitingServiceImplementation;
1095 }
1096 ClassSymbol impl = (ClassSymbol) it.tsym;
1097 if ((impl.flags_field & PUBLIC) == 0) {
1098 log.error(implName.pos(), Errors.NotDefPublic(impl, impl.location()));
1099 }
1100 //find provider factory:
1101 MethodSymbol factory = factoryMethod(impl);
1102 if (factory != null) {
1103 Type returnType = factory.type.getReturnType();
1104 if (!types.isSubtype(returnType, st)) {
1105 log.error(implName.pos(), Errors.ServiceImplementationProviderReturnMustBeSubtypeOfServiceInterface);
1106 }
1107 } else {
1108 if (!types.isSubtype(it, st)) {
1109 log.error(implName.pos(), Errors.ServiceImplementationMustBeSubtypeOfServiceInterface);
1110 } else if ((impl.flags() & ABSTRACT) != 0) {
1111 log.error(implName.pos(), Errors.ServiceImplementationIsAbstract(impl));
1112 } else if (impl.isInner()) {
1113 log.error(implName.pos(), Errors.ServiceImplementationIsInner(impl));
1114 } else {
1115 MethodSymbol constr = noArgsConstructor(impl);
1116 if (constr == null) {
1117 log.error(implName.pos(), Errors.ServiceImplementationDoesntHaveANoArgsConstructor(impl));
1118 } else if ((constr.flags() & PUBLIC) == 0) {
1119 log.error(implName.pos(), Errors.ServiceImplementationNoArgsConstructorNotPublic(impl));
1120 }
1121 }
1122 }
1123 if (it.hasTag(CLASS)) {
1124 if (allProvides.computeIfAbsent(service, s -> new HashSet<>()).add(impl)) {
1125 impls.append(impl);
1126 } else {
1127 log.error(implName.pos(), Errors.DuplicateProvides(service, impl));
1128 }
1129 }
1130 }
1131 if (st.hasTag(CLASS) && !impls.isEmpty()) {
1132 Directive.ProvidesDirective d = new Directive.ProvidesDirective(service, impls.toList());
1133 msym.provides = msym.provides.prepend(d);
1134 msym.directives = msym.directives.prepend(d);
1135 directiveToTreeMap.put(d, tree);
1136 }
1137 }
1138
1139 @Override
1140 public void visitRequires(JCRequires tree) {
1141 if (tree.directive != null && allModules().contains(tree.directive.module)) {
1142 chk.checkDeprecated(tree.moduleName.pos(), msym, tree.directive.module);
1143 chk.checkPreview(tree.moduleName.pos(), msym, tree.directive.module);
1144 chk.checkModuleRequires(tree.moduleName.pos(), tree.directive);
1145 msym.directives = msym.directives.prepend(tree.directive);
1146 }
1147 }
1148
1149 @Override
1150 public void visitUses(JCUses tree) {
1151 Type st = attr.attribType(tree.qualid, env, syms.objectType);
1152 Symbol sym = TreeInfo.symbol(tree.qualid);
1153 if ((sym.flags() & ENUM) != 0) {
1154 log.error(tree.qualid.pos(), Errors.ServiceDefinitionIsEnum(st.tsym));
1155 } else if (st.hasTag(CLASS)) {
1156 ClassSymbol service = (ClassSymbol) st.tsym;
1157 if (allUses.add(service)) {
1158 Directive.UsesDirective d = new Directive.UsesDirective(service);
1159 msym.uses = msym.uses.prepend(d);
1160 msym.directives = msym.directives.prepend(d);
1161 } else {
1162 log.error(tree.pos(), Errors.DuplicateUses(service));
1163 }
1164 }
1165 }
1166
1167 private void checkForCorrectness() {
1168 for (Directive.ProvidesDirective provides : msym.provides) {
1169 JCProvides tree = directiveToTreeMap.get(provides);
1170 for (ClassSymbol impl : provides.impls) {
1171 /* The implementation must be defined in the same module as the provides directive
1172 * (else, error)
1173 */
1174 PackageSymbol implementationDefiningPackage = impl.packge();
1175 if (implementationDefiningPackage.modle != msym) {
1176 // TODO: should use tree for the implementation name, not the entire provides tree
1177 // TODO: should improve error message to identify the implementation type
1178 log.error(tree.pos(), Errors.ServiceImplementationNotInRightModule(implementationDefiningPackage.modle));
1179 }
1180
1181 /* There is no inherent requirement that module that provides a service should actually
1182 * use it itself. However, it is a pointless declaration if the service package is not
1183 * exported and there is no uses for the service.
1184 */
1185 PackageSymbol interfaceDeclaringPackage = provides.service.packge();
1186 boolean isInterfaceDeclaredInCurrentModule = interfaceDeclaringPackage.modle == msym;
1187 boolean isInterfaceExportedFromAReadableModule =
1188 msym.visiblePackages.get(interfaceDeclaringPackage.fullname) == interfaceDeclaringPackage;
1189 if (isInterfaceDeclaredInCurrentModule && !isInterfaceExportedFromAReadableModule) {
1190 // ok the interface is declared in this module. Let's check if it's exported
1191 boolean warn = true;
1192 for (ExportsDirective export : msym.exports) {
1193 if (interfaceDeclaringPackage == export.packge) {
1194 warn = false;
1195 break;
1196 }
1197 }
1198 if (warn) {
1199 for (UsesDirective uses : msym.uses) {
1200 if (provides.service == uses.service) {
1201 warn = false;
1202 break;
1203 }
1204 }
1205 }
1206 if (warn) {
1207 log.warning(tree.pos(), Warnings.ServiceProvidedButNotExportedOrUsed(provides.service));
1208 }
1209 }
1210 }
1211 }
1212 }
1213 }
1214
1215 private Set<ModuleSymbol> allModules;
1216
1217 public Set<ModuleSymbol> allModules() {
1218 Assert.checkNonNull(allModules);
1219 return allModules;
1220 }
1221
1222 private void setupAllModules() {
1223 Assert.checkNonNull(rootModules);
1224 Assert.checkNull(allModules);
1225
1226 //java.base may not be completed yet and computeTransitiveClosure
1227 //may not complete it either, make sure it is completed:
1228 syms.java_base.complete();
1229
1230 Set<ModuleSymbol> observable;
1231
1232 if (limitModsOpt == null && extraLimitMods.isEmpty()) {
1233 observable = null;
1234 } else {
1235 Set<ModuleSymbol> limitMods = new HashSet<>();
1236 if (limitModsOpt != null) {
1237 for (String limit : limitModsOpt.split(",")) {
1238 if (!isValidName(limit))
1239 continue;
1240 limitMods.add(syms.enterModule(names.fromString(limit)));
1241 }
1242 }
1243 for (String limit : extraLimitMods) {
1244 limitMods.add(syms.enterModule(names.fromString(limit)));
1245 }
1246 observable = computeTransitiveClosure(limitMods, rootModules, null);
1247 observable.addAll(rootModules);
1248 if (lintOptions) {
1249 for (ModuleSymbol msym : limitMods) {
1250 if (!observable.contains(msym)) {
1251 log.warning(LintCategory.OPTIONS,
1252 Warnings.ModuleForOptionNotFound(Option.LIMIT_MODULES, msym));
1253 }
1254 }
1255 }
1256 }
1257
1258 Predicate<ModuleSymbol> observablePred = sym ->
1259 (observable == null) ? (moduleFinder.findModule(sym).kind != ERR) : observable.contains(sym);
1260 Predicate<ModuleSymbol> systemModulePred = sym -> (sym.flags() & Flags.SYSTEM_MODULE) != 0;
1261 Set<ModuleSymbol> enabledRoot = new LinkedHashSet<>();
1262
1263 if (rootModules.contains(syms.unnamedModule)) {
1264 Predicate<ModuleSymbol> jdkModulePred;
1265 if (target.allApiModulesAreRoots()) {
1266 jdkModulePred = sym -> {
1267 sym.complete();
1268 return sym.exports.stream().anyMatch(e -> e.modules == null);
1269 };
1270 } else {
1271 ModuleSymbol javaSE = syms.getModule(java_se);
1272 if (javaSE != null && (observable == null || observable.contains(javaSE))) {
1273 jdkModulePred = sym -> {
1274 sym.complete();
1275 return !sym.name.startsWith(java_)
1276 && sym.exports.stream().anyMatch(e -> e.modules == null);
1277 };
1278 enabledRoot.add(javaSE);
1279 } else {
1280 jdkModulePred = sym -> true;
1281 }
1282 }
1283
1284 Predicate<ModuleSymbol> noIncubatorPred = sym -> {
1285 sym.complete();
1286 return !sym.resolutionFlags.contains(ModuleResolutionFlags.DO_NOT_RESOLVE_BY_DEFAULT);
1287 };
1288
1289 for (ModuleSymbol sym : new HashSet<>(syms.getAllModules())) {
1290 try {
1291 if (systemModulePred.test(sym) && observablePred.test(sym) && jdkModulePred.test(sym) && noIncubatorPred.test(sym)) {
1292 enabledRoot.add(sym);
1293 }
1294 } catch (CompletionFailure ex) {
1295 chk.completionError(null, ex);
1296 }
1297 }
1298 }
1299
1300 enabledRoot.addAll(rootModules);
1301
1302 if (addModsOpt != null || !extraAddMods.isEmpty()) {
1303 Set<String> fullAddMods = new HashSet<>();
1304 fullAddMods.addAll(extraAddMods);
1305
1306 if (addModsOpt != null) {
1307 fullAddMods.addAll(Arrays.asList(addModsOpt.split(",")));
1308 }
1309
1310 for (String added : fullAddMods) {
1311 Stream<ModuleSymbol> modules;
1312 switch (added) {
1313 case ALL_SYSTEM:
1314 modules = new HashSet<>(syms.getAllModules())
1315 .stream()
1316 .filter(systemModulePred.and(observablePred));
1317 break;
1318 case ALL_MODULE_PATH:
1319 modules = new HashSet<>(syms.getAllModules())
1320 .stream()
1321 .filter(systemModulePred.negate().and(observablePred));
1322 break;
1323 default:
1324 if (!isValidName(added))
1325 continue;
1326 modules = Stream.of(syms.enterModule(names.fromString(added)));
1327 break;
1328 }
1329 modules.forEach(sym -> {
1330 enabledRoot.add(sym);
1331 if (observable != null)
1332 observable.add(sym);
1333 });
1334 }
1335 }
1336
1337 Set<ModuleSymbol> result = computeTransitiveClosure(enabledRoot, rootModules, observable);
1338
1339 result.add(syms.unnamedModule);
1340
1341 boolean hasAutomatic = result.stream().anyMatch(IS_AUTOMATIC);
1342
1343 if (hasAutomatic) {
1344 syms.getAllModules()
1345 .stream()
1346 .filter(IS_AUTOMATIC)
1347 .forEach(result::add);
1348 }
1349
1350 String incubatingModules = filterAlreadyWarnedIncubatorModules(result.stream()
1351 .filter(msym -> msym.resolutionFlags.contains(ModuleResolutionFlags.WARN_INCUBATING))
1352 .map(msym -> msym.name.toString()))
1353 .collect(Collectors.joining(","));
1354
1355 if (!incubatingModules.isEmpty()) {
1356 log.warning(Warnings.IncubatingModules(incubatingModules));
1357 }
1358
1359 allModules = result;
1360
1361 //add module versions from options, if any:
1362 if (moduleVersionOpt != null) {
1363 Name version = names.fromString(moduleVersionOpt);
1364 rootModules.forEach(m -> m.version = version);
1365 }
1366 }
1367 //where:
1368 private Stream<String> filterAlreadyWarnedIncubatorModules(Stream<String> incubatingModules) {
1369 if (!sourceLauncher) return incubatingModules;
1370 Set<String> bootModules = ModuleLayer.boot()
1371 .modules()
1372 .stream()
1373 .map(Module::getName)
1374 .collect(Collectors.toSet());
1375 return incubatingModules.filter(module -> !bootModules.contains(module));
1376 }
1377 private static final Predicate<ModuleSymbol> IS_AUTOMATIC =
1378 m -> (m.flags_field & Flags.AUTOMATIC_MODULE) != 0;
1379
1380 public boolean isInModuleGraph(ModuleSymbol msym) {
1381 return allModules == null || allModules.contains(msym);
1382 }
1383
1384 private Set<ModuleSymbol> computeTransitiveClosure(Set<? extends ModuleSymbol> base,
1385 Set<? extends ModuleSymbol> rootModules,
1386 Set<ModuleSymbol> observable) {
1387 List<ModuleSymbol> primaryTodo = List.nil();
1388 List<ModuleSymbol> secondaryTodo = List.nil();
1389
1390 for (ModuleSymbol ms : base) {
1391 if (rootModules.contains(ms)) {
1392 primaryTodo = primaryTodo.prepend(ms);
1393 } else {
1394 secondaryTodo = secondaryTodo.prepend(ms);
1395 }
1396 }
1397
1398 Set<ModuleSymbol> result = new LinkedHashSet<>();
1399 result.add(syms.java_base);
1400
1401 while (primaryTodo.nonEmpty() || secondaryTodo.nonEmpty()) {
1402 try {
1403 ModuleSymbol current;
1404 boolean isPrimaryTodo;
1405 if (primaryTodo.nonEmpty()) {
1406 current = primaryTodo.head;
1407 primaryTodo = primaryTodo.tail;
1408 isPrimaryTodo = true;
1409 } else {
1410 current = secondaryTodo.head;
1411 secondaryTodo = secondaryTodo.tail;
1412 isPrimaryTodo = false;
1413 }
1414 if (observable != null && !observable.contains(current))
1415 continue;
1416 if (!result.add(current) || current == syms.unnamedModule || ((current.flags_field & Flags.AUTOMATIC_MODULE) != 0))
1417 continue;
1418 current.complete();
1419 if (current.kind == ERR && (isPrimaryTodo || base.contains(current)) && warnedMissing.add(current)) {
1420 log.error(Errors.ModuleNotFound(current));
1421 }
1422 for (RequiresDirective rd : current.requires) {
1423 if (rd.module == syms.java_base) continue;
1424 if ((rd.isTransitive() && isPrimaryTodo) || rootModules.contains(current)) {
1425 primaryTodo = primaryTodo.prepend(rd.module);
1426 } else {
1427 secondaryTodo = secondaryTodo.prepend(rd.module);
1428 }
1429 }
1430 } catch (CompletionFailure ex) {
1431 chk.completionError(null, ex);
1432 }
1433 }
1434
1435 return result;
1436 }
1437
1438 public ModuleSymbol getObservableModule(Name name) {
1439 ModuleSymbol mod = syms.getModule(name);
1440
1441 if (allModules().contains(mod)) {
1442 return mod;
1443 }
1444
1445 return null;
1446 }
1447
1448 private Completer getUnnamedModuleCompleter() {
1449 moduleFinder.findAllModules();
1450 return new Symbol.Completer() {
1451 @Override
1452 public void complete(Symbol sym) throws CompletionFailure {
1453 if (inInitModules) {
1454 sym.completer = this;
1455 return ;
1456 }
1457 ModuleSymbol msym = (ModuleSymbol) sym;
1458 Set<ModuleSymbol> allModules = new HashSet<>(allModules());
1459 allModules.remove(syms.unnamedModule);
1460 for (ModuleSymbol m : allModules) {
1461 m.complete();
1462 }
1463 initVisiblePackages(msym, allModules);
1464 }
1465
1466 @Override
1467 public String toString() {
1468 return "unnamedModule Completer";
1469 }
1470 };
1471 }
1472
1473 private final Map<ModuleSymbol, Set<ModuleSymbol>> requiresTransitiveCache = new HashMap<>();
1474
1475 private void completeModule(ModuleSymbol msym) {
1476 if (inInitModules) {
1477 msym.completer = sym -> completeModule(msym);
1478 return ;
1479 }
1480
1481 if ((msym.flags_field & Flags.AUTOMATIC_MODULE) != 0) {
1482 completeAutomaticModule(msym);
1483 }
1484
1485 Assert.checkNonNull(msym.requires);
1486
1487 initAddReads();
1488
1489 msym.requires = msym.requires.appendList(List.from(addReads.getOrDefault(msym, Collections.emptySet())));
1490
1491 List<RequiresDirective> requires = msym.requires;
1492
1493 while (requires.nonEmpty()) {
1494 if (!allModules().contains(requires.head.module)) {
1495 Env<AttrContext> env = typeEnvs.get(msym);
1496 if (env != null) {
1497 JavaFileObject origSource = log.useSource(env.toplevel.sourcefile);
1498 try {
1499 log.error(/*XXX*/env.tree, Errors.ModuleNotFound(requires.head.module));
1500 } finally {
1501 log.useSource(origSource);
1502 }
1503 } else {
1504 Assert.check((msym.flags() & Flags.AUTOMATIC_MODULE) == 0);
1505 }
1506 msym.requires = List.filter(msym.requires, requires.head);
1507 }
1508 requires = requires.tail;
1509 }
1510
1511 Set<ModuleSymbol> readable = new LinkedHashSet<>();
1512 Set<ModuleSymbol> requiresTransitive = new HashSet<>();
1513
1514 for (RequiresDirective d : msym.requires) {
1515 d.module.complete();
1516 readable.add(d.module);
1517 Set<ModuleSymbol> s = retrieveRequiresTransitive(d.module);
1518 Assert.checkNonNull(s, () -> "no entry in cache for " + d.module);
1519 readable.addAll(s);
1520 if (d.flags.contains(RequiresFlag.TRANSITIVE)) {
1521 requiresTransitive.add(d.module);
1522 requiresTransitive.addAll(s);
1523 }
1524 }
1525
1526 requiresTransitiveCache.put(msym, requiresTransitive);
1527 initVisiblePackages(msym, readable);
1528 for (ExportsDirective d: msym.exports) {
1529 if (d.packge != null) {
1530 d.packge.modle = msym;
1531 }
1532 }
1533 }
1534
1535 private Set<ModuleSymbol> retrieveRequiresTransitive(ModuleSymbol msym) {
1536 Set<ModuleSymbol> requiresTransitive = requiresTransitiveCache.get(msym);
1537
1538 if (requiresTransitive == null) {
1539 //the module graph may contain cycles involving automatic modules or --add-reads edges
1540 requiresTransitive = new HashSet<>();
1541
1542 Set<ModuleSymbol> seen = new HashSet<>();
1543 List<ModuleSymbol> todo = List.of(msym);
1544
1545 while (todo.nonEmpty()) {
1546 ModuleSymbol current = todo.head;
1547 todo = todo.tail;
1548 if (!seen.add(current))
1549 continue;
1550 requiresTransitive.add(current);
1551 current.complete();
1552 Iterable<? extends RequiresDirective> requires;
1553 if (current != syms.unnamedModule) {
1554 Assert.checkNonNull(current.requires, () -> current + ".requires == null; " + msym);
1555 requires = current.requires;
1556 for (RequiresDirective rd : requires) {
1557 if (rd.isTransitive())
1558 todo = todo.prepend(rd.module);
1559 }
1560 } else {
1561 for (ModuleSymbol mod : allModules()) {
1562 todo = todo.prepend(mod);
1563 }
1564 }
1565 }
1566
1567 requiresTransitive.remove(msym);
1568 }
1569
1570 return requiresTransitive;
1571 }
1572
1573 private void initVisiblePackages(ModuleSymbol msym, Collection<ModuleSymbol> readable) {
1574 initAddExports();
1575
1576 msym.visiblePackages = new LinkedHashMap<>();
1577 msym.readModules = new HashSet<>(readable);
1578
1579 Map<Name, ModuleSymbol> seen = new HashMap<>();
1580
1581 for (ModuleSymbol rm : readable) {
1582 if (rm == syms.unnamedModule)
1583 continue;
1584 addVisiblePackages(msym, seen, rm, rm.exports);
1585 }
1586
1587 addExports.forEach((exportsFrom, exports) -> {
1588 if (msym.readModules.contains(exportsFrom)) {
1589 addVisiblePackages(msym, seen, exportsFrom, exports);
1590 }
1591 });
1592 }
1593
1594 private void addVisiblePackages(ModuleSymbol msym,
1595 Map<Name, ModuleSymbol> seenPackages,
1596 ModuleSymbol exportsFrom,
1597 Collection<ExportsDirective> exports) {
1598 for (ExportsDirective d : exports) {
1599 if (d.modules == null || d.modules.contains(msym)) {
1600 Name packageName = d.packge.fullname;
1601 ModuleSymbol previousModule = seenPackages.get(packageName);
1602
1603 if (previousModule != null && previousModule != exportsFrom) {
1604 Env<AttrContext> env = typeEnvs.get(msym);
1605 JavaFileObject origSource = env != null ? log.useSource(env.toplevel.sourcefile)
1606 : null;
1607 DiagnosticPosition pos = env != null ? env.tree.pos() : null;
1608 try {
1609 if (msym.isUnnamed()) {
1610 log.error(pos, Errors.PackageClashFromRequiresInUnnamed(packageName,
1611 previousModule, exportsFrom));
1612 } else {
1613 log.error(pos, Errors.PackageClashFromRequires(msym, packageName,
1614 previousModule, exportsFrom));
1615 }
1616 } finally {
1617 if (env != null)
1618 log.useSource(origSource);
1619 }
1620 continue;
1621 }
1622
1623 seenPackages.put(packageName, exportsFrom);
1624 msym.visiblePackages.put(d.packge.fullname, d.packge);
1625 }
1626 }
1627 }
1628
1629 private void initAddExports() {
1630 if (addExports != null)
1631 return;
1632
1633 addExports = new LinkedHashMap<>();
1634 Set<ModuleSymbol> unknownModules = new HashSet<>();
1635
1636 if (addExportsOpt == null)
1637 return;
1638
1639 Pattern ep = Pattern.compile("([^/]+)/([^=]+)=(.*)");
1640 for (String s: addExportsOpt.split("\0+")) {
1641 if (s.isEmpty())
1642 continue;
1643 Matcher em = ep.matcher(s);
1644 if (!em.matches()) {
1645 continue;
1646 }
1647
1648 // Terminology comes from
1649 // --add-exports module/package=target,...
1650 // Compare to
1651 // module module { exports package to target, ... }
1652 String moduleName = em.group(1);
1653 String packageName = em.group(2);
1654 String targetNames = em.group(3);
1655
1656 if (!isValidName(moduleName))
1657 continue;
1658
1659 ModuleSymbol msym = syms.enterModule(names.fromString(moduleName));
1660 if (!isKnownModule(msym, unknownModules))
1661 continue;
1662
1663 if (!isValidName(packageName))
1664 continue;
1665
1666 if (!allowAccessIntoSystem && (msym.flags() & Flags.SYSTEM_MODULE) != 0) {
1667 log.error(Errors.AddExportsWithRelease(msym));
1668 continue;
1669 }
1670
1671 PackageSymbol p = syms.enterPackage(msym, names.fromString(packageName));
1672 p.modle = msym; // TODO: do we need this?
1673
1674 List<ModuleSymbol> targetModules = List.nil();
1675 for (String toModule : targetNames.split("[ ,]+")) {
1676 ModuleSymbol m;
1677 if (toModule.equals("ALL-UNNAMED")) {
1678 m = syms.unnamedModule;
1679 } else {
1680 if (!isValidName(toModule))
1681 continue;
1682 m = syms.enterModule(names.fromString(toModule));
1683 if (!isKnownModule(m, unknownModules))
1684 continue;
1685 }
1686 targetModules = targetModules.prepend(m);
1687 }
1688
1689 Set<ExportsDirective> extra = addExports.computeIfAbsent(msym, _x -> new LinkedHashSet<>());
1690 ExportsDirective d = new ExportsDirective(p, targetModules);
1691 extra.add(d);
1692 }
1693 }
1694
1695 private boolean isKnownModule(ModuleSymbol msym, Set<ModuleSymbol> unknownModules) {
1696 if (allModules.contains(msym)) {
1697 return true;
1698 }
1699
1700 if (!unknownModules.contains(msym)) {
1701 if (lintOptions) {
1702 log.warning(LintCategory.OPTIONS,
1703 Warnings.ModuleForOptionNotFound(Option.ADD_EXPORTS, msym));
1704 }
1705 unknownModules.add(msym);
1706 }
1707 return false;
1708 }
1709
1710 private void initAddReads() {
1711 if (addReads != null)
1712 return;
1713
1714 addReads = new LinkedHashMap<>();
1715
1716 if (addReadsOpt == null)
1717 return;
1718
1719 Pattern rp = Pattern.compile("([^=]+)=(.*)");
1720 for (String s : addReadsOpt.split("\0+")) {
1721 if (s.isEmpty())
1722 continue;
1723 Matcher rm = rp.matcher(s);
1724 if (!rm.matches()) {
1725 continue;
1726 }
1727
1728 // Terminology comes from
1729 // --add-reads source-module=target-module,...
1730 // Compare to
1731 // module source-module { requires target-module; ... }
1732 String sourceName = rm.group(1);
1733 String targetNames = rm.group(2);
1734
1735 if (!isValidName(sourceName))
1736 continue;
1737
1738 ModuleSymbol msym = syms.enterModule(names.fromString(sourceName));
1739 if (!allModules.contains(msym)) {
1740 if (lintOptions) {
1741 log.warning(Warnings.ModuleForOptionNotFound(Option.ADD_READS, msym));
1742 }
1743 continue;
1744 }
1745
1746 if (!allowAccessIntoSystem && (msym.flags() & Flags.SYSTEM_MODULE) != 0) {
1747 log.error(Errors.AddReadsWithRelease(msym));
1748 continue;
1749 }
1750
1751 for (String targetName : targetNames.split("[ ,]+", -1)) {
1752 ModuleSymbol targetModule;
1753 if (targetName.equals("ALL-UNNAMED")) {
1754 targetModule = syms.unnamedModule;
1755 } else {
1756 if (!isValidName(targetName))
1757 continue;
1758 targetModule = syms.enterModule(names.fromString(targetName));
1759 if (!allModules.contains(targetModule)) {
1760 if (lintOptions) {
1761 log.warning(LintCategory.OPTIONS, Warnings.ModuleForOptionNotFound(Option.ADD_READS, targetModule));
1762 }
1763 continue;
1764 }
1765 }
1766 addReads.computeIfAbsent(msym, m -> new HashSet<>())
1767 .add(new RequiresDirective(targetModule, EnumSet.of(RequiresFlag.EXTRA)));
1768 }
1769 }
1770 }
1771
1772 private void checkCyclicDependencies(JCModuleDecl mod) {
1773 for (JCDirective d : mod.directives) {
1774 JCRequires rd;
1775 if (!d.hasTag(Tag.REQUIRES) || (rd = (JCRequires) d).directive == null)
1776 continue;
1777 Set<ModuleSymbol> nonSyntheticDeps = new HashSet<>();
1778 List<ModuleSymbol> queue = List.of(rd.directive.module);
1779 while (queue.nonEmpty()) {
1780 ModuleSymbol current = queue.head;
1781 queue = queue.tail;
1782 if (!nonSyntheticDeps.add(current))
1783 continue;
1784 current.complete();
1785 if ((current.flags() & Flags.AUTOMATIC_MODULE) != 0)
1786 continue;
1787 Assert.checkNonNull(current.requires, current::toString);
1788 for (RequiresDirective dep : current.requires) {
1789 if (!dep.flags.contains(RequiresFlag.EXTRA))
1790 queue = queue.prepend(dep.module);
1791 }
1792 }
1793 if (nonSyntheticDeps.contains(mod.sym)) {
1794 log.error(rd.moduleName.pos(), Errors.CyclicRequires(rd.directive.module));
1795 }
1796 }
1797 }
1798
1799 private boolean isValidName(CharSequence name) {
1800 return SourceVersion.isName(name, Source.toSourceVersion(source));
1801 }
1802
1803 // DEBUG
1804 private String toString(ModuleSymbol msym) {
1805 return msym.name + "["
1806 + "kind:" + msym.kind + ";"
1807 + "locn:" + toString(msym.sourceLocation) + "," + toString(msym.classLocation) + ";"
1808 + "info:" + toString(msym.module_info.sourcefile) + ","
1809 + toString(msym.module_info.classfile) + ","
1810 + msym.module_info.completer
1811 + "]";
1812 }
1813
1814 // DEBUG
1815 String toString(Location locn) {
1816 return (locn == null) ? "--" : locn.getName();
1817 }
1818
1819 // DEBUG
1820 String toString(JavaFileObject fo) {
1821 return (fo == null) ? "--" : fo.getName();
1822 }
1823
1824 public void newRound() {
1825 allModules = null;
1826 rootModules = null;
1827 defaultModule = null;
1828 warnedMissing.clear();
1829 }
1830
1831 public interface PackageNameFinder {
1832 public Name findPackageNameOf(JavaFileObject jfo);
1833 }
1834 }
--- EOF ---