1 /*
2  * Copyright (c) 2009, 2014, 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 import java.io.BufferedWriter;
25 import java.io.File;
26 import java.io.FileWriter;
27 import java.io.IOException;
28 import java.io.PrintStream;
29 import java.io.PrintWriter;
30 import java.lang.annotation.*;
31 import java.lang.reflect.*;
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.Collections;
35 import java.util.HashMap;
36 import java.util.List;
37 import java.util.Map;
38 
39 import com.sun.tools.classfile.ClassFile;
40 import com.sun.tools.classfile.TypeAnnotation;
41 import com.sun.tools.classfile.TypeAnnotation.TargetType;
42 
43 import static java.lang.String.format;
44 
45 public class Driver {
46 
47     private static final PrintStream out = System.err;
48 
49     private final Object testObject;
50 
Driver(Class<?> clazz)51     public Driver(Class<?> clazz) throws IllegalAccessException, InstantiationException {
52         testObject = clazz.newInstance();
53     }
54 
main(String[] args)55     public static void main(String[] args) throws Exception {
56         if (args.length == 0 || args.length > 1)
57             throw new IllegalArgumentException("Usage: java Driver <test-name>");
58         String name = args[0];
59         new Driver(Class.forName(name)).runDriver();
60     }
61 
62     private final String[][] extraParamsCombinations = new String[][] {
63         new String[] { },
64         new String[] { "-g" },
65     };
66 
67     private final String[] retentionPolicies = {RetentionPolicy.CLASS.toString(), RetentionPolicy.RUNTIME.toString()};
68 
runDriver()69     protected void runDriver() {
70         int passed = 0, failed = 0;
71         Class<?> clazz = testObject.getClass();
72         out.println("Tests for " + clazz.getName());
73 
74         // Find methods
75         for (Method method : clazz.getMethods()) {
76             try {
77                 Map<String, TypeAnnotation.Position> expected = expectedOf(method);
78                 if (expected == null)
79                     continue;
80                 if (method.getReturnType() != String.class)
81                     throw new IllegalArgumentException("Test method needs to return a string: " + method);
82 
83                 String compact = (String) method.invoke(testObject);
84                 for (String retentionPolicy : retentionPolicies) {
85                     String testClassName = getTestClassName(method, retentionPolicy);
86                     String testClass = testClassOf(method, testClassName);
87                     String fullFile = wrap(compact, new HashMap<String, String>() {{
88                         put("%RETENTION_POLICY%", retentionPolicy);
89                         put("%TEST_CLASS_NAME%", testClassName);
90                     }});
91                     for (String[] extraParams : extraParamsCombinations) {
92                         try {
93                             ClassFile cf = compileAndReturn(fullFile, testClass, extraParams);
94                             List<TypeAnnotation> actual = ReferenceInfoUtil.extendedAnnotationsOf(cf);
95                             ReferenceInfoUtil.compare(expected, actual, cf);
96                             out.format("PASSED:  %s %s%n", testClassName, Arrays.toString(extraParams));
97                             ++passed;
98                         } catch (Throwable e) {
99                             out.format("FAILED:  %s %s%n", testClassName, Arrays.toString(extraParams));
100                             out.println(fullFile);
101                             out.println("    " + e.toString());
102                             e.printStackTrace(out);
103                             ++failed;
104                         }
105                     }
106                 }
107             } catch (IllegalAccessException | InvocationTargetException e) {
108                 out.println("FAILED:  " + method.getName());
109                 out.println("    " + e.toString());
110                 e.printStackTrace(out);
111                 ++failed;
112             }
113         }
114 
115         out.println();
116         int total = passed + failed;
117         out.println(total + " total tests: " + passed + " PASSED, " + failed + " FAILED");
118 
119         out.flush();
120 
121         if (failed != 0)
122             throw new RuntimeException(failed + " tests failed");
123     }
124 
expectedOf(Method m)125     private Map<String, TypeAnnotation.Position> expectedOf(Method m) {
126         TADescription ta = m.getAnnotation(TADescription.class);
127         TADescriptions tas = m.getAnnotation(TADescriptions.class);
128 
129         if (ta == null && tas == null)
130             return null;
131 
132         Map<String, TypeAnnotation.Position> result =
133             new HashMap<>();
134 
135         if (ta != null)
136             result.putAll(expectedOf(ta));
137 
138         if (tas != null) {
139             for (TADescription a : tas.value()) {
140                 result.putAll(expectedOf(a));
141             }
142         }
143 
144         return result;
145     }
146 
expectedOf(TADescription d)147     private Map<String, TypeAnnotation.Position> expectedOf(TADescription d) {
148         String annoName = d.annotation();
149 
150         TypeAnnotation.Position p = new TypeAnnotation.Position();
151         p.type = d.type();
152         if (d.offset() != NOT_SET)
153             p.offset = d.offset();
154         if (d.lvarOffset().length != 0)
155             p.lvarOffset = d.lvarOffset();
156         if (d.lvarLength().length != 0)
157             p.lvarLength = d.lvarLength();
158         if (d.lvarIndex().length != 0)
159             p.lvarIndex = d.lvarIndex();
160         if (d.boundIndex() != NOT_SET)
161             p.bound_index = d.boundIndex();
162         if (d.paramIndex() != NOT_SET)
163             p.parameter_index = d.paramIndex();
164         if (d.typeIndex() != NOT_SET)
165             p.type_index = d.typeIndex();
166         if (d.exceptionIndex() != NOT_SET)
167             p.exception_index = d.exceptionIndex();
168         if (d.genericLocation().length != 0) {
169             p.location = TypeAnnotation.Position.getTypePathFromBinary(wrapIntArray(d.genericLocation()));
170         }
171 
172         return Collections.singletonMap(annoName, p);
173     }
174 
wrapIntArray(int[] ints)175     private List<Integer> wrapIntArray(int[] ints) {
176         List<Integer> list = new ArrayList<>(ints.length);
177         for (int i : ints)
178             list.add(i);
179         return list;
180     }
181 
getTestClassName(Method m, String retentionPolicy)182     private String getTestClassName(Method m, String retentionPolicy) {
183         return format("%s_%s_%s", testObject.getClass().getSimpleName(),
184                 m.getName(), retentionPolicy);
185     }
186 
testClassOf(Method m, String testClassName)187     private String testClassOf(Method m, String testClassName) {
188         TestClass tc = m.getAnnotation(TestClass.class);
189         if (tc != null) {
190             return tc.value().replace("%TEST_CLASS_NAME%", testClassName);
191         } else {
192             return testClassName;
193         }
194     }
195 
compileAndReturn(String fullFile, String testClass, String... extraParams)196     private ClassFile compileAndReturn(String fullFile, String testClass, String... extraParams) throws Exception {
197         File source = writeTestFile(fullFile, testClass);
198         File clazzFile = compileTestFile(source, testClass, extraParams);
199         return ClassFile.read(clazzFile);
200     }
201 
writeTestFile(String fullFile, String testClass)202     protected File writeTestFile(String fullFile, String testClass) throws IOException {
203         File f = new File(getClassDir(), format("%s.java", testClass));
204         try (PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(f)))) {
205             out.println(fullFile);
206             return f;
207         }
208     }
209 
getClassDir()210     private String getClassDir() {
211         return System.getProperty("test.classes", Driver.class.getResource(".").getPath());
212     }
213 
compileTestFile(File f, String testClass, String... extraParams)214     protected File compileTestFile(File f, String testClass, String... extraParams) {
215         List<String> options = new ArrayList<>();
216         options.addAll(Arrays.asList(extraParams));
217         options.add(f.getPath());
218         int rc = com.sun.tools.javac.Main.compile(options.toArray(new String[options.size()]));
219         if (rc != 0)
220             throw new Error("compilation failed. rc=" + rc);
221         String path = f.getParent() != null ? f.getParent() : "";
222         return new File(path, format("%s.class", testClass));
223     }
224 
wrap(String compact, Map<String, String> replacements)225     private String wrap(String compact, Map<String, String> replacements) {
226         StringBuilder sb = new StringBuilder();
227 
228         // Automatically import java.util
229         sb.append("\nimport java.io.*;");
230         sb.append("\nimport java.util.*;");
231         sb.append("\nimport java.lang.annotation.*;");
232 
233         sb.append("\n\n");
234         boolean isSnippet = !(compact.startsWith("class")
235                               || compact.contains(" class"))
236                             && !compact.contains("interface")
237                             && !compact.contains("enum");
238         if (isSnippet)
239             sb.append("class %TEST_CLASS_NAME% {\n");
240 
241         sb.append(compact);
242         sb.append("\n");
243 
244         if (isSnippet)
245             sb.append("}\n\n");
246 
247         if (isSnippet) {
248             // Have a few common nested types for testing
249             sb.append("class Outer { class Inner {} class Middle { class MInner {} } }");
250             sb.append("class SOuter { static class SInner {} }");
251             sb.append("class GOuter<X, Y> { class GInner<X, Y> {} }");
252         }
253 
254         // create A ... F annotation declarations
255         sb.append("\n@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface A {}");
256         sb.append("\n@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface B {}");
257         sb.append("\n@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface C {}");
258         sb.append("\n@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface D {}");
259         sb.append("\n@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface E {}");
260         sb.append("\n@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface F {}");
261 
262         // create TA ... TF proper type annotations
263         sb.append("\n");
264         sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
265                 " @Retention(RetentionPolicy.%RETENTION_POLICY%)  @interface TA {}");
266         sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
267                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface TB {}");
268         sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
269                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface TC {}");
270         sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
271                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface TD {}");
272         sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
273                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface TE {}");
274         sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
275                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface TF {}");
276         sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
277                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface TG {}");
278         sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
279                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface TH {}");
280         sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
281                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface TI {}");
282         sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
283                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface TJ {}");
284         sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
285                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface TK {}");
286         sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
287                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface TL {}");
288         sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
289                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface TM {}");
290 
291         // create RT?, RT?s for repeating type annotations
292         sb.append("\n");
293         sb.append("\n@Repeatable(RTAs.class) @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
294                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface RTA {}");
295         sb.append("\n@Repeatable(RTBs.class) @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
296                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface RTB {}");
297         sb.append("\n@Repeatable(RTCs.class) @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
298                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface RTC {}");
299         sb.append("\n@Repeatable(RTDs.class) @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
300                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface RTD {}");
301         sb.append("\n@Repeatable(RTEs.class) @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
302                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface RTE {}");
303         sb.append("\n@Repeatable(RTFs.class) @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
304                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface RTF {}");
305         sb.append("\n@Repeatable(RTGs.class) @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
306                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface RTG {}");
307         sb.append("\n@Repeatable(RTHs.class) @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
308                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface RTH {}");
309         sb.append("\n@Repeatable(RTIs.class) @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
310                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface RTI {}");
311         sb.append("\n@Repeatable(RTJs.class) @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
312                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface RTJ {}");
313         sb.append("\n@Repeatable(RTKs.class) @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
314                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface RTK {}");
315 
316         sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
317                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface RTAs { RTA[] value(); }");
318         sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
319                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface RTBs { RTB[] value(); }");
320         sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
321                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface RTCs { RTC[] value(); }");
322         sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
323                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface RTDs { RTD[] value(); }");
324         sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
325                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface RTEs { RTE[] value(); }");
326         sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
327                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface RTFs { RTF[] value(); }");
328         sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
329                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface RTGs { RTG[] value(); }");
330         sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
331                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface RTHs { RTH[] value(); }");
332         sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
333                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface RTIs { RTI[] value(); }");
334         sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
335                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface RTJs { RTJ[] value(); }");
336         sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})" +
337                 "@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface RTKs { RTK[] value(); }");
338 
339         sb.append("\n@Target(value={ElementType.TYPE,ElementType.FIELD,ElementType.METHOD," +
340                 "ElementType.PARAMETER,ElementType.CONSTRUCTOR,ElementType.LOCAL_VARIABLE})");
341         sb.append("\n@Retention(RetentionPolicy.%RETENTION_POLICY%) @interface Decl {}");
342 
343         return replaceAll(sb.toString(), replacements);
344     }
345 
replaceAll(String src, Map<String, String> replacements)346     private String replaceAll(String src, Map<String, String> replacements) {
347         for (Map.Entry<String, String> entry : replacements.entrySet()) {
348             src = src.replace(entry.getKey(), entry.getValue());
349         }
350         return src;
351     }
352 
353     public static final int NOT_SET = -888;
354 
355 }
356 
357 @Retention(RetentionPolicy.RUNTIME)
358 @Target(ElementType.METHOD)
359 @Repeatable(TADescriptions.class)
360 @interface TADescription {
annotation()361     String annotation();
362 
type()363     TargetType type();
offset()364     int offset() default Driver.NOT_SET;
lvarOffset()365     int[] lvarOffset() default { };
lvarLength()366     int[] lvarLength() default { };
lvarIndex()367     int[] lvarIndex() default { };
boundIndex()368     int boundIndex() default Driver.NOT_SET;
paramIndex()369     int paramIndex() default Driver.NOT_SET;
typeIndex()370     int typeIndex() default Driver.NOT_SET;
exceptionIndex()371     int exceptionIndex() default Driver.NOT_SET;
372 
genericLocation()373     int[] genericLocation() default {};
374 }
375 
376 @Retention(RetentionPolicy.RUNTIME)
377 @Target(ElementType.METHOD)
378 @interface TADescriptions {
value()379     TADescription[] value() default {};
380 }
381 
382 /**
383  * The name of the class that should be analyzed.
384  * Should only need to be provided when analyzing inner classes.
385  */
386 @Retention(RetentionPolicy.RUNTIME)
387 @Target(ElementType.METHOD)
388 @interface TestClass {
value()389     String value();
390 }
391