1 /* 2 * Copyright (c) 2009, 2013 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.Collections; 34 import java.util.HashMap; 35 import java.util.List; 36 import java.util.Map; 37 38 import com.sun.tools.classfile.ClassFile; 39 import com.sun.tools.classfile.TypeAnnotation; 40 import com.sun.tools.classfile.TypeAnnotation.TargetType; 41 42 public class Driver { 43 44 private static final PrintStream out = System.out; 45 46 public static void main(String[] args) throws Exception { 47 if (args.length == 0 || args.length > 1) 48 throw new IllegalArgumentException("Usage: java Driver <test-name>"); 49 String name = args[0]; 50 Class<?> clazz = Class.forName(name); 51 new Driver().runDriver(clazz.newInstance()); 52 } 53 54 protected void runDriver(Object object) throws Exception { 55 int passed = 0, failed = 0; 56 Class<?> clazz = object.getClass(); 57 out.println("Tests for " + clazz.getName()); 58 59 // Find methods 60 for (Method method : clazz.getMethods()) { 61 Map<String, TypeAnnotation.Position> expected = expectedOf(method); 62 if (expected == null) 63 continue; 64 if (method.getReturnType() != String.class) 65 throw new IllegalArgumentException("Test method needs to return a string: " + method); 66 String testClass = testClassOf(method); 67 68 try { 69 String compact = (String)method.invoke(object); 70 String fullFile = wrap(compact); 71 ClassFile cf = compileAndReturn(fullFile, testClass); 72 List<TypeAnnotation> actual = ReferenceInfoUtil.extendedAnnotationsOf(cf); 73 ReferenceInfoUtil.compare(expected, actual, cf); 74 out.println("PASSED: " + method.getName()); 75 ++passed; 76 } catch (Throwable e) { 77 out.println("FAILED: " + method.getName()); 78 out.println(" " + e.toString()); 79 ++failed; 80 } 81 } 82 83 out.println(); 84 int total = passed + failed; 85 out.println(total + " total tests: " + passed + " PASSED, " + failed + " FAILED"); 86 87 out.flush(); 88 89 if (failed != 0) 90 throw new RuntimeException(failed + " tests failed"); 91 } 92 93 private Map<String, TypeAnnotation.Position> expectedOf(Method m) { 94 TADescription ta = m.getAnnotation(TADescription.class); 95 TADescriptions tas = m.getAnnotation(TADescriptions.class); 96 97 if (ta == null && tas == null) 98 return null; 99 100 Map<String, TypeAnnotation.Position> result = 101 new HashMap<String, TypeAnnotation.Position>(); 102 103 if (ta != null) 104 result.putAll(expectedOf(ta)); 105 106 if (tas != null) { 107 for (TADescription a : tas.value()) { 108 result.putAll(expectedOf(a)); 109 } 110 } 111 112 return result; 113 } 114 115 private Map<String, TypeAnnotation.Position> expectedOf(TADescription d) { 116 String annoName = d.annotation(); 117 118 TypeAnnotation.Position p = new TypeAnnotation.Position(); 119 p.type = d.type(); 120 if (d.offset() != NOT_SET) 121 p.offset = d.offset(); 122 if (d.lvarOffset().length != 0) 123 p.lvarOffset = d.lvarOffset(); 124 if (d.lvarLength().length != 0) 125 p.lvarLength = d.lvarLength(); 126 if (d.lvarIndex().length != 0) 127 p.lvarIndex = d.lvarIndex(); 128 if (d.boundIndex() != NOT_SET) 129 p.bound_index = d.boundIndex(); 130 if (d.paramIndex() != NOT_SET) 131 p.parameter_index = d.paramIndex(); 132 if (d.typeIndex() != NOT_SET) 133 p.type_index = d.typeIndex(); 134 if (d.exceptionIndex() != NOT_SET) 135 p.exception_index = d.exceptionIndex(); 136 if (d.genericLocation().length != 0) { 137 p.location = TypeAnnotation.Position.getTypePathFromBinary(wrapIntArray(d.genericLocation())); 138 } 139 140 return Collections.singletonMap(annoName, p); 141 } 142 143 private List<Integer> wrapIntArray(int[] ints) { 144 List<Integer> list = new ArrayList<Integer>(ints.length); 145 for (int i : ints) 146 list.add(i); 147 return list; 148 } 149 150 private String testClassOf(Method m) { 151 TestClass tc = m.getAnnotation(TestClass.class); 152 if (tc != null) { 153 return tc.value(); 154 } else { 155 return "Test"; 156 } 157 } 158 159 private ClassFile compileAndReturn(String fullFile, String testClass) throws Exception { 160 File source = writeTestFile(fullFile); 161 File clazzFile = compileTestFile(source, testClass); 162 return ClassFile.read(clazzFile); 163 } 164 165 protected File writeTestFile(String fullFile) throws IOException { 166 File f = new File("Test.java"); 167 PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(f))); 168 out.println(fullFile); 169 out.close(); 170 return f; 171 } 172 173 protected File compileTestFile(File f, String testClass) { 174 int rc = com.sun.tools.javac.Main.compile(new String[] { "-source", "1.8", "-g", f.getPath() }); 175 if (rc != 0) 176 throw new Error("compilation failed. rc=" + rc); 177 String path; 178 if (f.getParent() != null) { 179 path = f.getParent(); 180 } else { 181 path = ""; 182 } 183 184 return new File(path + testClass + ".class"); 185 } 186 187 private String wrap(String compact) { 188 StringBuilder sb = new StringBuilder(); 189 190 // Automatically import java.util 191 sb.append("\nimport java.util.*;"); 192 sb.append("\nimport java.lang.annotation.*;"); 193 194 sb.append("\n\n"); 195 boolean isSnippet = !(compact.startsWith("class") 196 || compact.contains(" class")) 197 && !compact.contains("interface") 198 && !compact.contains("enum"); 199 if (isSnippet) 200 sb.append("class Test {\n"); 201 202 sb.append(compact); 203 sb.append("\n"); 204 205 if (isSnippet) 206 sb.append("}\n\n"); 207 208 if (isSnippet) { 209 // Have a few common nested types for testing 210 sb.append("class Outer { class Inner {} }"); 211 sb.append("class SOuter { static class SInner {} }"); 212 sb.append("class GOuter<X, Y> { class GInner<X, Y> {} }"); 213 } 214 215 // create A ... F annotation declarations 216 sb.append("\n@interface A {}"); 217 sb.append("\n@interface B {}"); 218 sb.append("\n@interface C {}"); 219 sb.append("\n@interface D {}"); 220 sb.append("\n@interface E {}"); 221 sb.append("\n@interface F {}"); 222 223 // create TA ... TF proper type annotations 224 sb.append("\n"); 225 sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface TA {}"); 226 sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface TB {}"); 227 sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface TC {}"); 228 sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface TD {}"); 229 sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface TE {}"); 230 sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface TF {}"); 231 sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface TG {}"); 232 sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface TH {}"); 233 sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface TI {}"); 234 sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface TJ {}"); 235 sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface TK {}"); 236 sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface TL {}"); 237 sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface TM {}"); 238 239 // create RTA, RTAs, RTB, RTBs for repeating type annotations 240 sb.append("\n"); 241 sb.append("\n@Repeatable(RTAs.class) @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface RTA {}"); 242 sb.append("\n@Repeatable(RTBs.class) @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface RTB {}"); 243 244 sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface RTAs { RTA[] value(); }"); 245 sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface RTBs { RTB[] value(); }"); 246 247 sb.append("\n@Target(value={ElementType.TYPE,ElementType.FIELD,ElementType.METHOD,ElementType.PARAMETER,ElementType.CONSTRUCTOR,ElementType.LOCAL_VARIABLE})"); 248 sb.append("\n@interface Decl {}"); 249 250 return sb.toString(); 251 } 252 253 public static final int NOT_SET = -888; 254 255 } 256 257 @Retention(RetentionPolicy.RUNTIME) 258 @Target(ElementType.METHOD) 259 @interface TADescription { 260 String annotation(); 261 262 TargetType type(); 263 int offset() default Driver.NOT_SET; 264 int[] lvarOffset() default { }; 265 int[] lvarLength() default { }; 266 int[] lvarIndex() default { }; 267 int boundIndex() default Driver.NOT_SET; 268 int paramIndex() default Driver.NOT_SET; 269 int typeIndex() default Driver.NOT_SET; 270 int exceptionIndex() default Driver.NOT_SET; 271 272 int[] genericLocation() default {}; 273 } 274 275 @Retention(RetentionPolicy.RUNTIME) 276 @Target(ElementType.METHOD) 277 @interface TADescriptions { 278 TADescription[] value() default {}; 279 } 280 281 /** 282 * The name of the class that should be analyzed. 283 * Should only need to be provided when analyzing inner classes. 284 */ 285 @Retention(RetentionPolicy.RUNTIME) 286 @Target(ElementType.METHOD) 287 @interface TestClass { 288 String value() default "Test"; 289 }