1 /*
2  * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /**
25  * @test
26  * @bug 8208184
27  * @summary tests for module resolution
28  * @library /tools/lib
29  * @modules
30  *      jdk.compiler/com.sun.tools.javac.api
31  *      jdk.compiler/com.sun.tools.javac.main
32  * @build toolbox.ToolBox toolbox.JavacTask ModuleTestBase
33  * @run main QueryBeforeEnter
34  */
35 
36 import java.io.File;
37 import java.io.IOException;
38 import java.io.OutputStream;
39 import java.net.URI;
40 import java.net.URISyntaxException;
41 import java.nio.file.*;
42 import java.util.ArrayList;
43 import java.util.HashMap;
44 import java.util.List;
45 import java.util.Map;
46 import java.util.Map.Entry;
47 import java.util.Arrays;
48 import java.util.Set;
49 
50 import javax.annotation.processing.AbstractProcessor;
51 import javax.annotation.processing.RoundEnvironment;
52 import javax.annotation.processing.SupportedAnnotationTypes;
53 import javax.lang.model.SourceVersion;
54 import javax.lang.model.element.TypeElement;
55 import javax.tools.FileObject;
56 import javax.tools.ForwardingJavaFileManager;
57 import javax.tools.JavaCompiler;
58 import javax.tools.JavaFileManager;
59 import javax.tools.JavaFileObject;
60 import javax.tools.JavaFileObject.Kind;
61 import javax.tools.SimpleJavaFileObject;
62 import javax.tools.StandardJavaFileManager;
63 import javax.tools.StandardLocation;
64 import javax.tools.ToolProvider;
65 
66 // import com.sun.source.util.JavacTask;
67 import com.sun.source.util.Plugin;
68 import com.sun.source.util.TaskEvent;
69 import com.sun.source.util.TaskListener;
70 import com.sun.tools.javac.Main;
71 
72 import toolbox.JavacTask;
73 import toolbox.Task;
74 import toolbox.ToolBox;
75 
76 public class QueryBeforeEnter extends ModuleTestBase {
main(String... args)77     public static void main(String... args) throws Exception {
78         QueryBeforeEnter t = new QueryBeforeEnter();
79         t.runTests();
80     }
81 
82     @Test
testEmpty(Path base)83     public void testEmpty(Path base) throws Exception {
84         JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
85         com.sun.source.util.JavacTask task =
86             (com.sun.source.util.JavacTask) javaCompiler.getTask(null, null, null, null, null, null);
87         TypeElement jlString = task.getElements().getTypeElement("java.lang.String");
88 
89         assertNotNull(jlString);
90     }
91 
92     @Test
testUnnamed(Path base)93     public void testUnnamed(Path base) throws Exception {
94         Path moduleSrc = base.resolve("module-src");
95         Path m1 = moduleSrc.resolve("m1x");
96 
97         tb.writeJavaFiles(m1,
98                           "module m1x { exports m1x; }",
99                           "package m1x; public class M1 {}");
100 
101         Path m2 = moduleSrc.resolve("m2x");
102 
103         tb.writeJavaFiles(m2,
104                           "module m2x { exports m2x; }",
105                           "package m2x; public class M2 {}");
106 
107         Path modulePath = base.resolve("module-path");
108 
109         Files.createDirectories(modulePath);
110 
111         new JavacTask(tb)
112                 .options("--module-source-path", moduleSrc.toString())
113                 .outdir(modulePath)
114                 .files(findJavaFiles(moduleSrc))
115                 .run()
116                 .writeAll();
117 
118         Path cpSrc = base.resolve("cp-src");
119 
120         tb.writeJavaFiles(cpSrc,
121                           "package cp; public class CP {}");
122 
123         Path cp = base.resolve("cp");
124 
125         Files.createDirectories(cp);
126 
127         new JavacTask(tb)
128                 .outdir(cp)
129                 .files(findJavaFiles(cpSrc))
130                 .run()
131                 .writeAll();
132 
133         Path src = base.resolve("src");
134 
135         tb.writeJavaFiles(src,
136                           "package test; public class Test1 {}",
137                           "package test; public class Test2 {}");
138 
139         Path out = base.resolve("out");
140 
141         Files.createDirectories(out);
142 
143         JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
144         try (StandardJavaFileManager fm = javaCompiler.getStandardFileManager(null, null, null)) {
145             com.sun.source.util.JavacTask task =
146                 (com.sun.source.util.JavacTask) javaCompiler.getTask(null,
147                                                               null,
148                                                               d -> { throw new IllegalStateException(d.toString()); },
149                                                               Arrays.asList("--module-path", modulePath.toString(),
150                                                                             "--class-path", cp.toString(),
151                                                                             "-sourcepath", src.toString()),
152                                                               null,
153                                                               fm.getJavaFileObjects(src.resolve("test").resolve("Test2.java")));
154             assertNotNull(task.getElements().getTypeElement("java.lang.String"));
155             assertNotNull(task.getElements().getTypeElement("javax.tools.ToolProvider"));
156             assertNull(task.getElements().getTypeElement("m1x.M1"));
157             assertNull(task.getElements().getTypeElement("m2x.M2"));
158             assertNotNull(task.getElements().getTypeElement("cp.CP"));
159             assertNotNull(task.getElements().getTypeElement("test.Test1"));
160             assertNotNull(task.getElements().getTypeElement("test.Test2"));
161             assertNotNull(task.getElements().getModuleElement("java.base"));
162             assertNotNull(task.getElements().getModuleElement("java.compiler"));
163             assertNull(task.getElements().getModuleElement("m1x"));
164             assertNull(task.getElements().getModuleElement("m2x"));
165         }
166     }
167 
168     @Test
testSingleNamed(Path base)169     public void testSingleNamed(Path base) throws Exception {
170         Path moduleSrc = base.resolve("module-src");
171         Path m1 = moduleSrc.resolve("m1x");
172 
173         tb.writeJavaFiles(m1,
174                           "module m1x { exports m1x; }",
175                           "package m1x; public class M1 {}");
176 
177         Path m2 = moduleSrc.resolve("m2x");
178 
179         tb.writeJavaFiles(m2,
180                           "module m2x { exports m2x; }",
181                           "package m2x; public class M2 {}");
182 
183         Path modulePath = base.resolve("module-path");
184 
185         Files.createDirectories(modulePath);
186 
187         new JavacTask(tb)
188                 .options("--module-source-path", moduleSrc.toString())
189                 .outdir(modulePath)
190                 .files(findJavaFiles(moduleSrc))
191                 .run()
192                 .writeAll();
193 
194         Path cpSrc = base.resolve("cp-src");
195 
196         tb.writeJavaFiles(cpSrc,
197                           "package cp; public class CP {}");
198 
199         Path cp = base.resolve("cp");
200 
201         Files.createDirectories(cp);
202 
203         new JavacTask(tb)
204                 .outdir(cp)
205                 .files(findJavaFiles(cpSrc))
206                 .run()
207                 .writeAll();
208 
209         Path src = base.resolve("src");
210 
211         tb.writeJavaFiles(src,
212                           "module test { requires java.base; requires m1x; } ",
213                           "package test; public class Test {}");
214 
215         Path out = base.resolve("out");
216 
217         Files.createDirectories(out);
218 
219         JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
220         try (StandardJavaFileManager fm = javaCompiler.getStandardFileManager(null, null, null)) {
221             com.sun.source.util.JavacTask task =
222                 (com.sun.source.util.JavacTask) javaCompiler.getTask(null,
223                                                               null,
224                                                               d -> { throw new IllegalStateException(d.toString()); },
225                                                               Arrays.asList("--module-path", modulePath.toString(),
226                                                                             "--class-path", cp.toString(),
227                                                                             "-sourcepath", src.toString()),
228                                                               null,
229                                                               fm.getJavaFileObjects(findJavaFiles(src)));
230             assertNotNull(task.getElements().getTypeElement("java.lang.String"));
231             assertNull(task.getElements().getTypeElement("javax.tools.ToolProvider"));
232             assertNotNull(task.getElements().getTypeElement("m1x.M1"));
233             assertNull(task.getElements().getTypeElement("m2x.M2"));
234             assertNotNull(task.getElements().getTypeElement("test.Test"));
235             assertNotNull(task.getElements().getModuleElement("java.base"));
236             assertNull(task.getElements().getModuleElement("java.compiler"));
237             assertNotNull(task.getElements().getModuleElement("m1x"));
238             assertNull(task.getElements().getModuleElement("m2x"));
239             assertNotNull(task.getElements().getModuleElement("test"));
240         }
241     }
242 
243     @Test
testMultiModule(Path base)244     public void testMultiModule(Path base) throws Exception {
245         Path modulePathSrc = base.resolve("module-path-src");
246         Path m1 = modulePathSrc.resolve("m1x");
247 
248         tb.writeJavaFiles(m1,
249                           "module m1x { exports m1x; }",
250                           "package m1x; public class M1 {}");
251 
252         Path m2 = modulePathSrc.resolve("m2x");
253 
254         tb.writeJavaFiles(m2,
255                           "module m2x { exports m2x; }",
256                           "package m2x; public class M2 {}");
257 
258         Path modulePath = base.resolve("module-path");
259 
260         Files.createDirectories(modulePath);
261 
262         new JavacTask(tb)
263                 .options("--module-source-path", modulePathSrc.toString())
264                 .outdir(modulePath)
265                 .files(findJavaFiles(modulePathSrc))
266                 .run()
267                 .writeAll();
268 
269         Path cpSrc = base.resolve("cp-src");
270 
271         tb.writeJavaFiles(cpSrc,
272                           "package cp; public class CP {}");
273 
274         Path cp = base.resolve("cp");
275 
276         Files.createDirectories(cp);
277 
278         new JavacTask(tb)
279                 .outdir(cp)
280                 .files(findJavaFiles(cpSrc))
281                 .run()
282                 .writeAll();
283 
284         Path moduleSrc = base.resolve("module-src");
285         Path m3 = moduleSrc.resolve("m3x");
286 
287         tb.writeJavaFiles(m3,
288                           "module m3x { requires m1x; exports m3x; }",
289                           "package m3x; public class M3 {  }");
290 
291         Path m4 = moduleSrc.resolve("m4x");
292 
293         tb.writeJavaFiles(m4,
294                           "module m4x { exports m4x; }",
295                           "package m4x; public class M4 {}");
296 
297         Path out = base.resolve("out");
298 
299         Files.createDirectories(out);
300 
301         JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
302         try (StandardJavaFileManager fm = javaCompiler.getStandardFileManager(null, null, null)) {
303             com.sun.source.util.JavacTask task =
304                 (com.sun.source.util.JavacTask) javaCompiler.getTask(null,
305                                                               null,
306                                                               d -> { throw new IllegalStateException(d.toString()); },
307                                                               Arrays.asList("--module-path", modulePath.toString(),
308                                                                             "--class-path", cp.toString(),
309                                                                             "--module-source-path", moduleSrc.toString(),
310                                                                             "-d", out.toString()),
311                                                               null,
312                                                               fm.getJavaFileObjects(findJavaFiles(moduleSrc)));
313             assertNotNull(task.getElements().getTypeElement("java.lang.String"));
314             assertNull(task.getElements().getTypeElement("javax.tools.ToolProvider"));
315             assertNotNull(task.getElements().getTypeElement("m1x.M1"));
316             assertNull(task.getElements().getTypeElement("m2x.M2"));
317             assertNotNull(task.getElements().getTypeElement("m3x.M3"));
318             assertNotNull(task.getElements().getTypeElement("m4x.M4"));
319             assertNotNull(task.getElements().getModuleElement("java.base"));
320             assertNull(task.getElements().getModuleElement("java.compiler"));
321             assertNotNull(task.getElements().getModuleElement("m1x"));
322             assertNull(task.getElements().getModuleElement("m2x"));
323             assertNotNull(task.getElements().getModuleElement("m3x"));
324             assertNotNull(task.getElements().getModuleElement("m4x"));
325         }
326     }
327 
328     @Test
testTooSoon(Path base)329     public void testTooSoon(Path base) throws Exception {
330         Path src = base.resolve("src");
331 
332         tb.writeJavaFiles(src,
333                           "package test; public class Test {}");
334 
335         Path out = base.resolve("out");
336 
337         Files.createDirectories(out);
338 
339         Path reg = base.resolve("reg");
340         Path regFile = reg.resolve("META-INF").resolve("services").resolve(Plugin.class.getName());
341 
342         Files.createDirectories(regFile.getParent());
343 
344         try (OutputStream regOut = Files.newOutputStream(regFile)) {
345             regOut.write(PluginImpl.class.getName().getBytes());
346         }
347 
348         String processorPath = System.getProperty("test.class.path") + File.pathSeparator + reg.toString();
349 
350         JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
351         Path testSource = src.resolve("test").resolve("Test.java");
352         try (StandardJavaFileManager fm = javaCompiler.getStandardFileManager(null, null, null)) {
353             com.sun.source.util.JavacTask task =
354                 (com.sun.source.util.JavacTask) javaCompiler.getTask(null,
355                                                               null,
356                                                               d -> { throw new IllegalStateException(d.toString()); },
357                                                               Arrays.asList("--processor-path", processorPath,
358                                                                             "-processor", AP.class.getName(),
359                                                                             "-Xplugin:test"),
360                                                               null,
361                                                               fm.getJavaFileObjects(testSource));
362             task.call();
363         }
364 
365         Main.compile(new String[] {"--processor-path", processorPath,
366                                    "-Xplugin:test",
367                                    testSource.toString()});
368     }
369 
370     public static class PluginImpl implements Plugin {
371 
372         @Override
getName()373         public String getName() {
374             return "test";
375         }
376 
377         @Override
init(com.sun.source.util.JavacTask task, String... args)378         public void init(com.sun.source.util.JavacTask task, String... args) {
379             task.addTaskListener(new TaskListener() {
380                 boolean wasEntered;
381                 @Override
382                 public void started(TaskEvent e) {
383                     switch (e.getKind()) {
384                         case COMPILATION: case PARSE:
385                             shouldFail(e.getKind());
386                             break;
387                         case ANNOTATION_PROCESSING: case ENTER:
388                             if (wasEntered) {
389                                 shouldPass(e.getKind());
390                             } else {
391                                 shouldFail(e.getKind());
392                             }
393                             break;
394                         default:
395                             shouldPass(e.getKind());
396                             break;
397                     }
398                 }
399                 @Override
400                 public void finished(TaskEvent e) {
401                     switch (e.getKind()) {
402                         case PARSE:
403                             shouldFail(e.getKind());
404                             break;
405                         case ENTER:
406                             wasEntered = true;
407                             //fall-through:
408                         default:
409                             shouldPass(e.getKind());
410                             break;
411                     }
412                 }
413                 private void shouldFail(TaskEvent.Kind kind) {
414                     try {
415                         task.getElements().getTypeElement("java.lang.String");
416                         throw new AssertionError("Expected exception not observed; kind=" + kind.name());
417                     } catch (IllegalStateException ex) {
418                         //correct
419                     }
420                 }
421                 private void shouldPass(TaskEvent.Kind kind) {
422                     assertNotNull(task.getElements().getTypeElement("java.lang.String"));
423                 }
424             });
425 
426         }
427 
428     }
429 
430     @SupportedAnnotationTypes("*")
431     public static final class AP extends AbstractProcessor {
432 
433         @Override
process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)434         public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
435             return false;
436         }
437 
438         @Override
getSupportedSourceVersion()439         public SourceVersion getSupportedSourceVersion() {
440             return SourceVersion.latest();
441         }
442 
443     }
444 
445     @Test
testBrokenModule(Path base)446     public void testBrokenModule(Path base) throws Exception {
447         Map<String, String> sourceFileName2Content = new HashMap<>();
448 
449         sourceFileName2Content.put("module-info.java", "module test { requires unknown.; } ");
450         sourceFileName2Content.put("test/Test.java", "package test; public class Test {}");
451 
452         Path out = base.resolve("out");
453 
454         Files.createDirectories(out);
455 
456         JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
457         try (StandardJavaFileManager fm = javaCompiler.getStandardFileManager(null, null, null)) {
458             com.sun.source.util.JavacTask task =
459                 (com.sun.source.util.JavacTask) javaCompiler.getTask(null,
460                                                               new TestMemoryFileManager(fm, sourceFileName2Content),
461                                                               null,
462                                                               Arrays.asList("-d", out.toString()),
463                                                               null,
464                                                               null);
465             task.getElements().getTypeElement("test.Test");
466         }
467     }
468 
469     private static final class TestMemoryFileManager extends ForwardingJavaFileManager<JavaFileManager> {
470 
471         private final Map<String, String> sourceFileName2Content;
472 
TestMemoryFileManager(JavaFileManager fileManager, Map<String, String> sourceFileName2Content)473         public TestMemoryFileManager(JavaFileManager fileManager, Map<String, String> sourceFileName2Content) {
474             super(fileManager);
475             this.sourceFileName2Content = sourceFileName2Content;
476         }
477 
478         @Override
list(Location location, String packageName, Set<Kind> kinds, boolean recurse)479         public Iterable<JavaFileObject> list(Location location, String packageName, Set<Kind> kinds, boolean recurse) throws IOException {
480             if (location == StandardLocation.SOURCE_PATH) {
481                 List<JavaFileObject> result = new ArrayList<>();
482                 String dir = packageName.replace('.', '/') + "/";
483 
484                 for (Entry<String, String> e : sourceFileName2Content.entrySet()) {
485                     if (e.getKey().startsWith(dir) &&
486                         !e.getKey().substring(dir.length()).contains("/")) {
487                         try {
488                             result.add(new SourceFileObject(e.getKey(), e.getValue()));
489                         } catch (URISyntaxException ex) {
490                             throw new IOException(ex);
491                         }
492                     }
493                 }
494 
495                 return result;
496             }
497             return super.list(location, packageName, kinds, recurse);
498         }
499 
500         @Override
getJavaFileForInput(Location location, String className, Kind kind)501         public JavaFileObject getJavaFileForInput(Location location, String className, Kind kind) throws IOException {
502             if (location == StandardLocation.SOURCE_PATH) {
503                 String path = className.replace('.', '/') + ".java";
504                 String code = sourceFileName2Content.get(path);
505 
506                 if (code == null) return null;
507 
508                 try {
509                     return new SourceFileObject(path, code);
510                 } catch (URISyntaxException ex) {
511                     throw new IOException(ex);
512                 }
513             }
514             return super.getJavaFileForInput(location, className, kind);
515         }
516 
517         @Override
hasLocation(Location location)518         public boolean hasLocation(Location location) {
519             return super.hasLocation(location) || location == StandardLocation.SOURCE_PATH;
520         }
521 
522         @Override
contains(Location location, FileObject fo)523         public boolean contains(Location location, FileObject fo) throws IOException {
524             if (location == StandardLocation.SOURCE_PATH) {
525                 return fo instanceof SourceFileObject;
526             }
527             return super.contains(location, fo);
528         }
529 
530         @Override
inferBinaryName(Location location, JavaFileObject file)531         public String inferBinaryName(Location location, JavaFileObject file) {
532             if (location == StandardLocation.SOURCE_PATH) {
533                 String path = ((SourceFileObject) file).path;
534                 String fileName = path.substring(path.lastIndexOf('/'));
535                 return fileName.substring(0, fileName.length() - ".java".length());
536             }
537             return super.inferBinaryName(location, file);
538         }
539 
540     }
541 
542     private static final class SourceFileObject extends SimpleJavaFileObject {
543         private final String path;
544         private final String code;
545 
SourceFileObject(String path, String code)546         public SourceFileObject(String path, String code) throws URISyntaxException {
547             super(new URI("mem://" + path), Kind.SOURCE);
548             this.path = path;
549             this.code = code;
550         }
551 
552         @Override
getCharContent(boolean ignoreEncodingErrors)553         public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
554             return code;
555         }
556 
557         @Override
isNameCompatible(String simpleName, Kind kind)558         public boolean isNameCompatible(String simpleName, Kind kind) {
559             return path.endsWith(simpleName + kind.extension);
560         }
561 
562     }
563 
assertNotNull(Object actual)564     private static void assertNotNull(Object actual) {
565         if (actual == null) {
566             throw new AssertionError("unexpected null!");
567         }
568     }
569 
assertNull(Object actual)570     private static void assertNull(Object actual) {
571         if (actual != null) {
572             throw new AssertionError("unexpected non null!");
573         }
574     }
575 
576 }
577