1 /* 2 * Javassist, a Java-bytecode translator toolkit. 3 * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. 4 * 5 * The contents of this file are subject to the Mozilla Public License Version 6 * 1.1 (the "License"); you may not use this file except in compliance with 7 * the License. Alternatively, the contents of this file may be used under 8 * the terms of the GNU Lesser General Public License Version 2.1 or later. 9 * 10 * Software distributed under the License is distributed on an "AS IS" basis, 11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 12 * for the specific language governing rights and limitations under the 13 * License. 14 */ 15 16 package javassist.bytecode; 17 18 import java.util.Map; 19 import java.util.HashMap; 20 import java.io.IOException; 21 import java.io.DataInputStream; 22 import java.io.ByteArrayOutputStream; 23 import javassist.bytecode.annotation.*; 24 25 /** 26 * A class representing 27 * <code>RuntimeVisibleAnnotations_attribute</code> and 28 * <code>RuntimeInvisibleAnnotations_attribute</code>. 29 * 30 * <p>To obtain an AnnotationAttribute object, invoke 31 * <code>getAttribute(AnnotationsAttribute.visibleTag)</code> 32 * in <code>ClassFile</code>, <code>MethodInfo</code>, 33 * or <code>FieldInfo</code>. The obtained attribute is a 34 * runtime visible annotations attribute. 35 * If the parameter is 36 * <code>AnnotationAttribute.invisibleTag</code>, then the obtained 37 * attribute is a runtime invisible one. 38 * 39 * <p>For example, 40 * 41 * <ul><pre> 42 * import javassist.bytecode.annotation.Annotation; 43 * : 44 * CtMethod m = ... ; 45 * MethodInfo minfo = m.getMethodInfo(); 46 * AnnotationsAttribute attr = (AnnotationsAttribute) 47 * minfo.getAttribute(AnnotationsAttribute.invisibleTag); 48 * Annotation an = attr.getAnnotation("Author"); 49 * String s = ((StringMemberValue)an.getMemberValue("name")).getValue(); 50 * System.out.println("@Author(name=" + s + ")"); 51 * </pre></ul> 52 * 53 * <p>This code snippet retrieves an annotation of the type <code>Author</code> 54 * from the <code>MethodInfo</code> object specified by <code>minfo</code>. 55 * Then, it prints the value of <code>name</code> in <code>Author</code>. 56 * 57 * <p>If the annotation type <code>Author</code> is annotated by a meta annotation: 58 * 59 * <ul><pre> 60 * @Retention(RetentionPolicy.RUNTIME) 61 * </pre></ul> 62 * 63 * <p>Then <code>Author</code> is visible at runtime. Therefore, the third 64 * statement of the code snippet above must be changed into: 65 * 66 * <ul><pre> 67 * AnnotationsAttribute attr = (AnnotationsAttribute) 68 * minfo.getAttribute(AnnotationsAttribute.visibleTag); 69 * </pre></ul> 70 * 71 * <p>The attribute tag must be <code>visibleTag</code> instead of 72 * <code>invisibleTag</code>. 73 * 74 * <p>If the member value of an annotation is not specified, the default value 75 * is used as that member value. If so, <code>getMemberValue()</code> in 76 * <code>Annotation</code> returns <code>null</code> 77 * since the default value is not included in the 78 * <code>AnnotationsAttribute</code>. It is included in the 79 * <code>AnnotationDefaultAttribute</code> of the method declared in the 80 * annotation type. 81 * 82 * <p>If you want to record a new AnnotationAttribute object, execute the 83 * following snippet: 84 * 85 * <ul><pre> 86 * ClassFile cf = ... ; 87 * ConstPool cp = cf.getConstPool(); 88 * AnnotationsAttribute attr 89 * = new AnnotationsAttribute(cp, AnnotationsAttribute.visibleTag); 90 * Annotation a = new Annotation("Author", cp); 91 * a.addMemberValue("name", new StringMemberValue("Chiba", cp)); 92 * attr.setAnnotation(a); 93 * cf.addAttribute(attr); 94 * cf.setVersionToJava5(); 95 * </pre></ul> 96 * 97 * <p>The last statement is necessary if the class file was produced by 98 * Javassist or JDK 1.4. Otherwise, it is not necessary. 99 * 100 * @see AnnotationDefaultAttribute 101 * @see javassist.bytecode.annotation.Annotation 102 */ 103 public class AnnotationsAttribute extends AttributeInfo { 104 /** 105 * The name of the <code>RuntimeVisibleAnnotations</code> attribute. 106 */ 107 public static final String visibleTag = "RuntimeVisibleAnnotations"; 108 109 /** 110 * The name of the <code>RuntimeInvisibleAnnotations</code> attribute. 111 */ 112 public static final String invisibleTag = "RuntimeInvisibleAnnotations"; 113 114 /** 115 * Constructs a <code>Runtime(In)VisibleAnnotations_attribute</code>. 116 * 117 * @param cp constant pool 118 * @param attrname attribute name (<code>visibleTag</code> or 119 * <code>invisibleTag</code>). 120 * @param info the contents of this attribute. It does not 121 * include <code>attribute_name_index</code> or 122 * <code>attribute_length</code>. 123 */ AnnotationsAttribute(ConstPool cp, String attrname, byte[] info)124 public AnnotationsAttribute(ConstPool cp, String attrname, byte[] info) { 125 super(cp, attrname, info); 126 } 127 128 /** 129 * Constructs an empty 130 * <code>Runtime(In)VisibleAnnotations_attribute</code>. 131 * A new annotation can be later added to the created attribute 132 * by <code>setAnnotations()</code>. 133 * 134 * @param cp constant pool 135 * @param attrname attribute name (<code>visibleTag</code> or 136 * <code>invisibleTag</code>). 137 * @see #setAnnotations(Annotation[]) 138 */ AnnotationsAttribute(ConstPool cp, String attrname)139 public AnnotationsAttribute(ConstPool cp, String attrname) { 140 this(cp, attrname, new byte[] { 0, 0 }); 141 } 142 143 /** 144 * @param n the attribute name. 145 */ AnnotationsAttribute(ConstPool cp, int n, DataInputStream in)146 AnnotationsAttribute(ConstPool cp, int n, DataInputStream in) 147 throws IOException 148 { 149 super(cp, n, in); 150 } 151 152 /** 153 * Returns <code>num_annotations</code>. 154 */ numAnnotations()155 public int numAnnotations() { 156 return ByteArray.readU16bit(info, 0); 157 } 158 159 /** 160 * Copies this attribute and returns a new copy. 161 */ copy(ConstPool newCp, Map classnames)162 public AttributeInfo copy(ConstPool newCp, Map classnames) { 163 Copier copier = new Copier(info, constPool, newCp, classnames); 164 try { 165 copier.annotationArray(); 166 return new AnnotationsAttribute(newCp, getName(), copier.close()); 167 } 168 catch (Exception e) { 169 throw new RuntimeException(e); 170 } 171 } 172 173 /** 174 * Parses the annotations and returns a data structure representing 175 * the annotation with the specified type. See also 176 * <code>getAnnotations()</code> as to the returned data structure. 177 * 178 * @param type the annotation type. 179 * @return null if the specified annotation type is not included. 180 * @see #getAnnotations() 181 */ getAnnotation(String type)182 public Annotation getAnnotation(String type) { 183 Annotation[] annotations = getAnnotations(); 184 for (int i = 0; i < annotations.length; i++) { 185 if (annotations[i].getTypeName().equals(type)) 186 return annotations[i]; 187 } 188 189 return null; 190 } 191 192 /** 193 * Adds an annotation. If there is an annotation with the same type, 194 * it is removed before the new annotation is added. 195 * 196 * @param annotation the added annotation. 197 */ addAnnotation(Annotation annotation)198 public void addAnnotation(Annotation annotation) { 199 String type = annotation.getTypeName(); 200 Annotation[] annotations = getAnnotations(); 201 for (int i = 0; i < annotations.length; i++) { 202 if (annotations[i].getTypeName().equals(type)) { 203 annotations[i] = annotation; 204 setAnnotations(annotations); 205 return; 206 } 207 } 208 209 Annotation[] newlist = new Annotation[annotations.length + 1]; 210 System.arraycopy(annotations, 0, newlist, 0, annotations.length); 211 newlist[annotations.length] = annotation; 212 setAnnotations(newlist); 213 } 214 215 /** 216 * Parses the annotations and returns a data structure representing 217 * that parsed annotations. Note that changes of the node values of the 218 * returned tree are not reflected on the annotations represented by 219 * this object unless the tree is copied back to this object by 220 * <code>setAnnotations()</code>. 221 * 222 * @see #setAnnotations(Annotation[]) 223 */ getAnnotations()224 public Annotation[] getAnnotations() { 225 try { 226 return new Parser(info, constPool).parseAnnotations(); 227 } 228 catch (Exception e) { 229 throw new RuntimeException(e); 230 } 231 } 232 233 /** 234 * Changes the annotations represented by this object according to 235 * the given array of <code>Annotation</code> objects. 236 * 237 * @param annotations the data structure representing the 238 * new annotations. 239 */ setAnnotations(Annotation[] annotations)240 public void setAnnotations(Annotation[] annotations) { 241 ByteArrayOutputStream output = new ByteArrayOutputStream(); 242 AnnotationsWriter writer = new AnnotationsWriter(output, constPool); 243 try { 244 int n = annotations.length; 245 writer.numAnnotations(n); 246 for (int i = 0; i < n; ++i) 247 annotations[i].write(writer); 248 249 writer.close(); 250 } 251 catch (IOException e) { 252 throw new RuntimeException(e); // should never reach here. 253 } 254 255 set(output.toByteArray()); 256 } 257 258 /** 259 * Changes the annotations. A call to this method is equivalent to: 260 * <ul><pre>setAnnotations(new Annotation[] { annotation })</pre></ul> 261 * 262 * @param annotation the data structure representing 263 * the new annotation. 264 */ setAnnotation(Annotation annotation)265 public void setAnnotation(Annotation annotation) { 266 setAnnotations(new Annotation[] { annotation }); 267 } 268 269 /** 270 * @param oldname a JVM class name. 271 * @param newname a JVM class name. 272 */ renameClass(String oldname, String newname)273 void renameClass(String oldname, String newname) { 274 HashMap map = new HashMap(); 275 map.put(oldname, newname); 276 renameClass(map); 277 } 278 renameClass(Map classnames)279 void renameClass(Map classnames) { 280 Renamer renamer = new Renamer(info, getConstPool(), classnames); 281 try { 282 renamer.annotationArray(); 283 } catch (Exception e) { 284 throw new RuntimeException(e); 285 } 286 } 287 getRefClasses(Map classnames)288 void getRefClasses(Map classnames) { renameClass(classnames); } 289 290 /** 291 * Returns a string representation of this object. 292 */ toString()293 public String toString() { 294 Annotation[] a = getAnnotations(); 295 StringBuilder sbuf = new StringBuilder(); 296 int i = 0; 297 while (i < a.length) { 298 sbuf.append(a[i++].toString()); 299 if (i != a.length) 300 sbuf.append(", "); 301 } 302 303 return sbuf.toString(); 304 } 305 306 static class Walker { 307 byte[] info; 308 Walker(byte[] attrInfo)309 Walker(byte[] attrInfo) { 310 info = attrInfo; 311 } 312 parameters()313 final void parameters() throws Exception { 314 int numParam = info[0] & 0xff; 315 parameters(numParam, 1); 316 } 317 parameters(int numParam, int pos)318 void parameters(int numParam, int pos) throws Exception { 319 for (int i = 0; i < numParam; ++i) 320 pos = annotationArray(pos); 321 } 322 annotationArray()323 final void annotationArray() throws Exception { 324 annotationArray(0); 325 } 326 annotationArray(int pos)327 final int annotationArray(int pos) throws Exception { 328 int num = ByteArray.readU16bit(info, pos); 329 return annotationArray(pos + 2, num); 330 } 331 annotationArray(int pos, int num)332 int annotationArray(int pos, int num) throws Exception { 333 for (int i = 0; i < num; ++i) 334 pos = annotation(pos); 335 336 return pos; 337 } 338 annotation(int pos)339 final int annotation(int pos) throws Exception { 340 int type = ByteArray.readU16bit(info, pos); 341 int numPairs = ByteArray.readU16bit(info, pos + 2); 342 return annotation(pos + 4, type, numPairs); 343 } 344 annotation(int pos, int type, int numPairs)345 int annotation(int pos, int type, int numPairs) throws Exception { 346 for (int j = 0; j < numPairs; ++j) 347 pos = memberValuePair(pos); 348 349 return pos; 350 } 351 memberValuePair(int pos)352 final int memberValuePair(int pos) throws Exception { 353 int nameIndex = ByteArray.readU16bit(info, pos); 354 return memberValuePair(pos + 2, nameIndex); 355 } 356 memberValuePair(int pos, int nameIndex)357 int memberValuePair(int pos, int nameIndex) throws Exception { 358 return memberValue(pos); 359 } 360 memberValue(int pos)361 final int memberValue(int pos) throws Exception { 362 int tag = info[pos] & 0xff; 363 if (tag == 'e') { 364 int typeNameIndex = ByteArray.readU16bit(info, pos + 1); 365 int constNameIndex = ByteArray.readU16bit(info, pos + 3); 366 enumMemberValue(pos, typeNameIndex, constNameIndex); 367 return pos + 5; 368 } 369 else if (tag == 'c') { 370 int index = ByteArray.readU16bit(info, pos + 1); 371 classMemberValue(pos, index); 372 return pos + 3; 373 } 374 else if (tag == '@') 375 return annotationMemberValue(pos + 1); 376 else if (tag == '[') { 377 int num = ByteArray.readU16bit(info, pos + 1); 378 return arrayMemberValue(pos + 3, num); 379 } 380 else { // primitive types or String. 381 int index = ByteArray.readU16bit(info, pos + 1); 382 constValueMember(tag, index); 383 return pos + 3; 384 } 385 } 386 constValueMember(int tag, int index)387 void constValueMember(int tag, int index) throws Exception {} 388 enumMemberValue(int pos, int typeNameIndex, int constNameIndex)389 void enumMemberValue(int pos, int typeNameIndex, int constNameIndex) 390 throws Exception { 391 } 392 classMemberValue(int pos, int index)393 void classMemberValue(int pos, int index) throws Exception {} 394 annotationMemberValue(int pos)395 int annotationMemberValue(int pos) throws Exception { 396 return annotation(pos); 397 } 398 arrayMemberValue(int pos, int num)399 int arrayMemberValue(int pos, int num) throws Exception { 400 for (int i = 0; i < num; ++i) { 401 pos = memberValue(pos); 402 } 403 404 return pos; 405 } 406 } 407 408 static class Renamer extends Walker { 409 ConstPool cpool; 410 Map classnames; 411 412 /** 413 * Constructs a renamer. It renames some class names 414 * into the new names specified by <code>map</code>. 415 * 416 * @param info the annotations attribute. 417 * @param cp the constant pool. 418 * @param map pairs of replaced and substituted class names. 419 * It can be null. 420 */ Renamer(byte[] info, ConstPool cp, Map map)421 Renamer(byte[] info, ConstPool cp, Map map) { 422 super(info); 423 cpool = cp; 424 classnames = map; 425 } 426 annotation(int pos, int type, int numPairs)427 int annotation(int pos, int type, int numPairs) throws Exception { 428 renameType(pos - 4, type); 429 return super.annotation(pos, type, numPairs); 430 } 431 enumMemberValue(int pos, int typeNameIndex, int constNameIndex)432 void enumMemberValue(int pos, int typeNameIndex, int constNameIndex) 433 throws Exception 434 { 435 renameType(pos + 1, typeNameIndex); 436 super.enumMemberValue(pos, typeNameIndex, constNameIndex); 437 } 438 classMemberValue(int pos, int index)439 void classMemberValue(int pos, int index) throws Exception { 440 renameType(pos + 1, index); 441 super.classMemberValue(pos, index); 442 } 443 renameType(int pos, int index)444 private void renameType(int pos, int index) { 445 String name = cpool.getUtf8Info(index); 446 String newName = Descriptor.rename(name, classnames); 447 if (!name.equals(newName)) { 448 int index2 = cpool.addUtf8Info(newName); 449 ByteArray.write16bit(index2, info, pos); 450 } 451 } 452 } 453 454 static class Copier extends Walker { 455 ByteArrayOutputStream output; 456 AnnotationsWriter writer; 457 ConstPool srcPool, destPool; 458 Map classnames; 459 460 /** 461 * Constructs a copier. This copier renames some class names 462 * into the new names specified by <code>map</code> when it copies 463 * an annotation attribute. 464 * 465 * @param info the source attribute. 466 * @param src the constant pool of the source class. 467 * @param dest the constant pool of the destination class. 468 * @param map pairs of replaced and substituted class names. 469 * It can be null. 470 */ Copier(byte[] info, ConstPool src, ConstPool dest, Map map)471 Copier(byte[] info, ConstPool src, ConstPool dest, Map map) { 472 super(info); 473 output = new ByteArrayOutputStream(); 474 writer = new AnnotationsWriter(output, dest); 475 srcPool = src; 476 destPool = dest; 477 classnames = map; 478 } 479 close()480 byte[] close() throws IOException { 481 writer.close(); 482 return output.toByteArray(); 483 } 484 parameters(int numParam, int pos)485 void parameters(int numParam, int pos) throws Exception { 486 writer.numParameters(numParam); 487 super.parameters(numParam, pos); 488 } 489 annotationArray(int pos, int num)490 int annotationArray(int pos, int num) throws Exception { 491 writer.numAnnotations(num); 492 return super.annotationArray(pos, num); 493 } 494 annotation(int pos, int type, int numPairs)495 int annotation(int pos, int type, int numPairs) throws Exception { 496 writer.annotation(copyType(type), numPairs); 497 return super.annotation(pos, type, numPairs); 498 } 499 memberValuePair(int pos, int nameIndex)500 int memberValuePair(int pos, int nameIndex) throws Exception { 501 writer.memberValuePair(copy(nameIndex)); 502 return super.memberValuePair(pos, nameIndex); 503 } 504 constValueMember(int tag, int index)505 void constValueMember(int tag, int index) throws Exception { 506 writer.constValueIndex(tag, copy(index)); 507 super.constValueMember(tag, index); 508 } 509 enumMemberValue(int pos, int typeNameIndex, int constNameIndex)510 void enumMemberValue(int pos, int typeNameIndex, int constNameIndex) 511 throws Exception 512 { 513 writer.enumConstValue(copyType(typeNameIndex), copy(constNameIndex)); 514 super.enumMemberValue(pos, typeNameIndex, constNameIndex); 515 } 516 classMemberValue(int pos, int index)517 void classMemberValue(int pos, int index) throws Exception { 518 writer.classInfoIndex(copyType(index)); 519 super.classMemberValue(pos, index); 520 } 521 annotationMemberValue(int pos)522 int annotationMemberValue(int pos) throws Exception { 523 writer.annotationValue(); 524 return super.annotationMemberValue(pos); 525 } 526 arrayMemberValue(int pos, int num)527 int arrayMemberValue(int pos, int num) throws Exception { 528 writer.arrayValue(num); 529 return super.arrayMemberValue(pos, num); 530 } 531 532 /** 533 * Copies a constant pool entry into the destination constant pool 534 * and returns the index of the copied entry. 535 * 536 * @param srcIndex the index of the copied entry into the source 537 * constant pool. 538 * @return the index of the copied item into the destination 539 * constant pool. 540 */ copy(int srcIndex)541 int copy(int srcIndex) { 542 return srcPool.copy(srcIndex, destPool, classnames); 543 } 544 545 /** 546 * Copies a constant pool entry into the destination constant pool 547 * and returns the index of the copied entry. That entry must be 548 * a Utf8Info representing a class name in the L<class name>; form. 549 * 550 * @param srcIndex the index of the copied entry into the source 551 * constant pool. 552 * @return the index of the copied item into the destination 553 * constant pool. 554 */ copyType(int srcIndex)555 int copyType(int srcIndex) { 556 String name = srcPool.getUtf8Info(srcIndex); 557 String newName = Descriptor.rename(name, classnames); 558 return destPool.addUtf8Info(newName); 559 } 560 } 561 562 static class Parser extends Walker { 563 ConstPool pool; 564 Annotation[][] allParams; // all parameters 565 Annotation[] allAnno; // all annotations 566 Annotation currentAnno; // current annotation 567 MemberValue currentMember; // current member 568 569 /** 570 * Constructs a parser. This parser constructs a parse tree of 571 * the annotations. 572 * 573 * @param info the attribute. 574 * @param src the constant pool. 575 */ Parser(byte[] info, ConstPool cp)576 Parser(byte[] info, ConstPool cp) { 577 super(info); 578 pool = cp; 579 } 580 parseParameters()581 Annotation[][] parseParameters() throws Exception { 582 parameters(); 583 return allParams; 584 } 585 parseAnnotations()586 Annotation[] parseAnnotations() throws Exception { 587 annotationArray(); 588 return allAnno; 589 } 590 parseMemberValue()591 MemberValue parseMemberValue() throws Exception { 592 memberValue(0); 593 return currentMember; 594 } 595 parameters(int numParam, int pos)596 void parameters(int numParam, int pos) throws Exception { 597 Annotation[][] params = new Annotation[numParam][]; 598 for (int i = 0; i < numParam; ++i) { 599 pos = annotationArray(pos); 600 params[i] = allAnno; 601 } 602 603 allParams = params; 604 } 605 annotationArray(int pos, int num)606 int annotationArray(int pos, int num) throws Exception { 607 Annotation[] array = new Annotation[num]; 608 for (int i = 0; i < num; ++i) { 609 pos = annotation(pos); 610 array[i] = currentAnno; 611 } 612 613 allAnno = array; 614 return pos; 615 } 616 annotation(int pos, int type, int numPairs)617 int annotation(int pos, int type, int numPairs) throws Exception { 618 currentAnno = new Annotation(type, pool); 619 return super.annotation(pos, type, numPairs); 620 } 621 memberValuePair(int pos, int nameIndex)622 int memberValuePair(int pos, int nameIndex) throws Exception { 623 pos = super.memberValuePair(pos, nameIndex); 624 currentAnno.addMemberValue(nameIndex, currentMember); 625 return pos; 626 } 627 constValueMember(int tag, int index)628 void constValueMember(int tag, int index) throws Exception { 629 MemberValue m; 630 ConstPool cp = pool; 631 switch (tag) { 632 case 'B' : 633 m = new ByteMemberValue(index, cp); 634 break; 635 case 'C' : 636 m = new CharMemberValue(index, cp); 637 break; 638 case 'D' : 639 m = new DoubleMemberValue(index, cp); 640 break; 641 case 'F' : 642 m = new FloatMemberValue(index, cp); 643 break; 644 case 'I' : 645 m = new IntegerMemberValue(index, cp); 646 break; 647 case 'J' : 648 m = new LongMemberValue(index, cp); 649 break; 650 case 'S' : 651 m = new ShortMemberValue(index, cp); 652 break; 653 case 'Z' : 654 m = new BooleanMemberValue(index, cp); 655 break; 656 case 's' : 657 m = new StringMemberValue(index, cp); 658 break; 659 default : 660 throw new RuntimeException("unknown tag:" + tag); 661 } 662 663 currentMember = m; 664 super.constValueMember(tag, index); 665 } 666 enumMemberValue(int pos, int typeNameIndex, int constNameIndex)667 void enumMemberValue(int pos, int typeNameIndex, int constNameIndex) 668 throws Exception 669 { 670 currentMember = new EnumMemberValue(typeNameIndex, 671 constNameIndex, pool); 672 super.enumMemberValue(pos, typeNameIndex, constNameIndex); 673 } 674 classMemberValue(int pos, int index)675 void classMemberValue(int pos, int index) throws Exception { 676 currentMember = new ClassMemberValue(index, pool); 677 super.classMemberValue(pos, index); 678 } 679 annotationMemberValue(int pos)680 int annotationMemberValue(int pos) throws Exception { 681 Annotation anno = currentAnno; 682 pos = super.annotationMemberValue(pos); 683 currentMember = new AnnotationMemberValue(currentAnno, pool); 684 currentAnno = anno; 685 return pos; 686 } 687 arrayMemberValue(int pos, int num)688 int arrayMemberValue(int pos, int num) throws Exception { 689 ArrayMemberValue amv = new ArrayMemberValue(pool); 690 MemberValue[] elements = new MemberValue[num]; 691 for (int i = 0; i < num; ++i) { 692 pos = memberValue(pos); 693 elements[i] = currentMember; 694 } 695 696 amv.setValue(elements); 697 currentMember = amv; 698 return pos; 699 } 700 } 701 } 702