1// This is a wrapper class around an TypeAnnotationVisitor that can be used 2// to verify the information it visits specifies a valid [extended] annotation. 3 4package annotations.io.classfile; 5 6/*>>> 7import org.checkerframework.checker.nullness.qual.*; 8*/ 9 10import java.util.ArrayList; 11import java.util.List; 12 13import org.objectweb.asm.AnnotationVisitor; 14import org.objectweb.asm.TypeAnnotationVisitor; 15 16import com.sun.tools.javac.code.TargetType; 17import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry; 18 19/** 20 * A <code>SafeTypeAnnotationVisitor</code> wraps around an 21 * TypeAnnotationVisitor and delegates all calls to it. However, it 22 * maintains a record of all methods that have been called and on 23 * calling {@link SafeTypeAnnotationVisitor#visitEnd}, 24 * performs a check to verify that all the data passed 25 * to this visitor specifies a legal annotation or extended annotation. Its 26 * intended use is to wrap around an <code>TypeAnnotationWriter</code> and 27 * thus ensure that no illegal class files are written, although it will work 28 * more generally on any visitor. 29 * 30 * <p> 31 * Also note that nothing special needs to be done about subannotations 32 * and regular arrays, since their lengths are not passed in to this visitor. 33 * 34 * <p> 35 * If none of the <code>visitX*</code> methods has been called, it is 36 * automatically a legal annotation. Else, if some of the <code>visitX*</code> 37 * methods have been called, the check will ensure that the data passed to this 38 * specifies a legal extended annotation, as defined by its target type. 39 */ 40public class SafeTypeAnnotationVisitor 41implements TypeAnnotationVisitor { 42 43 // The visitor this delegates all calls to. 44 private final TypeAnnotationVisitor xav; 45 46 // Each list keeps a record of what was passed in to the similarly-named 47 // method, and except for xLocationArgs, should all contain at most 1 element. 48 private final List<Integer> xIndexArgs; 49 private final List<Integer> xLengthArgs; 50 private final List<TypePathEntry> xLocationArgs; 51 private final List<Integer> xLocationLengthArgs; 52 private final List<Integer> xOffsetArgs; 53 private final List<Integer> xStartPcArgs; 54 private final List<Integer> xTargetTypeArgs; 55 private final List<Integer> xParamIndexArgs; 56 private final List<Integer> xBoundIndexArgs; 57 private final List<Integer> xTypeIndexArgs; 58 59 // Counts the number of times visitXNameAndArgsSize is called. 60 private int xNameAndArgsCount; 61 62 /** 63 * Constructs a new <code> SafeTypeAnnotationVisitor </code> that 64 * delegates all calls to the given visitor. 65 * 66 * @param xav the visitor to delegate all method calls to 67 */ 68 public SafeTypeAnnotationVisitor(TypeAnnotationVisitor xav) { 69 this.xav = xav; 70 // Start most of these with a capacity of one, since for legal annotations 71 // they should not contain more than one element. 72 xIndexArgs = new ArrayList<Integer>(1); 73 xLengthArgs = new ArrayList<Integer>(1); 74 xLocationArgs = new ArrayList<TypePathEntry>(); 75 xLocationLengthArgs = new ArrayList<Integer>(1); 76 xOffsetArgs = new ArrayList<Integer>(1); 77 xStartPcArgs = new ArrayList<Integer>(1); 78 xTargetTypeArgs = new ArrayList<Integer>(1); 79 xParamIndexArgs = new ArrayList<Integer>(1); 80 xBoundIndexArgs = new ArrayList<Integer>(1); 81 xTypeIndexArgs = new ArrayList<Integer>(1); 82 xNameAndArgsCount = 0; 83 } 84 85 /** 86 * {@inheritDoc} 87 * @see org.objectweb.asm.AnnotationVisitor#visit(java.lang.String, java.lang.Object) 88 */ 89 @Override 90 public void visit(String name, Object value) { 91 xav.visit(name, value); 92 } 93 94 /** 95 * {@inheritDoc} 96 * @see org.objectweb.asm.AnnotationVisitor#visitAnnotation(java.lang.String, java.lang.String) 97 */ 98 @Override 99 public AnnotationVisitor visitAnnotation(String name, String desc) { 100 return xav.visitAnnotation(name, desc); 101 } 102 103 /** 104 * {@inheritDoc} 105 * @see org.objectweb.asm.AnnotationVisitor#visitArray(java.lang.String) 106 */ 107 @Override 108 public AnnotationVisitor visitArray(String name) { 109 return xav.visitArray(name); 110 } 111 112 /** 113 * {@inheritDoc} 114 * @see org.objectweb.asm.AnnotationVisitor#visitEnum(java.lang.String, java.lang.String, java.lang.String) 115 */ 116 @Override 117 public void visitEnum(String name, String desc, String value) { 118 xav.visitEnum(name, desc, value); 119 } 120 121 /** 122 * {@inheritDoc} 123 * @see org.objectweb.asm.TypeAnnotationVisitor#visitXIndex(int) 124 */ 125 @Override 126 public void visitXIndex(int index) { 127 xIndexArgs.add(index); 128 xav.visitXIndex(index); 129 } 130 131 /** 132 * {@inheritDoc} 133 * @see org.objectweb.asm.TypeAnnotationVisitor#visitXLength(int) 134 */ 135 @Override 136 public void visitXLength(int length) { 137 xLengthArgs.add(length); 138 xav.visitXLength(length); 139 } 140 141 /** 142 * {@inheritDoc} 143 * @see org.objectweb.asm.TypeAnnotationVisitor#visitXLocation(TypePathEntry) 144 */ 145 @Override 146 public void visitXLocation(TypePathEntry location) { 147 xLocationArgs.add(location); 148 xav.visitXLocation(location); 149 } 150 151 /** 152 * {@inheritDoc} 153 * @see org.objectweb.asm.TypeAnnotationVisitor#visitXLocationLength(int) 154 */ 155 @Override 156 public void visitXLocationLength(int location_length) { 157 xLocationLengthArgs.add(location_length); 158 xav.visitXLocationLength(location_length); 159 } 160 161 /** 162 * {@inheritDoc} 163 * @see org.objectweb.asm.TypeAnnotationVisitor#visitXOffset(int) 164 */ 165 @Override 166 public void visitXOffset(int offset) { 167 xOffsetArgs.add(offset); 168 xav.visitXOffset(offset); 169 } 170 171 @Override 172 public void visitXNumEntries(int num_entries) { 173 } 174 175 /** 176 * {@inheritDoc} 177 * @see org.objectweb.asm.TypeAnnotationVisitor#visitXStartPc(int) 178 */ 179 @Override 180 public void visitXStartPc(int start_pc) { 181 xStartPcArgs.add(start_pc); 182 xav.visitXStartPc(start_pc); 183 } 184 185 /** 186 * {@inheritDoc} 187 * @see org.objectweb.asm.TypeAnnotationVisitor#visitXTargetType(int) 188 */ 189 @Override 190 public void visitXTargetType(int target_type) { 191 xTargetTypeArgs.add(target_type); 192 xav.visitXTargetType(target_type); 193 } 194 195 /** 196 * {@inheritDoc} 197 * @see org.objectweb.asm.TypeAnnotationVisitor#visitXParamIndex(int) 198 */ 199 @Override 200 public void visitXParamIndex(int param_index) { 201 xParamIndexArgs.add(param_index); 202 xav.visitXParamIndex(param_index); 203 } 204 205 /** 206 * {@inheritDoc} 207 * @see org.objectweb.asm.TypeAnnotationVisitor#visitXBoundIndex(int) 208 */ 209 @Override 210 public void visitXBoundIndex(int bound_index) { 211 if (bound_index != -1) { 212 xBoundIndexArgs.add(bound_index); 213 xav.visitXBoundIndex(bound_index); 214 } 215 } 216 217 @Override 218 public void visitXTypeIndex(int type_index) { 219 xTypeIndexArgs.add(type_index); 220 xav.visitXTypeIndex(type_index); 221 } 222 223 @Override 224 public void visitXExceptionIndex(int exception_index) { 225 // TODO 226 } 227 228 @Override 229 public void visitXNameAndArgsSize() { 230 xNameAndArgsCount++; 231 xav.visitXNameAndArgsSize(); 232 } 233 234 /** 235 * Visits the end of the annotation, and also performs a check 236 * to ensure that the information this has visited specifies a legal 237 * annotation. If the information does not specify a legal annotation, 238 * throws an exception. 239 * 240 * {@inheritDoc} 241 * @throws InvalidTypeAnnotationException if the information this 242 * has visited does not specify a legal extended annotation 243 * @see org.objectweb.asm.AnnotationVisitor#visitEnd() 244 */ 245 @Override 246 public void visitEnd() { 247 if (xTargetTypeArgs.size() > 0) { 248 checkX(); 249 } else { 250 // This has not visited an the target type of an extended annotation, so 251 // must ensure that all other extended information lists are empty. 252 if (xIndexArgs.size() != 0 || 253 xLengthArgs.size() != 0 || 254 xLocationArgs.size() != 0 || 255 xLocationLengthArgs.size() != 0 || 256 xOffsetArgs.size() != 0 || 257 xStartPcArgs.size() != 0) { 258 throw new InvalidTypeAnnotationException( 259 "No target type was specified, yet other visitX* methods were still called."); 260 } 261 } 262 xav.visitEnd(); 263 } 264 265 /** 266 * Checks that the extended information this has visited is valid. 267 * 268 * @throws InvalidTypeAnnotationException if extended information is 269 * not valid 270 */ 271 private void checkX() { 272 // First, check to see that only one target type was specified, and 273 // then dispatch to checkListSize() based on that target type. 274 if (xTargetTypeArgs.size() != 1) { 275 throw new 276 InvalidTypeAnnotationException("More than one target type visited."); 277 } 278 279 if (xNameAndArgsCount != 1) { 280 throw new InvalidTypeAnnotationException("Name and args count should " 281 + " be visited 1 time, actually visited " + xNameAndArgsCount 282 + " times."); 283 } 284 285 // Since the correct size of xLocationArgs is specified by 286 // xLocationLengthArgs, this information must be looked up first. 287 int c = 0; 288 if (xLocationLengthArgs.size() > 0) { 289 c = xLocationLengthArgs.get(0); 290 } 291 292 switch(TargetType.fromTargetTypeValue(xTargetTypeArgs.get(0))) { 293 case CAST: 294 checkListSize(0, 0, c, 1, 1, 0, 0, 0, 1, 295 "Invalid typecast annotation:"); 296 break; 297 case INSTANCEOF: 298 checkListSize(0, 0, c, 1, 1, 0, 0, 0, 0, 299 "Invalid type test annotation:"); 300 break; 301 case NEW: 302 checkListSize(0, 0, c, 1, 1, 0, 0, 0, 0, 303 "Invalid object creation annotation:"); 304 break; 305 case METHOD_RECEIVER: 306 checkListSize(0, 0, c, 1, 0, 0, 0, 0, 0, 307 "Invalid method receiver annotation:"); 308 break; 309 case LOCAL_VARIABLE: 310 checkListSize(1, 1, c, 1, 0, 1, 0, 0, 0, 311 "Invalid local variable annotation:"); 312 break; 313 case METHOD_RETURN: 314 checkListSize(0, 0, c, 1, 0, 0, 0, 0, 0, 315 "Invalid method return type annotation:"); 316 break; 317 case METHOD_FORMAL_PARAMETER: 318 checkListSize(0, 0, c, 1, 0, 0, 1, 0, 0, 319 "Invalid method parameter annotation:"); 320 break; 321 case FIELD: 322 checkListSize(0, 0, c, 1, 0, 0, 0, 0, 0, 323 "Invalid field annotation:"); 324 break; 325 case CLASS_TYPE_PARAMETER: 326 checkListSize(0, 0, c, 1, 0, 0, 1, 0, 0, 327 "Invalid class type parameter annotation:"); 328 break; 329 case CLASS_TYPE_PARAMETER_BOUND: 330 checkListSize(0, 0, c, 1, 0, 0, 1, 1, 0, 331 "Invalid class type parameter bound annotation:"); 332 break; 333 case METHOD_TYPE_PARAMETER: 334 checkListSize(0, 0, c, 1, 0, 0, 1, 0, 0, 335 "Invalid method type parameter annotation:"); 336 break; 337 case METHOD_TYPE_PARAMETER_BOUND: 338 checkListSize(0, 0, c, 1, 0, 0, 1, 1, 0, 339 "Invalid method type parameter bound annotation:"); 340 break; 341 case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT: 342 case METHOD_INVOCATION_TYPE_ARGUMENT: 343 case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: 344 case METHOD_REFERENCE_TYPE_ARGUMENT: 345 // TODO 346 break; 347 case CLASS_EXTENDS: 348 checkListSize(0, 0, c, 1, 0, 0, 0, 0, 1, 349 "Invalid class extends/implements annotation:"); 350 break; 351 case THROWS: 352 checkListSize(0, 0, c, 1, 0, 0, 0, 0, 1, 353 "Invalid exception type in throws annotation:"); 354 break; 355 default: 356 throw new InvalidTypeAnnotationException( 357 "Unknown target type given: " + xTargetTypeArgs.get(0)); 358 } 359 } 360 361 /** 362 * If list.size() != correctLength, appends a descriptive error message to sb 363 * specifying how many times methodName was supposed to be called and how 364 * many times it was actually called. 365 * Else, has no effect. 366 * 367 * @param list the list of arguments actually visited 368 * @param correctLength the correct length of list 369 * @param methodName the name of the method whose arguments went into list 370 * @param sb the StringBuilder to append error messages to 371 */ 372 private void appendMessage(List<?> list, int idealLength, 373 String methodName, StringBuilder sb) { 374 if (list.size() != idealLength) { 375 sb.append("\nInvalid method calls: "); 376 sb.append(methodName); 377 sb.append(" was called "); 378 sb.append(list.size()); 379 sb.append(" times, but should have only been called "); 380 sb.append(idealLength); 381 sb.append(" times"); 382 } 383 } 384 385 /** 386 * Checks that the seven lists containing extended annotation information are 387 * of the specified sizes. If at least one of the lists is not of the 388 * correct length, this throws an exception with a message equal to msg, plus 389 * information describing which lists were incorrect. 390 * 391 * @throws InvalidTypeAnnotationException if the extended information 392 * lists are not of the correct length 393 */ 394 private void checkListSize( 395 int correctLengthIndex, 396 int correctLengthLength, 397 int correctLengthLocation, 398 int correctLengthLocationLength, 399 int correctLengthOffset, 400 int correctLengthStartPc, 401 int correctLengthParamIndex, 402 int correctLengthBoundIndex, 403 int correctLengthTypeIndex, 404 String msg) { 405 StringBuilder sb = new StringBuilder(); 406 appendMessage(xIndexArgs, correctLengthIndex, "visitXIndex", sb); 407 appendMessage(xLengthArgs, correctLengthLength, "visitXLength", sb); 408 appendMessage(xLocationArgs, correctLengthLocation, "visitXLocation", sb); 409 appendMessage(xLocationLengthArgs, correctLengthLocationLength, 410 "visitXLocationLength", sb); 411 appendMessage(xOffsetArgs, correctLengthOffset, "visitXOffset", sb); 412 appendMessage(xStartPcArgs, correctLengthStartPc, "visitXStartPc", sb); 413 appendMessage(xParamIndexArgs, correctLengthParamIndex, "visitXParamIndex", sb); 414 appendMessage(xBoundIndexArgs, correctLengthBoundIndex, "visitXBoundIndex", sb); 415 appendMessage(xTypeIndexArgs, correctLengthTypeIndex, "VisitXTypeIndex", sb); 416 417 // At this point, sb will contain Strings iff there is an error 418 // in the extended annotation information. 419 String s = sb.toString(); 420 if (s.length() > 0) { 421 throw new InvalidTypeAnnotationException(msg + s); 422 } 423 } 424} 425