1 /* 2 * Copyright (c) 2009, 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.util.ArrayList; 25 import java.util.Arrays; 26 import java.util.List; 27 import java.util.Map; 28 29 import com.sun.tools.classfile.Attribute; 30 import com.sun.tools.classfile.ClassFile; 31 import com.sun.tools.classfile.TypeAnnotation; 32 import com.sun.tools.classfile.Field; 33 import com.sun.tools.classfile.Method; 34 import com.sun.tools.classfile.RuntimeTypeAnnotations_attribute; 35 import com.sun.tools.classfile.ConstantPool.InvalidIndex; 36 import com.sun.tools.classfile.ConstantPool.UnexpectedEntry; 37 38 public class ReferenceInfoUtil { 39 40 public static final int IGNORE_VALUE = -321; 41 42 public static List<TypeAnnotation> extendedAnnotationsOf(ClassFile cf) { 43 List<TypeAnnotation> annos = new ArrayList<TypeAnnotation>(); 44 findAnnotations(cf, annos); 45 return annos; 46 } 47 48 /////////////////// Extract type annotations ////////////////// 49 private static void findAnnotations(ClassFile cf, List<TypeAnnotation> annos) { 50 findAnnotations(cf, Attribute.RuntimeVisibleTypeAnnotations, annos); 51 findAnnotations(cf, Attribute.RuntimeInvisibleTypeAnnotations, annos); 52 53 for (Field f : cf.fields) { 54 findAnnotations(cf, f, annos); 55 } 56 for (Method m: cf.methods) { 57 findAnnotations(cf, m, annos); 58 } 59 } 60 61 private static void findAnnotations(ClassFile cf, Method m, List<TypeAnnotation> annos) { 62 findAnnotations(cf, m, Attribute.RuntimeVisibleTypeAnnotations, annos); 63 findAnnotations(cf, m, Attribute.RuntimeInvisibleTypeAnnotations, annos); 64 } 65 66 private static void findAnnotations(ClassFile cf, Field m, List<TypeAnnotation> annos) { 67 findAnnotations(cf, m, Attribute.RuntimeVisibleTypeAnnotations, annos); 68 findAnnotations(cf, m, Attribute.RuntimeInvisibleTypeAnnotations, annos); 69 } 70 71 // test the result of Attributes.getIndex according to expectations 72 // encoded in the method's name 73 private static void findAnnotations(ClassFile cf, String name, List<TypeAnnotation> annos) { 74 int index = cf.attributes.getIndex(cf.constant_pool, name); 75 if (index != -1) { 76 Attribute attr = cf.attributes.get(index); 77 assert attr instanceof RuntimeTypeAnnotations_attribute; 78 RuntimeTypeAnnotations_attribute tAttr = (RuntimeTypeAnnotations_attribute)attr; 79 annos.addAll(Arrays.asList(tAttr.annotations)); 80 } 81 } 82 83 // test the result of Attributes.getIndex according to expectations 84 // encoded in the method's name 85 private static void findAnnotations(ClassFile cf, Method m, String name, List<TypeAnnotation> annos) { 86 int index = m.attributes.getIndex(cf.constant_pool, name); 87 if (index != -1) { 88 Attribute attr = m.attributes.get(index); 89 assert attr instanceof RuntimeTypeAnnotations_attribute; 90 RuntimeTypeAnnotations_attribute tAttr = (RuntimeTypeAnnotations_attribute)attr; 91 annos.addAll(Arrays.asList(tAttr.annotations)); 92 } 93 } 94 95 // test the result of Attributes.getIndex according to expectations 96 // encoded in the method's name 97 private static void findAnnotations(ClassFile cf, Field m, String name, List<TypeAnnotation> annos) { 98 int index = m.attributes.getIndex(cf.constant_pool, name); 99 if (index != -1) { 100 Attribute attr = m.attributes.get(index); 101 assert attr instanceof RuntimeTypeAnnotations_attribute; 102 RuntimeTypeAnnotations_attribute tAttr = (RuntimeTypeAnnotations_attribute)attr; 103 annos.addAll(Arrays.asList(tAttr.annotations)); 104 } 105 } 106 107 /////////////////// TA Position Builder /////////////////////// 108 /* TODO: comment out this dead code. Was this unfinished code that was 109 * supposed to be used somewhere? The tests pass without this. 110 private static class TAPositionBuilder { 111 private TypeAnnotation.Position pos = new TypeAnnotation.Position(); 112 113 private TAPositionBuilder() { } 114 115 public TypeAnnotation.Position build() { return pos; } 116 117 public static TAPositionBuilder ofType(TypeAnnotation.TargetType type) { 118 TAPositionBuilder builder = new TAPositionBuilder(); 119 builder.pos.type = type; 120 return builder; 121 } 122 123 public TAPositionBuilder atOffset(int offset) { 124 switch (pos.type) { 125 // type cast 126 case TYPECAST: 127 // instanceof 128 case INSTANCEOF: 129 // new expression 130 case NEW: 131 pos.offset = offset; 132 break; 133 default: 134 throw new IllegalArgumentException("invalid field for given type: " + pos.type); 135 } 136 return this; 137 } 138 139 public TAPositionBuilder atLocalPosition(int offset, int length, int index) { 140 switch (pos.type) { 141 // local variable 142 case LOCAL_VARIABLE: 143 pos.lvarOffset = new int[] { offset }; 144 pos.lvarLength = new int[] { length }; 145 pos.lvarIndex = new int[] { index }; 146 break; 147 default: 148 throw new IllegalArgumentException("invalid field for given type: " + pos.type); 149 } 150 return this; 151 } 152 153 public TAPositionBuilder atParameterIndex(int index) { 154 switch (pos.type) { 155 // type parameters 156 case CLASS_TYPE_PARAMETER: 157 case METHOD_TYPE_PARAMETER: 158 // method parameter 159 case METHOD_FORMAL_PARAMETER: 160 pos.parameter_index = index; 161 break; 162 default: 163 throw new IllegalArgumentException("invalid field for given type: " + pos.type); 164 } 165 return this; 166 } 167 168 public TAPositionBuilder atParamBound(int param, int bound) { 169 switch (pos.type) { 170 // type parameters bounds 171 case CLASS_TYPE_PARAMETER_BOUND: 172 case METHOD_TYPE_PARAMETER_BOUND: 173 pos.parameter_index = param; 174 pos.bound_index = bound; 175 break; 176 default: 177 throw new IllegalArgumentException("invalid field for given type: " + pos.type); 178 } 179 return this; 180 } 181 182 public TAPositionBuilder atWildcardPosition(TypeAnnotation.Position pos) { 183 switch (pos.type) { 184 // wildcards 185 case WILDCARD_BOUND: 186 pos.wildcard_position = pos; 187 break; 188 default: 189 throw new IllegalArgumentException("invalid field for given type: " + pos.type); 190 } 191 return this; 192 } 193 194 public TAPositionBuilder atTypeIndex(int index) { 195 switch (pos.type) { 196 // class extends or implements clauses 197 case CLASS_EXTENDS: 198 // throws 199 case THROWS: 200 pos.type_index = index; 201 break; 202 default: 203 throw new IllegalArgumentException("invalid field for given type: " + pos.type); 204 } 205 return this; 206 } 207 208 public TAPositionBuilder atOffsetWithIndex(int offset, int index) { 209 switch (pos.type) { 210 // method type argument: wasn't specified 211 case NEW_TYPE_ARGUMENT: 212 case METHOD_TYPE_ARGUMENT: 213 pos.offset = offset; 214 pos.type_index = index; 215 break; 216 default: 217 throw new IllegalArgumentException("invalid field for given type: " + pos.type); 218 } 219 return this; 220 } 221 222 public TAPositionBuilder atGenericLocation(Integer ...loc) { 223 pos.location = Arrays.asList(loc); 224 pos.type = pos.type.getGenericComplement(); 225 return this; 226 } 227 }*/ 228 229 /////////////////////// Equality testing ///////////////////// 230 private static boolean areEquals(int a, int b) { 231 return a == b || a == IGNORE_VALUE || b == IGNORE_VALUE; 232 } 233 234 private static boolean areEquals(int[] a, int[] a2) { 235 if (a==a2) 236 return true; 237 if (a==null || a2==null) 238 return false; 239 240 int length = a.length; 241 if (a2.length != length) 242 return false; 243 244 for (int i=0; i<length; i++) 245 if (a[i] != a2[i] && a[i] != IGNORE_VALUE && a2[i] != IGNORE_VALUE) 246 return false; 247 248 return true; 249 } 250 251 public static boolean areEquals(TypeAnnotation.Position p1, TypeAnnotation.Position p2) { 252 if (p1 == p2) 253 return true; 254 if (p1 == null || p2 == null) 255 return false; 256 257 return ((p1.type == p2.type) 258 && (p1.location.equals(p2.location)) 259 && areEquals(p1.offset, p2.offset) 260 && areEquals(p1.lvarOffset, p2.lvarOffset) 261 && areEquals(p1.lvarLength, p2.lvarLength) 262 && areEquals(p1.lvarIndex, p2.lvarIndex) 263 && areEquals(p1.bound_index, p2.bound_index) 264 && areEquals(p1.parameter_index, p2.parameter_index) 265 && areEquals(p1.type_index, p2.type_index) 266 && areEquals(p1.exception_index, p2.exception_index)); 267 } 268 269 private static TypeAnnotation findAnnotation(String name, List<TypeAnnotation> annotations, ClassFile cf) throws InvalidIndex, UnexpectedEntry { 270 String properName = "L" + name + ";"; 271 for (TypeAnnotation anno : annotations) { 272 String actualName = cf.constant_pool.getUTF8Value(anno.annotation.type_index); 273 if (properName.equals(actualName)) 274 return anno; 275 } 276 return null; 277 } 278 279 public static boolean compare(Map<String, TypeAnnotation.Position> expectedAnnos, 280 List<TypeAnnotation> actualAnnos, ClassFile cf) throws InvalidIndex, UnexpectedEntry { 281 if (actualAnnos.size() != expectedAnnos.size()) { 282 throw new ComparisionException("Wrong number of annotations", 283 expectedAnnos, 284 actualAnnos); 285 } 286 287 for (Map.Entry<String, TypeAnnotation.Position> e : expectedAnnos.entrySet()) { 288 String aName = e.getKey(); 289 TypeAnnotation.Position expected = e.getValue(); 290 TypeAnnotation actual = findAnnotation(aName, actualAnnos, cf); 291 if (actual == null) 292 throw new ComparisionException("Expected annotation not found: " + aName); 293 294 // TODO: you currently get an exception if the test case does not use all necessary 295 // annotation attributes, e.g. forgetting the offset for a local variable. 296 // It would be nicer to give an understandable warning instead. 297 if (!areEquals(expected, actual.position)) { 298 throw new ComparisionException("Unexpected position for annotation : " + aName + 299 "\n Expected: " + expected.toString() + 300 "\n Found: " + actual.position.toString()); 301 } 302 } 303 return true; 304 } 305 } 306 307 class ComparisionException extends RuntimeException { 308 private static final long serialVersionUID = -3930499712333815821L; 309 310 public final Map<String, TypeAnnotation.Position> expected; 311 public final List<TypeAnnotation> found; 312 313 public ComparisionException(String message) { 314 this(message, null, null); 315 } 316 317 public ComparisionException(String message, Map<String, TypeAnnotation.Position> expected, List<TypeAnnotation> found) { 318 super(message); 319 this.expected = expected; 320 this.found = found; 321 } 322 323 public String toString() { 324 String str = super.toString(); 325 if (expected != null && found != null) { 326 str += "\n\tExpected: " + expected.size() + " annotations; but found: " + found.size() + " annotations\n" + 327 " Expected: " + expected + 328 "\n Found: " + found; 329 } 330 return str; 331 } 332 }