jjg@1521: /* jjg@1755: * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. jjg@1521: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. jjg@1521: * jjg@1521: * This code is free software; you can redistribute it and/or modify it jjg@1521: * under the terms of the GNU General Public License version 2 only, as jjg@1521: * published by the Free Software Foundation. jjg@1521: * jjg@1521: * This code is distributed in the hope that it will be useful, but WITHOUT jjg@1521: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or jjg@1521: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License jjg@1521: * version 2 for more details (a copy is included in the LICENSE file that jjg@1521: * accompanied this code). jjg@1521: * jjg@1521: * You should have received a copy of the GNU General Public License version jjg@1521: * 2 along with this work; if not, write to the Free Software Foundation, jjg@1521: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. jjg@1521: * jjg@1521: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA jjg@1521: * or visit www.oracle.com if you need additional information or have any jjg@1521: * questions. jjg@1521: */ jjg@1521: jjg@1521: import java.util.ArrayList; jjg@1521: import java.util.Arrays; jjg@1521: import java.util.List; jjg@1521: import java.util.Map; jjg@1521: jjg@1521: import com.sun.tools.classfile.Attribute; jjg@1521: import com.sun.tools.classfile.ClassFile; jjg@1755: import com.sun.tools.classfile.Code_attribute; jjg@1521: import com.sun.tools.classfile.TypeAnnotation; jjg@1521: import com.sun.tools.classfile.Field; jjg@1521: import com.sun.tools.classfile.Method; jjg@1521: import com.sun.tools.classfile.RuntimeTypeAnnotations_attribute; jjg@1521: import com.sun.tools.classfile.ConstantPool.InvalidIndex; jjg@1521: import com.sun.tools.classfile.ConstantPool.UnexpectedEntry; jjg@1521: jjg@1521: public class ReferenceInfoUtil { jjg@1521: jjg@1521: public static final int IGNORE_VALUE = -321; jjg@1521: jjg@1521: public static List extendedAnnotationsOf(ClassFile cf) { jjg@1521: List annos = new ArrayList(); jjg@1521: findAnnotations(cf, annos); jjg@1521: return annos; jjg@1521: } jjg@1521: jjg@1521: /////////////////// Extract type annotations ////////////////// jjg@1521: private static void findAnnotations(ClassFile cf, List annos) { jjg@1521: findAnnotations(cf, Attribute.RuntimeVisibleTypeAnnotations, annos); jjg@1521: findAnnotations(cf, Attribute.RuntimeInvisibleTypeAnnotations, annos); jjg@1521: jjg@1521: for (Field f : cf.fields) { jjg@1521: findAnnotations(cf, f, annos); jjg@1521: } jjg@1521: for (Method m: cf.methods) { jjg@1521: findAnnotations(cf, m, annos); jjg@1521: } jjg@1521: } jjg@1521: jjg@1521: private static void findAnnotations(ClassFile cf, Method m, List annos) { jjg@1521: findAnnotations(cf, m, Attribute.RuntimeVisibleTypeAnnotations, annos); jjg@1521: findAnnotations(cf, m, Attribute.RuntimeInvisibleTypeAnnotations, annos); jjg@1521: } jjg@1521: jjg@1521: private static void findAnnotations(ClassFile cf, Field m, List annos) { jjg@1521: findAnnotations(cf, m, Attribute.RuntimeVisibleTypeAnnotations, annos); jjg@1521: findAnnotations(cf, m, Attribute.RuntimeInvisibleTypeAnnotations, annos); jjg@1521: } jjg@1521: jjg@1521: // test the result of Attributes.getIndex according to expectations jjg@1521: // encoded in the method's name jjg@1521: private static void findAnnotations(ClassFile cf, String name, List annos) { jjg@1521: int index = cf.attributes.getIndex(cf.constant_pool, name); jjg@1521: if (index != -1) { jjg@1521: Attribute attr = cf.attributes.get(index); jjg@1521: assert attr instanceof RuntimeTypeAnnotations_attribute; jjg@1521: RuntimeTypeAnnotations_attribute tAttr = (RuntimeTypeAnnotations_attribute)attr; jjg@1521: annos.addAll(Arrays.asList(tAttr.annotations)); jjg@1521: } jjg@1521: } jjg@1521: jjg@1521: // test the result of Attributes.getIndex according to expectations jjg@1521: // encoded in the method's name jjg@1521: private static void findAnnotations(ClassFile cf, Method m, String name, List annos) { jjg@1521: int index = m.attributes.getIndex(cf.constant_pool, name); jjg@1521: if (index != -1) { jjg@1521: Attribute attr = m.attributes.get(index); jjg@1521: assert attr instanceof RuntimeTypeAnnotations_attribute; jjg@1521: RuntimeTypeAnnotations_attribute tAttr = (RuntimeTypeAnnotations_attribute)attr; jjg@1521: annos.addAll(Arrays.asList(tAttr.annotations)); jjg@1521: } jjg@1755: jjg@1755: int cindex = m.attributes.getIndex(cf.constant_pool, Attribute.Code); jjg@1755: if (cindex != -1) { jjg@1755: Attribute cattr = m.attributes.get(cindex); jjg@1755: assert cattr instanceof Code_attribute; jjg@1755: Code_attribute cAttr = (Code_attribute)cattr; jjg@1755: index = cAttr.attributes.getIndex(cf.constant_pool, name); jjg@1755: if (index != -1) { jjg@1755: Attribute attr = cAttr.attributes.get(index); jjg@1755: assert attr instanceof RuntimeTypeAnnotations_attribute; jjg@1755: RuntimeTypeAnnotations_attribute tAttr = (RuntimeTypeAnnotations_attribute)attr; jjg@1755: annos.addAll(Arrays.asList(tAttr.annotations)); jjg@1755: } jjg@1755: } jjg@1521: } jjg@1521: jjg@1521: // test the result of Attributes.getIndex according to expectations jjg@1521: // encoded in the method's name jjg@1521: private static void findAnnotations(ClassFile cf, Field m, String name, List annos) { jjg@1521: int index = m.attributes.getIndex(cf.constant_pool, name); jjg@1521: if (index != -1) { jjg@1521: Attribute attr = m.attributes.get(index); jjg@1521: assert attr instanceof RuntimeTypeAnnotations_attribute; jjg@1521: RuntimeTypeAnnotations_attribute tAttr = (RuntimeTypeAnnotations_attribute)attr; jjg@1521: annos.addAll(Arrays.asList(tAttr.annotations)); jjg@1521: } jjg@1521: } jjg@1521: jjg@1521: /////////////////// TA Position Builder /////////////////////// jjg@1521: /* TODO: comment out this dead code. Was this unfinished code that was jjg@1521: * supposed to be used somewhere? The tests pass without this. jjg@1521: private static class TAPositionBuilder { jjg@1521: private TypeAnnotation.Position pos = new TypeAnnotation.Position(); jjg@1521: jjg@1521: private TAPositionBuilder() { } jjg@1521: jjg@1521: public TypeAnnotation.Position build() { return pos; } jjg@1521: jjg@1521: public static TAPositionBuilder ofType(TypeAnnotation.TargetType type) { jjg@1521: TAPositionBuilder builder = new TAPositionBuilder(); jjg@1521: builder.pos.type = type; jjg@1521: return builder; jjg@1521: } jjg@1521: jjg@1521: public TAPositionBuilder atOffset(int offset) { jjg@1521: switch (pos.type) { jjg@1521: // type cast jjg@1521: case TYPECAST: jjg@1521: // instanceof jjg@1521: case INSTANCEOF: jjg@1521: // new expression jjg@1521: case NEW: jjg@1521: pos.offset = offset; jjg@1521: break; jjg@1521: default: jjg@1521: throw new IllegalArgumentException("invalid field for given type: " + pos.type); jjg@1521: } jjg@1521: return this; jjg@1521: } jjg@1521: jjg@1521: public TAPositionBuilder atLocalPosition(int offset, int length, int index) { jjg@1521: switch (pos.type) { jjg@1521: // local variable jjg@1521: case LOCAL_VARIABLE: jjg@1521: pos.lvarOffset = new int[] { offset }; jjg@1521: pos.lvarLength = new int[] { length }; jjg@1521: pos.lvarIndex = new int[] { index }; jjg@1521: break; jjg@1521: default: jjg@1521: throw new IllegalArgumentException("invalid field for given type: " + pos.type); jjg@1521: } jjg@1521: return this; jjg@1521: } jjg@1521: jjg@1521: public TAPositionBuilder atParameterIndex(int index) { jjg@1521: switch (pos.type) { jjg@1521: // type parameters jjg@1521: case CLASS_TYPE_PARAMETER: jjg@1521: case METHOD_TYPE_PARAMETER: jjg@1521: // method parameter jjg@1521: case METHOD_FORMAL_PARAMETER: jjg@1521: pos.parameter_index = index; jjg@1521: break; jjg@1521: default: jjg@1521: throw new IllegalArgumentException("invalid field for given type: " + pos.type); jjg@1521: } jjg@1521: return this; jjg@1521: } jjg@1521: jjg@1521: public TAPositionBuilder atParamBound(int param, int bound) { jjg@1521: switch (pos.type) { jjg@1521: // type parameters bounds jjg@1521: case CLASS_TYPE_PARAMETER_BOUND: jjg@1521: case METHOD_TYPE_PARAMETER_BOUND: jjg@1521: pos.parameter_index = param; jjg@1521: pos.bound_index = bound; jjg@1521: break; jjg@1521: default: jjg@1521: throw new IllegalArgumentException("invalid field for given type: " + pos.type); jjg@1521: } jjg@1521: return this; jjg@1521: } jjg@1521: jjg@1521: public TAPositionBuilder atWildcardPosition(TypeAnnotation.Position pos) { jjg@1521: switch (pos.type) { jjg@1521: // wildcards jjg@1521: case WILDCARD_BOUND: jjg@1521: pos.wildcard_position = pos; jjg@1521: break; jjg@1521: default: jjg@1521: throw new IllegalArgumentException("invalid field for given type: " + pos.type); jjg@1521: } jjg@1521: return this; jjg@1521: } jjg@1521: jjg@1521: public TAPositionBuilder atTypeIndex(int index) { jjg@1521: switch (pos.type) { jjg@1521: // class extends or implements clauses jjg@1521: case CLASS_EXTENDS: jjg@1521: // throws jjg@1521: case THROWS: jjg@1521: pos.type_index = index; jjg@1521: break; jjg@1521: default: jjg@1521: throw new IllegalArgumentException("invalid field for given type: " + pos.type); jjg@1521: } jjg@1521: return this; jjg@1521: } jjg@1521: jjg@1521: public TAPositionBuilder atOffsetWithIndex(int offset, int index) { jjg@1521: switch (pos.type) { jjg@1521: // method type argument: wasn't specified jjg@1521: case NEW_TYPE_ARGUMENT: jjg@1521: case METHOD_TYPE_ARGUMENT: jjg@1521: pos.offset = offset; jjg@1521: pos.type_index = index; jjg@1521: break; jjg@1521: default: jjg@1521: throw new IllegalArgumentException("invalid field for given type: " + pos.type); jjg@1521: } jjg@1521: return this; jjg@1521: } jjg@1521: jjg@1521: public TAPositionBuilder atGenericLocation(Integer ...loc) { jjg@1521: pos.location = Arrays.asList(loc); jjg@1521: pos.type = pos.type.getGenericComplement(); jjg@1521: return this; jjg@1521: } jjg@1521: }*/ jjg@1521: jjg@1521: /////////////////////// Equality testing ///////////////////// jjg@1521: private static boolean areEquals(int a, int b) { jjg@1521: return a == b || a == IGNORE_VALUE || b == IGNORE_VALUE; jjg@1521: } jjg@1521: jjg@1521: private static boolean areEquals(int[] a, int[] a2) { jjg@1521: if (a==a2) jjg@1521: return true; jjg@1521: if (a==null || a2==null) jjg@1521: return false; jjg@1521: jjg@1521: int length = a.length; jjg@1521: if (a2.length != length) jjg@1521: return false; jjg@1521: jjg@1521: for (int i=0; i annotations, ClassFile cf) throws InvalidIndex, UnexpectedEntry { jjg@1521: String properName = "L" + name + ";"; jjg@1521: for (TypeAnnotation anno : annotations) { jjg@1521: String actualName = cf.constant_pool.getUTF8Value(anno.annotation.type_index); jjg@1521: if (properName.equals(actualName)) jjg@1521: return anno; jjg@1521: } jjg@1521: return null; jjg@1521: } jjg@1521: jjg@1521: public static boolean compare(Map expectedAnnos, jjg@1521: List actualAnnos, ClassFile cf) throws InvalidIndex, UnexpectedEntry { jjg@1521: if (actualAnnos.size() != expectedAnnos.size()) { jjg@1521: throw new ComparisionException("Wrong number of annotations", jjg@1521: expectedAnnos, jjg@1521: actualAnnos); jjg@1521: } jjg@1521: jjg@1521: for (Map.Entry e : expectedAnnos.entrySet()) { jjg@1521: String aName = e.getKey(); jjg@1521: TypeAnnotation.Position expected = e.getValue(); jjg@1521: TypeAnnotation actual = findAnnotation(aName, actualAnnos, cf); jjg@1521: if (actual == null) jjg@1521: throw new ComparisionException("Expected annotation not found: " + aName); jjg@1521: jjg@1521: // TODO: you currently get an exception if the test case does not use all necessary jjg@1521: // annotation attributes, e.g. forgetting the offset for a local variable. jjg@1521: // It would be nicer to give an understandable warning instead. jjg@1521: if (!areEquals(expected, actual.position)) { jjg@1521: throw new ComparisionException("Unexpected position for annotation : " + aName + jjg@1521: "\n Expected: " + expected.toString() + jjg@1521: "\n Found: " + actual.position.toString()); jjg@1521: } jjg@1521: } jjg@1521: return true; jjg@1521: } jjg@1521: } jjg@1521: jjg@1521: class ComparisionException extends RuntimeException { jjg@1521: private static final long serialVersionUID = -3930499712333815821L; jjg@1521: jjg@1521: public final Map expected; jjg@1521: public final List found; jjg@1521: jjg@1521: public ComparisionException(String message) { jjg@1521: this(message, null, null); jjg@1521: } jjg@1521: jjg@1521: public ComparisionException(String message, Map expected, List found) { jjg@1521: super(message); jjg@1521: this.expected = expected; jjg@1521: this.found = found; jjg@1521: } jjg@1521: jjg@1521: public String toString() { jjg@1521: String str = super.toString(); jjg@1521: if (expected != null && found != null) { jjg@1521: str += "\n\tExpected: " + expected.size() + " annotations; but found: " + found.size() + " annotations\n" + jjg@1521: " Expected: " + expected + jjg@1521: "\n Found: " + found; jjg@1521: } jjg@1521: return str; jjg@1521: } jjg@1521: }