aoqi@0: /* aoqi@0: * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: import java.io.BufferedWriter; aoqi@0: import java.io.File; aoqi@0: import java.io.FileWriter; aoqi@0: import java.io.IOException; aoqi@0: import java.io.PrintStream; aoqi@0: import java.io.PrintWriter; aoqi@0: import java.lang.annotation.*; aoqi@0: import java.lang.reflect.*; aoqi@0: import java.util.ArrayList; aoqi@0: import java.util.Arrays; aoqi@0: import java.util.Collections; aoqi@0: import java.util.HashMap; aoqi@0: import java.util.List; aoqi@0: import java.util.Map; aoqi@0: aoqi@0: import com.sun.tools.classfile.ClassFile; aoqi@0: import com.sun.tools.classfile.TypeAnnotation; aoqi@0: import com.sun.tools.classfile.TypeAnnotation.TargetType; aoqi@0: aoqi@0: public class Driver { aoqi@0: aoqi@0: private static final PrintStream out = System.out; aoqi@0: aoqi@0: public static void main(String[] args) throws Exception { aoqi@0: if (args.length == 0 || args.length > 1) aoqi@0: throw new IllegalArgumentException("Usage: java Driver "); aoqi@0: String name = args[0]; aoqi@0: Class clazz = Class.forName(name); aoqi@0: new Driver().runDriver(clazz.newInstance()); aoqi@0: } aoqi@0: aoqi@0: String[][] extraParamsCombinations = new String[][] { aoqi@0: new String[] { }, aoqi@0: new String[] { "-g" }, aoqi@0: }; aoqi@0: aoqi@0: protected void runDriver(Object object) throws Exception { aoqi@0: int passed = 0, failed = 0; aoqi@0: Class clazz = object.getClass(); aoqi@0: out.println("Tests for " + clazz.getName()); aoqi@0: aoqi@0: // Find methods aoqi@0: for (Method method : clazz.getMethods()) { aoqi@0: Map expected = expectedOf(method); aoqi@0: if (expected == null) aoqi@0: continue; aoqi@0: if (method.getReturnType() != String.class) aoqi@0: throw new IllegalArgumentException("Test method needs to return a string: " + method); aoqi@0: String testClass = testClassOf(method); aoqi@0: aoqi@0: for (String[] extraParams : extraParamsCombinations) { aoqi@0: try { aoqi@0: String compact = (String)method.invoke(object); aoqi@0: String fullFile = wrap(compact); aoqi@0: ClassFile cf = compileAndReturn(fullFile, testClass, extraParams); aoqi@0: List actual = ReferenceInfoUtil.extendedAnnotationsOf(cf); aoqi@0: ReferenceInfoUtil.compare(expected, actual, cf); aoqi@0: out.println("PASSED: " + method.getName()); aoqi@0: ++passed; aoqi@0: } catch (Throwable e) { aoqi@0: out.println("FAILED: " + method.getName()); aoqi@0: out.println(" " + e.toString()); aoqi@0: ++failed; aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: out.println(); aoqi@0: int total = passed + failed; aoqi@0: out.println(total + " total tests: " + passed + " PASSED, " + failed + " FAILED"); aoqi@0: aoqi@0: out.flush(); aoqi@0: aoqi@0: if (failed != 0) aoqi@0: throw new RuntimeException(failed + " tests failed"); aoqi@0: } aoqi@0: aoqi@0: private Map expectedOf(Method m) { aoqi@0: TADescription ta = m.getAnnotation(TADescription.class); aoqi@0: TADescriptions tas = m.getAnnotation(TADescriptions.class); aoqi@0: aoqi@0: if (ta == null && tas == null) aoqi@0: return null; aoqi@0: aoqi@0: Map result = aoqi@0: new HashMap(); aoqi@0: aoqi@0: if (ta != null) aoqi@0: result.putAll(expectedOf(ta)); aoqi@0: aoqi@0: if (tas != null) { aoqi@0: for (TADescription a : tas.value()) { aoqi@0: result.putAll(expectedOf(a)); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: return result; aoqi@0: } aoqi@0: aoqi@0: private Map expectedOf(TADescription d) { aoqi@0: String annoName = d.annotation(); aoqi@0: aoqi@0: TypeAnnotation.Position p = new TypeAnnotation.Position(); aoqi@0: p.type = d.type(); aoqi@0: if (d.offset() != NOT_SET) aoqi@0: p.offset = d.offset(); aoqi@0: if (d.lvarOffset().length != 0) aoqi@0: p.lvarOffset = d.lvarOffset(); aoqi@0: if (d.lvarLength().length != 0) aoqi@0: p.lvarLength = d.lvarLength(); aoqi@0: if (d.lvarIndex().length != 0) aoqi@0: p.lvarIndex = d.lvarIndex(); aoqi@0: if (d.boundIndex() != NOT_SET) aoqi@0: p.bound_index = d.boundIndex(); aoqi@0: if (d.paramIndex() != NOT_SET) aoqi@0: p.parameter_index = d.paramIndex(); aoqi@0: if (d.typeIndex() != NOT_SET) aoqi@0: p.type_index = d.typeIndex(); aoqi@0: if (d.exceptionIndex() != NOT_SET) aoqi@0: p.exception_index = d.exceptionIndex(); aoqi@0: if (d.genericLocation().length != 0) { aoqi@0: p.location = TypeAnnotation.Position.getTypePathFromBinary(wrapIntArray(d.genericLocation())); aoqi@0: } aoqi@0: aoqi@0: return Collections.singletonMap(annoName, p); aoqi@0: } aoqi@0: aoqi@0: private List wrapIntArray(int[] ints) { aoqi@0: List list = new ArrayList(ints.length); aoqi@0: for (int i : ints) aoqi@0: list.add(i); aoqi@0: return list; aoqi@0: } aoqi@0: aoqi@0: private String testClassOf(Method m) { aoqi@0: TestClass tc = m.getAnnotation(TestClass.class); aoqi@0: if (tc != null) { aoqi@0: return tc.value(); aoqi@0: } else { aoqi@0: return "Test"; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: private ClassFile compileAndReturn(String fullFile, String testClass, String... extraParams) throws Exception { aoqi@0: File source = writeTestFile(fullFile); aoqi@0: File clazzFile = compileTestFile(source, testClass); aoqi@0: return ClassFile.read(clazzFile); aoqi@0: } aoqi@0: aoqi@0: protected File writeTestFile(String fullFile) throws IOException { aoqi@0: File f = new File("Test.java"); aoqi@0: PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(f))); aoqi@0: out.println(fullFile); aoqi@0: out.close(); aoqi@0: return f; aoqi@0: } aoqi@0: aoqi@0: protected File compileTestFile(File f, String testClass, String... extraParams) { aoqi@0: List options = new ArrayList<>(); aoqi@0: options.addAll(Arrays.asList("-source", "1.8")); aoqi@0: options.addAll(Arrays.asList(extraParams)); aoqi@0: options.add(f.getPath()); aoqi@0: int rc = com.sun.tools.javac.Main.compile(options.toArray(new String[options.size()])); aoqi@0: if (rc != 0) aoqi@0: throw new Error("compilation failed. rc=" + rc); aoqi@0: String path; aoqi@0: if (f.getParent() != null) { aoqi@0: path = f.getParent(); aoqi@0: } else { aoqi@0: path = ""; aoqi@0: } aoqi@0: aoqi@0: return new File(path + testClass + ".class"); aoqi@0: } aoqi@0: aoqi@0: private String wrap(String compact) { aoqi@0: StringBuilder sb = new StringBuilder(); aoqi@0: aoqi@0: // Automatically import java.util aoqi@0: sb.append("\nimport java.util.*;"); aoqi@0: sb.append("\nimport java.lang.annotation.*;"); aoqi@0: aoqi@0: sb.append("\n\n"); aoqi@0: boolean isSnippet = !(compact.startsWith("class") aoqi@0: || compact.contains(" class")) aoqi@0: && !compact.contains("interface") aoqi@0: && !compact.contains("enum"); aoqi@0: if (isSnippet) aoqi@0: sb.append("class Test {\n"); aoqi@0: aoqi@0: sb.append(compact); aoqi@0: sb.append("\n"); aoqi@0: aoqi@0: if (isSnippet) aoqi@0: sb.append("}\n\n"); aoqi@0: aoqi@0: if (isSnippet) { aoqi@0: // Have a few common nested types for testing aoqi@0: sb.append("class Outer { class Inner {} class Middle { class MInner {} } }"); aoqi@0: sb.append("class SOuter { static class SInner {} }"); aoqi@0: sb.append("class GOuter { class GInner {} }"); aoqi@0: } aoqi@0: aoqi@0: // create A ... F annotation declarations aoqi@0: sb.append("\n@interface A {}"); aoqi@0: sb.append("\n@interface B {}"); aoqi@0: sb.append("\n@interface C {}"); aoqi@0: sb.append("\n@interface D {}"); aoqi@0: sb.append("\n@interface E {}"); aoqi@0: sb.append("\n@interface F {}"); aoqi@0: aoqi@0: // create TA ... TF proper type annotations aoqi@0: sb.append("\n"); aoqi@0: sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface TA {}"); aoqi@0: sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface TB {}"); aoqi@0: sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface TC {}"); aoqi@0: sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface TD {}"); aoqi@0: sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface TE {}"); aoqi@0: sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface TF {}"); aoqi@0: sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface TG {}"); aoqi@0: sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface TH {}"); aoqi@0: sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface TI {}"); aoqi@0: sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface TJ {}"); aoqi@0: sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface TK {}"); aoqi@0: sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface TL {}"); aoqi@0: sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface TM {}"); aoqi@0: aoqi@0: // create RTA, RTAs, RTB, RTBs for repeating type annotations aoqi@0: sb.append("\n"); aoqi@0: sb.append("\n@Repeatable(RTAs.class) @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface RTA {}"); aoqi@0: sb.append("\n@Repeatable(RTBs.class) @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface RTB {}"); aoqi@0: aoqi@0: sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface RTAs { RTA[] value(); }"); aoqi@0: sb.append("\n@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @interface RTBs { RTB[] value(); }"); aoqi@0: aoqi@0: sb.append("\n@Target(value={ElementType.TYPE,ElementType.FIELD,ElementType.METHOD,ElementType.PARAMETER,ElementType.CONSTRUCTOR,ElementType.LOCAL_VARIABLE})"); aoqi@0: sb.append("\n@interface Decl {}"); aoqi@0: aoqi@0: return sb.toString(); aoqi@0: } aoqi@0: aoqi@0: public static final int NOT_SET = -888; aoqi@0: aoqi@0: } aoqi@0: aoqi@0: @Retention(RetentionPolicy.RUNTIME) aoqi@0: @Target(ElementType.METHOD) aoqi@0: @interface TADescription { aoqi@0: String annotation(); aoqi@0: aoqi@0: TargetType type(); aoqi@0: int offset() default Driver.NOT_SET; aoqi@0: int[] lvarOffset() default { }; aoqi@0: int[] lvarLength() default { }; aoqi@0: int[] lvarIndex() default { }; aoqi@0: int boundIndex() default Driver.NOT_SET; aoqi@0: int paramIndex() default Driver.NOT_SET; aoqi@0: int typeIndex() default Driver.NOT_SET; aoqi@0: int exceptionIndex() default Driver.NOT_SET; aoqi@0: aoqi@0: int[] genericLocation() default {}; aoqi@0: } aoqi@0: aoqi@0: @Retention(RetentionPolicy.RUNTIME) aoqi@0: @Target(ElementType.METHOD) aoqi@0: @interface TADescriptions { aoqi@0: TADescription[] value() default {}; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * The name of the class that should be analyzed. aoqi@0: * Should only need to be provided when analyzing inner classes. aoqi@0: */ aoqi@0: @Retention(RetentionPolicy.RUNTIME) aoqi@0: @Target(ElementType.METHOD) aoqi@0: @interface TestClass { aoqi@0: String value() default "Test"; aoqi@0: }