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