TestClientCodeWrapper.java revision 2933:49d207bf704d
1/*
2 * Copyright (c) 2011, 2015, 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 6437138 6482554
27 * @summary JSR 199: Compiler doesn't diagnose crash in user code
28 * @library ../lib
29 * @modules jdk.compiler/com.sun.tools.javac.api
30 * @build JavacTestingAbstractProcessor TestClientCodeWrapper
31 * @run main TestClientCodeWrapper
32 */
33
34import java.io.*;
35import java.lang.reflect.Method;
36import java.net.URI;
37import java.util.*;
38import javax.annotation.processing.*;
39import javax.lang.model.*;
40import javax.lang.model.element.*;
41import javax.tools.*;
42import com.sun.source.util.*;
43import com.sun.tools.javac.api.*;
44import javax.tools.JavaFileObject.Kind;
45
46public class TestClientCodeWrapper extends JavacTestingAbstractProcessor {
47    public static void main(String... args) throws Exception {
48        new TestClientCodeWrapper().run();
49    }
50
51    /**
52     * Run a series of compilations, each with a different user-provided object
53     * configured to throw an exception when a specific method is invoked.
54     * Then, verify the exception is thrown as expected.
55     *
56     * Some methods are not invoked from the compiler, and are excluded from the test.
57     */
58    void run() throws Exception {
59        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
60        try (StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null)) {
61            defaultFileManager = fm;
62
63            for (Method m: getMethodsExcept(JavaFileManager.class, "close", "getJavaFileForInput")) {
64                test(m);
65            }
66
67            for (Method m: getMethodsExcept(FileObject.class, "delete")) {
68                test(m);
69            }
70
71            for (Method m: getMethods(JavaFileObject.class)) {
72                test(m);
73            }
74
75            for (Method m: getMethodsExcept(Processor.class, "getCompletions")) {
76                test(m);
77            }
78
79            for (Method m: DiagnosticListener.class.getDeclaredMethods()) {
80                test(m);
81            }
82
83            for (Method m: TaskListener.class.getDeclaredMethods()) {
84                test(m);
85            }
86
87            if (errors > 0)
88                throw new Exception(errors + " errors occurred");
89        }
90    }
91
92    /** Get a sorted set of the methods declared on a class. */
93    Set<Method> getMethods(Class<?> clazz) {
94        return getMethodsExcept(clazz, new String[0]);
95    }
96
97    /** Get a sorted set of the methods declared on a class, excluding
98     *  specified methods by name. */
99    Set<Method> getMethodsExcept(Class<?> clazz, String... exclude) {
100        Set<Method> methods = new TreeSet<Method>(new Comparator<Method>() {
101            public int compare(Method m1, Method m2) {
102                return m1.toString().compareTo(m2.toString());
103            }
104        });
105        Set<String> e = new HashSet<String>(Arrays.asList(exclude));
106        for (Method m: clazz.getDeclaredMethods()) {
107            if (!e.contains(m.getName()))
108                methods.add(m);
109        }
110        return methods;
111    }
112
113    /**
114     * Test a method in a user supplied component, to verify javac's handling
115     * of any exceptions thrown by that method.
116     */
117    void test(Method m) throws Exception {
118        testNum++;
119
120        File extDirs = new File("empty-extdirs");
121        extDirs.mkdirs();
122
123        File testClasses = new File("test" + testNum);
124        testClasses.mkdirs();
125        defaultFileManager.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(testClasses));
126
127        System.err.println("test " + testNum + ": "
128                + m.getDeclaringClass().getSimpleName() + "." + m.getName());
129
130        StringWriter sw = new StringWriter();
131        PrintWriter pw = new PrintWriter(sw);
132
133        List<String> javacOptions = Arrays.asList(
134                "-extdirs", extDirs.getPath(), // for use by filemanager handleOption
135                "-processor", TestClientCodeWrapper.class.getName()
136                );
137
138        List<String> classes = Collections.emptyList();
139
140        JavacTool tool = JavacTool.create();
141        try {
142            JavacTask task = tool.getTask(pw,
143                    getFileManager(m, defaultFileManager),
144                    getDiagnosticListener(m, pw),
145                    javacOptions,
146                    classes,
147                    getCompilationUnits(m));
148
149            if (isDeclaredIn(m, Processor.class))
150                task.setProcessors(getProcessors(m));
151
152            if (isDeclaredIn(m, TaskListener.class))
153                task.setTaskListener(getTaskListener(m, pw));
154
155            boolean ok = task.call();
156            error("compilation " + (ok ? "succeeded" : "failed") + " unexpectedly");
157        } catch (RuntimeException e) {
158            System.err.println("caught " + e);
159            if (e.getClass() == RuntimeException.class) {
160                Throwable cause = e.getCause();
161                if (cause instanceof UserError) {
162                    String expect = m.getName();
163                    String found = cause.getMessage();
164                    checkEqual("exception messaqe", expect, found);
165                } else {
166                    cause.printStackTrace(System.err);
167                    error("Unexpected exception: " + cause);
168                }
169            } else {
170                e.printStackTrace(System.err);
171                error("Unexpected exception: " + e);
172            }
173        }
174
175        pw.close();
176        String out = sw.toString();
177        System.err.println(out);
178    }
179
180    /** Get a file manager to use for the test compilation. */
181    JavaFileManager getFileManager(Method m, JavaFileManager defaultFileManager) {
182        return isDeclaredIn(m, JavaFileManager.class, FileObject.class, JavaFileObject.class)
183                ? new UserFileManager(m, defaultFileManager)
184                : defaultFileManager;
185    }
186
187    /** Get a diagnostic listener to use for the test compilation. */
188    DiagnosticListener<JavaFileObject> getDiagnosticListener(Method m, PrintWriter out) {
189        return isDeclaredIn(m, DiagnosticListener.class)
190                ? new UserDiagnosticListener(m, out)
191                : null;
192    }
193
194    /** Get a set of file objects to use for the test compilation. */
195    Iterable<? extends JavaFileObject> getCompilationUnits(Method m) {
196        File testSrc = new File(System.getProperty("test.src"));
197        File thisSrc = new File(testSrc, TestClientCodeWrapper.class.getName() + ".java");
198        Iterable<? extends JavaFileObject> files = defaultFileManager.getJavaFileObjects(thisSrc);
199        if (isDeclaredIn(m, FileObject.class, JavaFileObject.class))
200            return Arrays.asList(new UserFileObject(m, files.iterator().next()));
201        else
202            return files;
203    }
204
205    /** Get a set of annotation processors to use for the test compilation. */
206    Iterable<? extends Processor> getProcessors(Method m) {
207        return Arrays.asList(new UserProcessor(m));
208    }
209
210    /** Get a task listener to use for the test compilation. */
211    TaskListener getTaskListener(Method m, PrintWriter out) {
212        return new UserTaskListener(m, out);
213    }
214
215    /** Check if two values are .equal, and report an error if not. */
216    <T> void checkEqual(String label, T expect, T found) {
217        if (!expect.equals(found))
218            error("Unexpected value for " + label + ": " + found + "; expected: " + expect);
219    }
220
221    /** Report an error. */
222    void error(String msg) {
223        System.err.println("Error: " + msg);
224        errors++;
225    }
226
227    /** Check if a method is declared in any of a set of classes */
228    static boolean isDeclaredIn(Method m, Class<?>... classes) {
229        Class<?> dc = m.getDeclaringClass();
230        for (Class<?> c: classes) {
231            if (c == dc) return true;
232        }
233        return false;
234    }
235
236    /** Throw an intentional error if the method has a given name. */
237    static void throwUserExceptionIfNeeded(Method m, String name) {
238        if (m != null && m.getName().equals(name))
239            throw new UserError(name);
240    }
241
242    StandardJavaFileManager defaultFileManager;
243    int testNum;
244    int errors;
245
246    //--------------------------------------------------------------------------
247
248    /**
249     * Processor used to trigger use of methods not normally used by javac.
250     */
251    @Override
252    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
253        boolean firstRound = false;
254        for (Element e: roundEnv.getRootElements()) {
255            if (e.getSimpleName().contentEquals(TestClientCodeWrapper.class.getSimpleName()))
256                firstRound = true;
257        }
258        if (firstRound) {
259            try {
260                FileObject f1 = filer.getResource(StandardLocation.CLASS_PATH, "",
261                    TestClientCodeWrapper.class.getName() + ".java");
262                f1.openInputStream().close();
263                f1.openReader(false).close();
264
265                FileObject f2 = filer.createResource(
266                        StandardLocation.CLASS_OUTPUT, "", "f2.txt", (Element[]) null);
267                f2.openOutputStream().close();
268
269                FileObject f3 = filer.createResource(
270                        StandardLocation.CLASS_OUTPUT, "", "f3.txt", (Element[]) null);
271                f3.openWriter().close();
272
273                JavaFileObject f4 = filer.createSourceFile("f4", (Element[]) null);
274                f4.openWriter().close();
275                f4.getNestingKind();
276                f4.getAccessLevel();
277
278                messager.printMessage(Diagnostic.Kind.NOTE, "informational note",
279                        roundEnv.getRootElements().iterator().next());
280
281            } catch (IOException e) {
282                throw new UserError(e);
283            }
284        }
285        return true;
286    }
287
288    //--------------------------------------------------------------------------
289
290    // <editor-fold defaultstate="collapsed" desc="User classes">
291
292    static class UserError extends Error {
293        private static final long serialVersionUID = 1L;
294        UserError(String msg) {
295            super(msg);
296        }
297        UserError(Throwable t) {
298            super(t);
299        }
300    }
301
302    static class UserFileManager extends ForwardingJavaFileManager<JavaFileManager> {
303        Method fileManagerMethod;
304        Method fileObjectMethod;
305
306        UserFileManager(Method m, JavaFileManager delegate) {
307            super(delegate);
308            if (isDeclaredIn(m, JavaFileManager.class)) {
309                fileManagerMethod = m;
310            } else if (isDeclaredIn(m, FileObject.class, JavaFileObject.class)) {
311                fileObjectMethod = m;
312            } else
313                assert false;
314        }
315
316        @Override
317        public ClassLoader getClassLoader(Location location) {
318            throwUserExceptionIfNeeded(fileManagerMethod, "getClassLoader");
319            return super.getClassLoader(location);
320        }
321
322        @Override
323        public Iterable<JavaFileObject> list(Location location, String packageName, Set<Kind> kinds, boolean recurse) throws IOException {
324            throwUserExceptionIfNeeded(fileManagerMethod, "list");
325            return wrap(super.list(location, packageName, kinds, recurse));
326        }
327
328        @Override
329        public String inferBinaryName(Location location, JavaFileObject file) {
330            throwUserExceptionIfNeeded(fileManagerMethod, "inferBinaryName");
331            return super.inferBinaryName(location, unwrap(file));
332        }
333
334        @Override
335        public boolean isSameFile(FileObject a, FileObject b) {
336            throwUserExceptionIfNeeded(fileManagerMethod, "isSameFile");
337            return super.isSameFile(unwrap(a), unwrap(b));
338        }
339
340        @Override
341        public boolean handleOption(String current, Iterator<String> remaining) {
342            throwUserExceptionIfNeeded(fileManagerMethod, "handleOption");
343            return super.handleOption(current, remaining);
344        }
345
346        @Override
347        public boolean hasLocation(Location location) {
348            throwUserExceptionIfNeeded(fileManagerMethod, "hasLocation");
349            return super.hasLocation(location);
350        }
351
352        @Override
353        public JavaFileObject getJavaFileForInput(Location location, String className, Kind kind) throws IOException {
354            throwUserExceptionIfNeeded(fileManagerMethod, "getJavaFileForInput");
355            return wrap(super.getJavaFileForInput(location, className, kind));
356        }
357
358        @Override
359        public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException {
360            throwUserExceptionIfNeeded(fileManagerMethod, "getJavaFileForOutput");
361            return wrap(super.getJavaFileForOutput(location, className, kind, sibling));
362        }
363
364        @Override
365        public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {
366            throwUserExceptionIfNeeded(fileManagerMethod, "getFileForInput");
367            return wrap(super.getFileForInput(location, packageName, relativeName));
368        }
369
370        @Override
371        public FileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling) throws IOException {
372            throwUserExceptionIfNeeded(fileManagerMethod, "getFileForOutput");
373            return wrap(super.getFileForOutput(location, packageName, relativeName, sibling));
374        }
375
376        @Override
377        public void flush() throws IOException {
378            throwUserExceptionIfNeeded(fileManagerMethod, "flush");
379            super.flush();
380        }
381
382        @Override
383        public void close() throws IOException {
384            throwUserExceptionIfNeeded(fileManagerMethod, "close");
385            super.close();
386        }
387
388        @Override
389        public int isSupportedOption(String option) {
390            throwUserExceptionIfNeeded(fileManagerMethod, "isSupportedOption");
391            return super.isSupportedOption(option);
392        }
393
394        public FileObject wrap(FileObject fo) {
395            if (fileObjectMethod == null)
396                return fo;
397            return new UserFileObject(fileObjectMethod, (JavaFileObject)fo);
398        }
399
400        FileObject unwrap(FileObject fo) {
401            if (fo instanceof UserFileObject)
402                return ((UserFileObject) fo).unwrap();
403            else
404                return fo;
405        }
406
407        public JavaFileObject wrap(JavaFileObject fo) {
408            if (fileObjectMethod == null)
409                return fo;
410            return new UserFileObject(fileObjectMethod, fo);
411        }
412
413        public Iterable<JavaFileObject> wrap(Iterable<? extends JavaFileObject> list) {
414            List<JavaFileObject> wrapped = new ArrayList<JavaFileObject>();
415            for (JavaFileObject fo : list)
416                wrapped.add(wrap(fo));
417            return Collections.unmodifiableList(wrapped);
418        }
419
420        JavaFileObject unwrap(JavaFileObject fo) {
421            if (fo instanceof UserFileObject)
422                return ((UserFileObject) fo).unwrap();
423            else
424                return fo;
425        }
426    }
427
428    static class UserFileObject extends ForwardingJavaFileObject<JavaFileObject> {
429        Method method;
430
431        UserFileObject(Method m, JavaFileObject delegate) {
432            super(delegate);
433            assert isDeclaredIn(m, FileObject.class, JavaFileObject.class);
434            this.method = m;
435        }
436
437        JavaFileObject unwrap() {
438            return fileObject;
439        }
440
441        @Override
442        public Kind getKind() {
443            throwUserExceptionIfNeeded(method, "getKind");
444            return super.getKind();
445        }
446
447        @Override
448        public boolean isNameCompatible(String simpleName, Kind kind) {
449            throwUserExceptionIfNeeded(method, "isNameCompatible");
450            return super.isNameCompatible(simpleName, kind);
451        }
452
453        @Override
454        public NestingKind getNestingKind() {
455            throwUserExceptionIfNeeded(method, "getNestingKind");
456            return super.getNestingKind();
457        }
458
459        @Override
460        public Modifier getAccessLevel() {
461            throwUserExceptionIfNeeded(method, "getAccessLevel");
462            return super.getAccessLevel();
463        }
464
465        @Override
466        public URI toUri() {
467            throwUserExceptionIfNeeded(method, "toUri");
468            return super.toUri();
469        }
470
471        @Override
472        public String getName() {
473            throwUserExceptionIfNeeded(method, "getName");
474            return super.getName();
475        }
476
477        @Override
478        public InputStream openInputStream() throws IOException {
479            throwUserExceptionIfNeeded(method, "openInputStream");
480            return super.openInputStream();
481        }
482
483        @Override
484        public OutputStream openOutputStream() throws IOException {
485            throwUserExceptionIfNeeded(method, "openOutputStream");
486            return super.openOutputStream();
487        }
488
489        @Override
490        public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
491            throwUserExceptionIfNeeded(method, "openReader");
492            return super.openReader(ignoreEncodingErrors);
493        }
494
495        @Override
496        public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
497            throwUserExceptionIfNeeded(method, "getCharContent");
498            return super.getCharContent(ignoreEncodingErrors);
499        }
500
501        @Override
502        public Writer openWriter() throws IOException {
503            throwUserExceptionIfNeeded(method, "openWriter");
504            return super.openWriter();
505        }
506
507        @Override
508        public long getLastModified() {
509            throwUserExceptionIfNeeded(method, "getLastModified");
510            return super.getLastModified();
511        }
512
513        @Override
514        public boolean delete() {
515            throwUserExceptionIfNeeded(method, "delete");
516            return super.delete();
517        }
518
519    }
520
521    static class UserProcessor extends JavacTestingAbstractProcessor {
522        Method method;
523
524        UserProcessor(Method m) {
525            assert isDeclaredIn(m, Processor.class);
526            method = m;
527        }
528
529        @Override
530        public Set<String> getSupportedOptions() {
531            throwUserExceptionIfNeeded(method, "getSupportedOptions");
532            return super.getSupportedOptions();
533        }
534
535        @Override
536        public Set<String> getSupportedAnnotationTypes() {
537            throwUserExceptionIfNeeded(method, "getSupportedAnnotationTypes");
538            return super.getSupportedAnnotationTypes();
539        }
540
541        @Override
542        public SourceVersion getSupportedSourceVersion() {
543            throwUserExceptionIfNeeded(method, "getSupportedSourceVersion");
544            return super.getSupportedSourceVersion();
545        }
546
547        @Override
548        public void init(ProcessingEnvironment processingEnv) {
549            throwUserExceptionIfNeeded(method, "init");
550            super.init(processingEnv);
551        }
552
553        @Override
554        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
555            throwUserExceptionIfNeeded(method, "process");
556            return true;
557        }
558
559        @Override
560        public Iterable<? extends Completion> getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String userText) {
561            throwUserExceptionIfNeeded(method, "getCompletions");
562            return super.getCompletions(element, annotation, member, userText);
563        }
564    }
565
566    static class UserDiagnosticListener implements DiagnosticListener<JavaFileObject> {
567        Method method;
568        PrintWriter out;
569
570        UserDiagnosticListener(Method m, PrintWriter out) {
571            assert isDeclaredIn(m, DiagnosticListener.class);
572            this.method = m;
573            this.out = out;
574        }
575
576        @Override
577        public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
578            throwUserExceptionIfNeeded(method, "report");
579            out.println("report: " + diagnostic);
580        }
581    }
582
583    static class UserTaskListener implements TaskListener {
584        Method method;
585        PrintWriter out;
586
587        UserTaskListener(Method m, PrintWriter out) {
588            assert isDeclaredIn(m, TaskListener.class);
589            this.method = m;
590            this.out = out;
591        }
592
593        @Override
594        public void started(TaskEvent e) {
595            throwUserExceptionIfNeeded(method, "started");
596            out.println("started: " + e);
597        }
598
599        @Override
600        public void finished(TaskEvent e) {
601            throwUserExceptionIfNeeded(method, "finished");
602            out.println("finished: " + e);
603        }
604    }
605
606    // </editor-fold>
607}
608