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; 17 18 import javassist.bytecode.*; 19 import javassist.compiler.Javac; 20 import javassist.compiler.SymbolTable; 21 import javassist.compiler.CompileError; 22 import javassist.compiler.ast.ASTree; 23 import javassist.compiler.ast.IntConst; 24 import javassist.compiler.ast.DoubleConst; 25 import javassist.compiler.ast.StringL; 26 27 /** 28 * An instance of CtField represents a field. 29 * 30 * @see CtClass#getDeclaredFields() 31 */ 32 public class CtField extends CtMember { 33 static final String javaLangString = "java.lang.String"; 34 35 protected FieldInfo fieldInfo; 36 37 /** 38 * Creates a <code>CtField</code> object. 39 * The created field must be added to a class 40 * with <code>CtClass.addField()</code>. 41 * An initial value of the field is specified 42 * by a <code>CtField.Initializer</code> object. 43 * 44 * <p>If getter and setter methods are needed, 45 * call <code>CtNewMethod.getter()</code> and 46 * <code>CtNewMethod.setter()</code>. 47 * 48 * @param type field type 49 * @param name field name 50 * @param declaring the class to which the field will be added. 51 * 52 * @see CtClass#addField(CtField) 53 * @see CtNewMethod#getter(String,CtField) 54 * @see CtNewMethod#setter(String,CtField) 55 * @see CtField.Initializer 56 */ CtField(CtClass type, String name, CtClass declaring)57 public CtField(CtClass type, String name, CtClass declaring) 58 throws CannotCompileException 59 { 60 this(Descriptor.of(type), name, declaring); 61 } 62 63 /** 64 * Creates a copy of the given field. 65 * The created field must be added to a class 66 * with <code>CtClass.addField()</code>. 67 * An initial value of the field is specified 68 * by a <code>CtField.Initializer</code> object. 69 * 70 * <p>If getter and setter methods are needed, 71 * call <code>CtNewMethod.getter()</code> and 72 * <code>CtNewMethod.setter()</code>. 73 * 74 * @param src the original field 75 * @param declaring the class to which the field will be added. 76 * @see CtNewMethod#getter(String,CtField) 77 * @see CtNewMethod#setter(String,CtField) 78 * @see CtField.Initializer 79 */ CtField(CtField src, CtClass declaring)80 public CtField(CtField src, CtClass declaring) 81 throws CannotCompileException 82 { 83 this(src.fieldInfo.getDescriptor(), src.fieldInfo.getName(), 84 declaring); 85 java.util.ListIterator iterator 86 = src.fieldInfo.getAttributes().listIterator(); 87 FieldInfo fi = fieldInfo; 88 fi.setAccessFlags(src.fieldInfo.getAccessFlags()); 89 ConstPool cp = fi.getConstPool(); 90 while (iterator.hasNext()) { 91 AttributeInfo ainfo = (AttributeInfo)iterator.next(); 92 fi.addAttribute(ainfo.copy(cp, null)); 93 } 94 } 95 CtField(String typeDesc, String name, CtClass clazz)96 private CtField(String typeDesc, String name, CtClass clazz) 97 throws CannotCompileException 98 { 99 super(clazz); 100 ClassFile cf = clazz.getClassFile2(); 101 if (cf == null) 102 throw new CannotCompileException("bad declaring class: " 103 + clazz.getName()); 104 105 fieldInfo = new FieldInfo(cf.getConstPool(), name, typeDesc); 106 } 107 CtField(FieldInfo fi, CtClass clazz)108 CtField(FieldInfo fi, CtClass clazz) { 109 super(clazz); 110 fieldInfo = fi; 111 } 112 113 /** 114 * Returns a String representation of the object. 115 */ toString()116 public String toString() { 117 return getDeclaringClass().getName() + "." + getName() 118 + ":" + fieldInfo.getDescriptor(); 119 } 120 extendToString(StringBuffer buffer)121 protected void extendToString(StringBuffer buffer) { 122 buffer.append(' '); 123 buffer.append(getName()); 124 buffer.append(' '); 125 buffer.append(fieldInfo.getDescriptor()); 126 } 127 128 /* Javac.CtFieldWithInit overrides. 129 */ getInitAST()130 protected ASTree getInitAST() { return null; } 131 132 /* Called by CtClassType.addField(). 133 */ getInit()134 Initializer getInit() { 135 ASTree tree = getInitAST(); 136 if (tree == null) 137 return null; 138 else 139 return Initializer.byExpr(tree); 140 } 141 142 /** 143 * Compiles the given source code and creates a field. 144 * Examples of the source code are: 145 * 146 * <ul><pre> 147 * "public String name;" 148 * "public int k = 3;"</pre></ul> 149 * 150 * <p>Note that the source code ends with <code>';'</code> 151 * (semicolon). 152 * 153 * @param src the source text. 154 * @param declaring the class to which the created field is added. 155 */ make(String src, CtClass declaring)156 public static CtField make(String src, CtClass declaring) 157 throws CannotCompileException 158 { 159 Javac compiler = new Javac(declaring); 160 try { 161 CtMember obj = compiler.compile(src); 162 if (obj instanceof CtField) 163 return (CtField)obj; // an instance of Javac.CtFieldWithInit 164 } 165 catch (CompileError e) { 166 throw new CannotCompileException(e); 167 } 168 169 throw new CannotCompileException("not a field"); 170 } 171 172 /** 173 * Returns the FieldInfo representing the field in the class file. 174 */ getFieldInfo()175 public FieldInfo getFieldInfo() { 176 declaringClass.checkModify(); 177 return fieldInfo; 178 } 179 180 /** 181 * Returns the FieldInfo representing the field in the class 182 * file (read only). 183 * Normal applications do not need calling this method. Use 184 * <code>getFieldInfo()</code>. 185 * 186 * <p>The <code>FieldInfo</code> object obtained by this method 187 * is read only. Changes to this object might not be reflected 188 * on a class file generated by <code>toBytecode()</code>, 189 * <code>toClass()</code>, etc in <code>CtClass</code>. 190 * 191 * <p>This method is available even if the <code>CtClass</code> 192 * containing this field is frozen. However, if the class is 193 * frozen, the <code>FieldInfo</code> might be also pruned. 194 * 195 * @see #getFieldInfo() 196 * @see CtClass#isFrozen() 197 * @see CtClass#prune() 198 */ getFieldInfo2()199 public FieldInfo getFieldInfo2() { return fieldInfo; } 200 201 /** 202 * Returns the class declaring the field. 203 */ getDeclaringClass()204 public CtClass getDeclaringClass() { 205 // this is redundant but for javadoc. 206 return super.getDeclaringClass(); 207 } 208 209 /** 210 * Returns the name of the field. 211 */ getName()212 public String getName() { 213 return fieldInfo.getName(); 214 } 215 216 /** 217 * Changes the name of the field. 218 */ setName(String newName)219 public void setName(String newName) { 220 declaringClass.checkModify(); 221 fieldInfo.setName(newName); 222 } 223 224 /** 225 * Returns the encoded modifiers of the field. 226 * 227 * @see Modifier 228 */ getModifiers()229 public int getModifiers() { 230 return AccessFlag.toModifier(fieldInfo.getAccessFlags()); 231 } 232 233 /** 234 * Sets the encoded modifiers of the field. 235 * 236 * @see Modifier 237 */ setModifiers(int mod)238 public void setModifiers(int mod) { 239 declaringClass.checkModify(); 240 fieldInfo.setAccessFlags(AccessFlag.of(mod)); 241 } 242 243 /** 244 * Returns true if the class has the specified annotation class. 245 * 246 * @param clz the annotation class. 247 * @return <code>true</code> if the annotation is found, otherwise <code>false</code>. 248 * @since 3.11 249 */ hasAnnotation(Class clz)250 public boolean hasAnnotation(Class clz) { 251 FieldInfo fi = getFieldInfo2(); 252 AnnotationsAttribute ainfo = (AnnotationsAttribute) 253 fi.getAttribute(AnnotationsAttribute.invisibleTag); 254 AnnotationsAttribute ainfo2 = (AnnotationsAttribute) 255 fi.getAttribute(AnnotationsAttribute.visibleTag); 256 return CtClassType.hasAnnotationType(clz, getDeclaringClass().getClassPool(), 257 ainfo, ainfo2); 258 } 259 260 /** 261 * Returns the annotation if the class has the specified annotation class. 262 * For example, if an annotation <code>@Author</code> is associated 263 * with this field, an <code>Author</code> object is returned. 264 * The member values can be obtained by calling methods on 265 * the <code>Author</code> object. 266 * 267 * @param clz the annotation class. 268 * @return the annotation if found, otherwise <code>null</code>. 269 * @since 3.11 270 */ getAnnotation(Class clz)271 public Object getAnnotation(Class clz) throws ClassNotFoundException { 272 FieldInfo fi = getFieldInfo2(); 273 AnnotationsAttribute ainfo = (AnnotationsAttribute) 274 fi.getAttribute(AnnotationsAttribute.invisibleTag); 275 AnnotationsAttribute ainfo2 = (AnnotationsAttribute) 276 fi.getAttribute(AnnotationsAttribute.visibleTag); 277 return CtClassType.getAnnotationType(clz, getDeclaringClass().getClassPool(), 278 ainfo, ainfo2); 279 } 280 281 /** 282 * Returns the annotations associated with this field. 283 * 284 * @return an array of annotation-type objects. 285 * @see #getAvailableAnnotations() 286 * @since 3.1 287 */ getAnnotations()288 public Object[] getAnnotations() throws ClassNotFoundException { 289 return getAnnotations(false); 290 } 291 292 /** 293 * Returns the annotations associated with this field. 294 * If any annotations are not on the classpath, they are not included 295 * in the returned array. 296 * 297 * @return an array of annotation-type objects. 298 * @see #getAnnotations() 299 * @since 3.3 300 */ getAvailableAnnotations()301 public Object[] getAvailableAnnotations(){ 302 try { 303 return getAnnotations(true); 304 } 305 catch (ClassNotFoundException e) { 306 throw new RuntimeException("Unexpected exception", e); 307 } 308 } 309 getAnnotations(boolean ignoreNotFound)310 private Object[] getAnnotations(boolean ignoreNotFound) throws ClassNotFoundException { 311 FieldInfo fi = getFieldInfo2(); 312 AnnotationsAttribute ainfo = (AnnotationsAttribute) 313 fi.getAttribute(AnnotationsAttribute.invisibleTag); 314 AnnotationsAttribute ainfo2 = (AnnotationsAttribute) 315 fi.getAttribute(AnnotationsAttribute.visibleTag); 316 return CtClassType.toAnnotationType(ignoreNotFound, getDeclaringClass().getClassPool(), 317 ainfo, ainfo2); 318 } 319 320 /** 321 * Returns the character string representing the type of the field. 322 * The field signature is represented by a character string 323 * called a field descriptor, which is defined in the JVM specification. 324 * If two fields have the same type, 325 * <code>getSignature()</code> returns the same string. 326 * 327 * <p>Note that the returned string is not the type signature 328 * contained in the <code>SignatureAttirbute</code>. It is 329 * a descriptor. To obtain a type signature, call the following 330 * methods: 331 * 332 * <ul><pre>getFieldInfo().getAttribute(SignatureAttribute.tag) 333 * </pre></ul> 334 * 335 * @see javassist.bytecode.Descriptor 336 * @see javassist.bytecode.SignatureAttribute 337 */ getSignature()338 public String getSignature() { 339 return fieldInfo.getDescriptor(); 340 } 341 342 /** 343 * Returns the type of the field. 344 */ getType()345 public CtClass getType() throws NotFoundException { 346 return Descriptor.toCtClass(fieldInfo.getDescriptor(), 347 declaringClass.getClassPool()); 348 } 349 350 /** 351 * Sets the type of the field. 352 */ setType(CtClass clazz)353 public void setType(CtClass clazz) { 354 declaringClass.checkModify(); 355 fieldInfo.setDescriptor(Descriptor.of(clazz)); 356 } 357 358 /** 359 * Returns the value of this field if it is a constant field. 360 * This method works only if the field type is a primitive type 361 * or <code>String</code> type. Otherwise, it returns <code>null</code>. 362 * A constant field is <code>static</code> and <code>final</code>. 363 * 364 * @return a <code>Integer</code>, <code>Long</code>, <code>Float</code>, 365 * <code>Double</code>, <code>Boolean</code>, 366 * or <code>String</code> object 367 * representing the constant value. 368 * <code>null</code> if it is not a constant field 369 * or if the field type is not a primitive type 370 * or <code>String</code>. 371 */ getConstantValue()372 public Object getConstantValue() { 373 // When this method is modified, 374 // see also getConstantFieldValue() in TypeChecker. 375 376 int index = fieldInfo.getConstantValue(); 377 if (index == 0) 378 return null; 379 380 ConstPool cp = fieldInfo.getConstPool(); 381 switch (cp.getTag(index)) { 382 case ConstPool.CONST_Long : 383 return new Long(cp.getLongInfo(index)); 384 case ConstPool.CONST_Float : 385 return new Float(cp.getFloatInfo(index)); 386 case ConstPool.CONST_Double : 387 return new Double(cp.getDoubleInfo(index)); 388 case ConstPool.CONST_Integer : 389 int value = cp.getIntegerInfo(index); 390 // "Z" means boolean type. 391 if ("Z".equals(fieldInfo.getDescriptor())) 392 return new Boolean(value != 0); 393 else 394 return new Integer(value); 395 case ConstPool.CONST_String : 396 return cp.getStringInfo(index); 397 default : 398 throw new RuntimeException("bad tag: " + cp.getTag(index) 399 + " at " + index); 400 } 401 } 402 403 /** 404 * Obtains an attribute with the given name. 405 * If that attribute is not found in the class file, this 406 * method returns null. 407 * 408 * <p>Note that an attribute is a data block specified by 409 * the class file format. 410 * See {@link javassist.bytecode.AttributeInfo}. 411 * 412 * @param name attribute name 413 */ getAttribute(String name)414 public byte[] getAttribute(String name) { 415 AttributeInfo ai = fieldInfo.getAttribute(name); 416 if (ai == null) 417 return null; 418 else 419 return ai.get(); 420 } 421 422 /** 423 * Adds an attribute. The attribute is saved in the class file. 424 * 425 * <p>Note that an attribute is a data block specified by 426 * the class file format. 427 * See {@link javassist.bytecode.AttributeInfo}. 428 * 429 * @param name attribute name 430 * @param data attribute value 431 */ setAttribute(String name, byte[] data)432 public void setAttribute(String name, byte[] data) { 433 declaringClass.checkModify(); 434 fieldInfo.addAttribute(new AttributeInfo(fieldInfo.getConstPool(), 435 name, data)); 436 } 437 438 // inner classes 439 440 /** 441 * Instances of this class specify how to initialize a field. 442 * <code>Initializer</code> is passed to 443 * <code>CtClass.addField()</code> with a <code>CtField</code>. 444 * 445 * <p>This class cannot be instantiated with the <code>new</code> operator. 446 * Factory methods such as <code>byParameter()</code> and 447 * <code>byNew</code> 448 * must be used for the instantiation. They create a new instance with 449 * the given parameters and return it. 450 * 451 * @see CtClass#addField(CtField,CtField.Initializer) 452 */ 453 public static abstract class Initializer { 454 /** 455 * Makes an initializer that assigns a constant integer value. 456 * The field must be integer, short, char, or byte type. 457 */ constant(int i)458 public static Initializer constant(int i) { 459 return new IntInitializer(i); 460 } 461 462 /** 463 * Makes an initializer that assigns a constant boolean value. 464 * The field must be boolean type. 465 */ constant(boolean b)466 public static Initializer constant(boolean b) { 467 return new IntInitializer(b ? 1 : 0); 468 } 469 470 /** 471 * Makes an initializer that assigns a constant long value. 472 * The field must be long type. 473 */ constant(long l)474 public static Initializer constant(long l) { 475 return new LongInitializer(l); 476 } 477 478 /** 479 * Makes an initializer that assigns a constant float value. 480 * The field must be float type. 481 */ constant(float l)482 public static Initializer constant(float l) { 483 return new FloatInitializer(l); 484 } 485 486 /** 487 * Makes an initializer that assigns a constant double value. 488 * The field must be double type. 489 */ constant(double d)490 public static Initializer constant(double d) { 491 return new DoubleInitializer(d); 492 } 493 494 /** 495 * Makes an initializer that assigns a constant string value. 496 * The field must be <code>java.lang.String</code> type. 497 */ constant(String s)498 public static Initializer constant(String s) { 499 return new StringInitializer(s); 500 } 501 502 /** 503 * Makes an initializer using a constructor parameter. 504 * 505 * <p>The initial value is the 506 * N-th parameter given to the constructor of the object including 507 * the field. If the constructor takes less than N parameters, 508 * the field is not initialized. 509 * If the field is static, it is never initialized. 510 * 511 * @param nth the n-th (>= 0) parameter is used as 512 * the initial value. 513 * If nth is 0, then the first parameter is 514 * used. 515 */ byParameter(int nth)516 public static Initializer byParameter(int nth) { 517 ParamInitializer i = new ParamInitializer(); 518 i.nthParam = nth; 519 return i; 520 } 521 522 /** 523 * Makes an initializer creating a new object. 524 * 525 * <p>This initializer creates a new object and uses it as the initial 526 * value of the field. The constructor of the created object receives 527 * the parameter: 528 * 529 * <ul><code>Object obj</code> - the object including the field.<br> 530 * </ul> 531 * 532 * <p>If the initialized field is static, then the constructor does 533 * not receive any parameters. 534 * 535 * @param objectType the class instantiated for the initial value. 536 */ byNew(CtClass objectType)537 public static Initializer byNew(CtClass objectType) { 538 NewInitializer i = new NewInitializer(); 539 i.objectType = objectType; 540 i.stringParams = null; 541 i.withConstructorParams = false; 542 return i; 543 } 544 545 /** 546 * Makes an initializer creating a new object. 547 * 548 * <p>This initializer creates a new object and uses it as the initial 549 * value of the field. The constructor of the created object receives 550 * the parameters: 551 * 552 * <ul><code>Object obj</code> - the object including the field.<br> 553 * <code>String[] strs</code> - the character strings specified 554 * by <code>stringParams</code><br> 555 * </ul> 556 * 557 * <p>If the initialized field is static, then the constructor 558 * receives only <code>strs</code>. 559 * 560 * @param objectType the class instantiated for the initial value. 561 * @param stringParams the array of strings passed to the 562 * constructor. 563 */ byNew(CtClass objectType, String[] stringParams)564 public static Initializer byNew(CtClass objectType, 565 String[] stringParams) { 566 NewInitializer i = new NewInitializer(); 567 i.objectType = objectType; 568 i.stringParams = stringParams; 569 i.withConstructorParams = false; 570 return i; 571 } 572 573 /** 574 * Makes an initializer creating a new object. 575 * 576 * <p>This initializer creates a new object and uses it as the initial 577 * value of the field. The constructor of the created object receives 578 * the parameters: 579 * 580 * <ul><code>Object obj</code> - the object including the field.<br> 581 * <code>Object[] args</code> - the parameters passed to the 582 * constructor of the object including the 583 * filed. 584 * </ul> 585 * 586 * <p>If the initialized field is static, then the constructor does 587 * not receive any parameters. 588 * 589 * @param objectType the class instantiated for the initial value. 590 * 591 * @see javassist.CtField.Initializer#byNewArray(CtClass,int) 592 * @see javassist.CtField.Initializer#byNewArray(CtClass,int[]) 593 */ byNewWithParams(CtClass objectType)594 public static Initializer byNewWithParams(CtClass objectType) { 595 NewInitializer i = new NewInitializer(); 596 i.objectType = objectType; 597 i.stringParams = null; 598 i.withConstructorParams = true; 599 return i; 600 } 601 602 /** 603 * Makes an initializer creating a new object. 604 * 605 * <p>This initializer creates a new object and uses it as the initial 606 * value of the field. The constructor of the created object receives 607 * the parameters: 608 * 609 * <ul><code>Object obj</code> - the object including the field.<br> 610 * <code>String[] strs</code> - the character strings specified 611 * by <code>stringParams</code><br> 612 * <code>Object[] args</code> - the parameters passed to the 613 * constructor of the object including the 614 * filed. 615 * </ul> 616 * 617 * <p>If the initialized field is static, then the constructor receives 618 * only <code>strs</code>. 619 * 620 * @param objectType the class instantiated for the initial value. 621 * @param stringParams the array of strings passed to the 622 * constructor. 623 */ byNewWithParams(CtClass objectType, String[] stringParams)624 public static Initializer byNewWithParams(CtClass objectType, 625 String[] stringParams) { 626 NewInitializer i = new NewInitializer(); 627 i.objectType = objectType; 628 i.stringParams = stringParams; 629 i.withConstructorParams = true; 630 return i; 631 } 632 633 /** 634 * Makes an initializer calling a static method. 635 * 636 * <p>This initializer calls a static method and uses the returned 637 * value as the initial value of the field. 638 * The called method receives the parameters: 639 * 640 * <ul><code>Object obj</code> - the object including the field.<br> 641 * </ul> 642 * 643 * <p>If the initialized field is static, then the method does 644 * not receive any parameters. 645 * 646 * <p>The type of the returned value must be the same as the field 647 * type. 648 * 649 * @param methodClass the class that the static method is 650 * declared in. 651 * @param methodName the name of the satic method. 652 */ byCall(CtClass methodClass, String methodName)653 public static Initializer byCall(CtClass methodClass, 654 String methodName) { 655 MethodInitializer i = new MethodInitializer(); 656 i.objectType = methodClass; 657 i.methodName = methodName; 658 i.stringParams = null; 659 i.withConstructorParams = false; 660 return i; 661 } 662 663 /** 664 * Makes an initializer calling a static method. 665 * 666 * <p>This initializer calls a static method and uses the returned 667 * value as the initial value of the field. The called method 668 * receives the parameters: 669 * 670 * <ul><code>Object obj</code> - the object including the field.<br> 671 * <code>String[] strs</code> - the character strings specified 672 * by <code>stringParams</code><br> 673 * </ul> 674 * 675 * <p>If the initialized field is static, then the method 676 * receive only <code>strs</code>. 677 * 678 * <p>The type of the returned value must be the same as the field 679 * type. 680 * 681 * @param methodClass the class that the static method is 682 * declared in. 683 * @param methodName the name of the satic method. 684 * @param stringParams the array of strings passed to the 685 * static method. 686 */ byCall(CtClass methodClass, String methodName, String[] stringParams)687 public static Initializer byCall(CtClass methodClass, 688 String methodName, 689 String[] stringParams) { 690 MethodInitializer i = new MethodInitializer(); 691 i.objectType = methodClass; 692 i.methodName = methodName; 693 i.stringParams = stringParams; 694 i.withConstructorParams = false; 695 return i; 696 } 697 698 /** 699 * Makes an initializer calling a static method. 700 * 701 * <p>This initializer calls a static method and uses the returned 702 * value as the initial value of the field. The called method 703 * receives the parameters: 704 * 705 * <ul><code>Object obj</code> - the object including the field.<br> 706 * <code>Object[] args</code> - the parameters passed to the 707 * constructor of the object including the 708 * filed. 709 * </ul> 710 * 711 * <p>If the initialized field is static, then the method does 712 * not receive any parameters. 713 * 714 * <p>The type of the returned value must be the same as the field 715 * type. 716 * 717 * @param methodClass the class that the static method is 718 * declared in. 719 * @param methodName the name of the satic method. 720 */ byCallWithParams(CtClass methodClass, String methodName)721 public static Initializer byCallWithParams(CtClass methodClass, 722 String methodName) { 723 MethodInitializer i = new MethodInitializer(); 724 i.objectType = methodClass; 725 i.methodName = methodName; 726 i.stringParams = null; 727 i.withConstructorParams = true; 728 return i; 729 } 730 731 /** 732 * Makes an initializer calling a static method. 733 * 734 * <p>This initializer calls a static method and uses the returned 735 * value as the initial value of the field. The called method 736 * receives the parameters: 737 * 738 * <ul><code>Object obj</code> - the object including the field.<br> 739 * <code>String[] strs</code> - the character strings specified 740 * by <code>stringParams</code><br> 741 * <code>Object[] args</code> - the parameters passed to the 742 * constructor of the object including the 743 * filed. 744 * </ul> 745 * 746 * <p>If the initialized field is static, then the method 747 * receive only <code>strs</code>. 748 * 749 * <p>The type of the returned value must be the same as the field 750 * type. 751 * 752 * @param methodClass the class that the static method is 753 * declared in. 754 * @param methodName the name of the satic method. 755 * @param stringParams the array of strings passed to the 756 * static method. 757 */ byCallWithParams(CtClass methodClass, String methodName, String[] stringParams)758 public static Initializer byCallWithParams(CtClass methodClass, 759 String methodName, String[] stringParams) { 760 MethodInitializer i = new MethodInitializer(); 761 i.objectType = methodClass; 762 i.methodName = methodName; 763 i.stringParams = stringParams; 764 i.withConstructorParams = true; 765 return i; 766 } 767 768 /** 769 * Makes an initializer creating a new array. 770 * 771 * @param type the type of the array. 772 * @param size the size of the array. 773 * @throws NotFoundException if the type of the array components 774 * is not found. 775 */ byNewArray(CtClass type, int size)776 public static Initializer byNewArray(CtClass type, int size) 777 throws NotFoundException 778 { 779 return new ArrayInitializer(type.getComponentType(), size); 780 } 781 782 /** 783 * Makes an initializer creating a new multi-dimensional array. 784 * 785 * @param type the type of the array. 786 * @param sizes an <code>int</code> array of the size in every 787 * dimension. 788 * The first element is the size in the first 789 * dimension. The second is in the second, etc. 790 */ byNewArray(CtClass type, int[] sizes)791 public static Initializer byNewArray(CtClass type, int[] sizes) { 792 return new MultiArrayInitializer(type, sizes); 793 } 794 795 /** 796 * Makes an initializer. 797 * 798 * @param source initializer expression. 799 */ byExpr(String source)800 public static Initializer byExpr(String source) { 801 return new CodeInitializer(source); 802 } 803 byExpr(ASTree source)804 static Initializer byExpr(ASTree source) { 805 return new PtreeInitializer(source); 806 } 807 808 // Check whether this initializer is valid for the field type. 809 // If it is invaild, this method throws an exception. check(String desc)810 void check(String desc) throws CannotCompileException {} 811 812 // produce codes for initialization compile(CtClass type, String name, Bytecode code, CtClass[] parameters, Javac drv)813 abstract int compile(CtClass type, String name, Bytecode code, 814 CtClass[] parameters, Javac drv) 815 throws CannotCompileException; 816 817 // produce codes for initialization compileIfStatic(CtClass type, String name, Bytecode code, Javac drv)818 abstract int compileIfStatic(CtClass type, String name, 819 Bytecode code, Javac drv) throws CannotCompileException; 820 821 // returns the index of CONSTANT_Integer_info etc 822 // if the value is constant. Otherwise, 0. getConstantValue(ConstPool cp, CtClass type)823 int getConstantValue(ConstPool cp, CtClass type) { return 0; } 824 } 825 826 static abstract class CodeInitializer0 extends Initializer { compileExpr(Javac drv)827 abstract void compileExpr(Javac drv) throws CompileError; 828 compile(CtClass type, String name, Bytecode code, CtClass[] parameters, Javac drv)829 int compile(CtClass type, String name, Bytecode code, 830 CtClass[] parameters, Javac drv) 831 throws CannotCompileException 832 { 833 try { 834 code.addAload(0); 835 compileExpr(drv); 836 code.addPutfield(Bytecode.THIS, name, Descriptor.of(type)); 837 return code.getMaxStack(); 838 } 839 catch (CompileError e) { 840 throw new CannotCompileException(e); 841 } 842 } 843 compileIfStatic(CtClass type, String name, Bytecode code, Javac drv)844 int compileIfStatic(CtClass type, String name, Bytecode code, 845 Javac drv) throws CannotCompileException 846 { 847 try { 848 compileExpr(drv); 849 code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type)); 850 return code.getMaxStack(); 851 } 852 catch (CompileError e) { 853 throw new CannotCompileException(e); 854 } 855 } 856 getConstantValue2(ConstPool cp, CtClass type, ASTree tree)857 int getConstantValue2(ConstPool cp, CtClass type, ASTree tree) { 858 if (type.isPrimitive()) { 859 if (tree instanceof IntConst) { 860 long value = ((IntConst)tree).get(); 861 if (type == CtClass.doubleType) 862 return cp.addDoubleInfo((double)value); 863 else if (type == CtClass.floatType) 864 return cp.addFloatInfo((float)value); 865 else if (type == CtClass.longType) 866 return cp.addLongInfo(value); 867 else if (type != CtClass.voidType) 868 return cp.addIntegerInfo((int)value); 869 } 870 else if (tree instanceof DoubleConst) { 871 double value = ((DoubleConst)tree).get(); 872 if (type == CtClass.floatType) 873 return cp.addFloatInfo((float)value); 874 else if (type == CtClass.doubleType) 875 return cp.addDoubleInfo(value); 876 } 877 } 878 else if (tree instanceof StringL 879 && type.getName().equals(javaLangString)) 880 return cp.addStringInfo(((StringL)tree).get()); 881 882 return 0; 883 } 884 } 885 886 static class CodeInitializer extends CodeInitializer0 { 887 private String expression; 888 CodeInitializer(String expr)889 CodeInitializer(String expr) { expression = expr; } 890 compileExpr(Javac drv)891 void compileExpr(Javac drv) throws CompileError { 892 drv.compileExpr(expression); 893 } 894 getConstantValue(ConstPool cp, CtClass type)895 int getConstantValue(ConstPool cp, CtClass type) { 896 try { 897 ASTree t = Javac.parseExpr(expression, new SymbolTable()); 898 return getConstantValue2(cp, type, t); 899 } 900 catch (CompileError e) { 901 return 0; 902 } 903 } 904 } 905 906 static class PtreeInitializer extends CodeInitializer0 { 907 private ASTree expression; 908 PtreeInitializer(ASTree expr)909 PtreeInitializer(ASTree expr) { expression = expr; } 910 compileExpr(Javac drv)911 void compileExpr(Javac drv) throws CompileError { 912 drv.compileExpr(expression); 913 } 914 getConstantValue(ConstPool cp, CtClass type)915 int getConstantValue(ConstPool cp, CtClass type) { 916 return getConstantValue2(cp, type, expression); 917 } 918 } 919 920 /** 921 * A field initialized with a parameter passed to the constructor 922 * of the class containing that field. 923 */ 924 static class ParamInitializer extends Initializer { 925 int nthParam; 926 ParamInitializer()927 ParamInitializer() {} 928 compile(CtClass type, String name, Bytecode code, CtClass[] parameters, Javac drv)929 int compile(CtClass type, String name, Bytecode code, 930 CtClass[] parameters, Javac drv) 931 throws CannotCompileException 932 { 933 if (parameters != null && nthParam < parameters.length) { 934 code.addAload(0); 935 int nth = nthParamToLocal(nthParam, parameters, false); 936 int s = code.addLoad(nth, type) + 1; 937 code.addPutfield(Bytecode.THIS, name, Descriptor.of(type)); 938 return s; // stack size 939 } 940 else 941 return 0; // do not initialize 942 } 943 944 /** 945 * Computes the index of the local variable that the n-th parameter 946 * is assigned to. 947 * 948 * @param nth n-th parameter 949 * @param params list of parameter types 950 * @param isStatic true if the method is static. 951 */ nthParamToLocal(int nth, CtClass[] params, boolean isStatic)952 static int nthParamToLocal(int nth, CtClass[] params, 953 boolean isStatic) { 954 CtClass longType = CtClass.longType; 955 CtClass doubleType = CtClass.doubleType; 956 int k; 957 if (isStatic) 958 k = 0; 959 else 960 k = 1; // 0 is THIS. 961 962 for (int i = 0; i < nth; ++i) { 963 CtClass type = params[i]; 964 if (type == longType || type == doubleType) 965 k += 2; 966 else 967 ++k; 968 } 969 970 return k; 971 } 972 compileIfStatic(CtClass type, String name, Bytecode code, Javac drv)973 int compileIfStatic(CtClass type, String name, Bytecode code, 974 Javac drv) throws CannotCompileException 975 { 976 return 0; 977 } 978 } 979 980 /** 981 * A field initialized with an object created by the new operator. 982 */ 983 static class NewInitializer extends Initializer { 984 CtClass objectType; 985 String[] stringParams; 986 boolean withConstructorParams; 987 NewInitializer()988 NewInitializer() {} 989 990 /** 991 * Produces codes in which a new object is created and assigned to 992 * the field as the initial value. 993 */ compile(CtClass type, String name, Bytecode code, CtClass[] parameters, Javac drv)994 int compile(CtClass type, String name, Bytecode code, 995 CtClass[] parameters, Javac drv) 996 throws CannotCompileException 997 { 998 int stacksize; 999 1000 code.addAload(0); 1001 code.addNew(objectType); 1002 code.add(Bytecode.DUP); 1003 code.addAload(0); 1004 1005 if (stringParams == null) 1006 stacksize = 4; 1007 else 1008 stacksize = compileStringParameter(code) + 4; 1009 1010 if (withConstructorParams) 1011 stacksize += CtNewWrappedMethod.compileParameterList(code, 1012 parameters, 1); 1013 1014 code.addInvokespecial(objectType, "<init>", getDescriptor()); 1015 code.addPutfield(Bytecode.THIS, name, Descriptor.of(type)); 1016 return stacksize; 1017 } 1018 getDescriptor()1019 private String getDescriptor() { 1020 final String desc3 1021 = "(Ljava/lang/Object;[Ljava/lang/String;[Ljava/lang/Object;)V"; 1022 1023 if (stringParams == null) 1024 if (withConstructorParams) 1025 return "(Ljava/lang/Object;[Ljava/lang/Object;)V"; 1026 else 1027 return "(Ljava/lang/Object;)V"; 1028 else 1029 if (withConstructorParams) 1030 return desc3; 1031 else 1032 return "(Ljava/lang/Object;[Ljava/lang/String;)V"; 1033 } 1034 1035 /** 1036 * Produces codes for a static field. 1037 */ compileIfStatic(CtClass type, String name, Bytecode code, Javac drv)1038 int compileIfStatic(CtClass type, String name, Bytecode code, 1039 Javac drv) throws CannotCompileException 1040 { 1041 String desc; 1042 1043 code.addNew(objectType); 1044 code.add(Bytecode.DUP); 1045 1046 int stacksize = 2; 1047 if (stringParams == null) 1048 desc = "()V"; 1049 else { 1050 desc = "([Ljava/lang/String;)V"; 1051 stacksize += compileStringParameter(code); 1052 } 1053 1054 code.addInvokespecial(objectType, "<init>", desc); 1055 code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type)); 1056 return stacksize; 1057 } 1058 compileStringParameter(Bytecode code)1059 protected final int compileStringParameter(Bytecode code) 1060 throws CannotCompileException 1061 { 1062 int nparam = stringParams.length; 1063 code.addIconst(nparam); 1064 code.addAnewarray(javaLangString); 1065 for (int j = 0; j < nparam; ++j) { 1066 code.add(Bytecode.DUP); // dup 1067 code.addIconst(j); // iconst_<j> 1068 code.addLdc(stringParams[j]); // ldc ... 1069 code.add(Bytecode.AASTORE); // aastore 1070 } 1071 1072 return 4; 1073 } 1074 1075 } 1076 1077 /** 1078 * A field initialized with the result of a static method call. 1079 */ 1080 static class MethodInitializer extends NewInitializer { 1081 String methodName; 1082 // the method class is specified by objectType. 1083 MethodInitializer()1084 MethodInitializer() {} 1085 1086 /** 1087 * Produces codes in which a new object is created and assigned to 1088 * the field as the initial value. 1089 */ compile(CtClass type, String name, Bytecode code, CtClass[] parameters, Javac drv)1090 int compile(CtClass type, String name, Bytecode code, 1091 CtClass[] parameters, Javac drv) 1092 throws CannotCompileException 1093 { 1094 int stacksize; 1095 1096 code.addAload(0); 1097 code.addAload(0); 1098 1099 if (stringParams == null) 1100 stacksize = 2; 1101 else 1102 stacksize = compileStringParameter(code) + 2; 1103 1104 if (withConstructorParams) 1105 stacksize += CtNewWrappedMethod.compileParameterList(code, 1106 parameters, 1); 1107 1108 String typeDesc = Descriptor.of(type); 1109 String mDesc = getDescriptor() + typeDesc; 1110 code.addInvokestatic(objectType, methodName, mDesc); 1111 code.addPutfield(Bytecode.THIS, name, typeDesc); 1112 return stacksize; 1113 } 1114 getDescriptor()1115 private String getDescriptor() { 1116 final String desc3 1117 = "(Ljava/lang/Object;[Ljava/lang/String;[Ljava/lang/Object;)"; 1118 1119 if (stringParams == null) 1120 if (withConstructorParams) 1121 return "(Ljava/lang/Object;[Ljava/lang/Object;)"; 1122 else 1123 return "(Ljava/lang/Object;)"; 1124 else 1125 if (withConstructorParams) 1126 return desc3; 1127 else 1128 return "(Ljava/lang/Object;[Ljava/lang/String;)"; 1129 } 1130 1131 /** 1132 * Produces codes for a static field. 1133 */ compileIfStatic(CtClass type, String name, Bytecode code, Javac drv)1134 int compileIfStatic(CtClass type, String name, Bytecode code, 1135 Javac drv) throws CannotCompileException 1136 { 1137 String desc; 1138 1139 int stacksize = 1; 1140 if (stringParams == null) 1141 desc = "()"; 1142 else { 1143 desc = "([Ljava/lang/String;)"; 1144 stacksize += compileStringParameter(code); 1145 } 1146 1147 String typeDesc = Descriptor.of(type); 1148 code.addInvokestatic(objectType, methodName, desc + typeDesc); 1149 code.addPutstatic(Bytecode.THIS, name, typeDesc); 1150 return stacksize; 1151 } 1152 } 1153 1154 static class IntInitializer extends Initializer { 1155 int value; 1156 IntInitializer(int v)1157 IntInitializer(int v) { value = v; } 1158 check(String desc)1159 void check(String desc) throws CannotCompileException { 1160 char c = desc.charAt(0); 1161 if (c != 'I' && c != 'S' && c != 'B' && c != 'C' && c != 'Z') 1162 throw new CannotCompileException("type mismatch"); 1163 } 1164 compile(CtClass type, String name, Bytecode code, CtClass[] parameters, Javac drv)1165 int compile(CtClass type, String name, Bytecode code, 1166 CtClass[] parameters, Javac drv) 1167 throws CannotCompileException 1168 { 1169 code.addAload(0); 1170 code.addIconst(value); 1171 code.addPutfield(Bytecode.THIS, name, Descriptor.of(type)); 1172 return 2; // stack size 1173 } 1174 compileIfStatic(CtClass type, String name, Bytecode code, Javac drv)1175 int compileIfStatic(CtClass type, String name, Bytecode code, 1176 Javac drv) throws CannotCompileException 1177 { 1178 code.addIconst(value); 1179 code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type)); 1180 return 1; // stack size 1181 } 1182 getConstantValue(ConstPool cp, CtClass type)1183 int getConstantValue(ConstPool cp, CtClass type) { 1184 return cp.addIntegerInfo(value); 1185 } 1186 } 1187 1188 static class LongInitializer extends Initializer { 1189 long value; 1190 LongInitializer(long v)1191 LongInitializer(long v) { value = v; } 1192 check(String desc)1193 void check(String desc) throws CannotCompileException { 1194 if (!desc.equals("J")) 1195 throw new CannotCompileException("type mismatch"); 1196 } 1197 compile(CtClass type, String name, Bytecode code, CtClass[] parameters, Javac drv)1198 int compile(CtClass type, String name, Bytecode code, 1199 CtClass[] parameters, Javac drv) 1200 throws CannotCompileException 1201 { 1202 code.addAload(0); 1203 code.addLdc2w(value); 1204 code.addPutfield(Bytecode.THIS, name, Descriptor.of(type)); 1205 return 3; // stack size 1206 } 1207 compileIfStatic(CtClass type, String name, Bytecode code, Javac drv)1208 int compileIfStatic(CtClass type, String name, Bytecode code, 1209 Javac drv) throws CannotCompileException 1210 { 1211 code.addLdc2w(value); 1212 code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type)); 1213 return 2; // stack size 1214 } 1215 getConstantValue(ConstPool cp, CtClass type)1216 int getConstantValue(ConstPool cp, CtClass type) { 1217 if (type == CtClass.longType) 1218 return cp.addLongInfo(value); 1219 else 1220 return 0; 1221 } 1222 } 1223 1224 static class FloatInitializer extends Initializer { 1225 float value; 1226 FloatInitializer(float v)1227 FloatInitializer(float v) { value = v; } 1228 check(String desc)1229 void check(String desc) throws CannotCompileException { 1230 if (!desc.equals("F")) 1231 throw new CannotCompileException("type mismatch"); 1232 } 1233 compile(CtClass type, String name, Bytecode code, CtClass[] parameters, Javac drv)1234 int compile(CtClass type, String name, Bytecode code, 1235 CtClass[] parameters, Javac drv) 1236 throws CannotCompileException 1237 { 1238 code.addAload(0); 1239 code.addFconst(value); 1240 code.addPutfield(Bytecode.THIS, name, Descriptor.of(type)); 1241 return 3; // stack size 1242 } 1243 compileIfStatic(CtClass type, String name, Bytecode code, Javac drv)1244 int compileIfStatic(CtClass type, String name, Bytecode code, 1245 Javac drv) throws CannotCompileException 1246 { 1247 code.addFconst(value); 1248 code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type)); 1249 return 2; // stack size 1250 } 1251 getConstantValue(ConstPool cp, CtClass type)1252 int getConstantValue(ConstPool cp, CtClass type) { 1253 if (type == CtClass.floatType) 1254 return cp.addFloatInfo(value); 1255 else 1256 return 0; 1257 } 1258 } 1259 1260 static class DoubleInitializer extends Initializer { 1261 double value; 1262 DoubleInitializer(double v)1263 DoubleInitializer(double v) { value = v; } 1264 check(String desc)1265 void check(String desc) throws CannotCompileException { 1266 if (!desc.equals("D")) 1267 throw new CannotCompileException("type mismatch"); 1268 } 1269 compile(CtClass type, String name, Bytecode code, CtClass[] parameters, Javac drv)1270 int compile(CtClass type, String name, Bytecode code, 1271 CtClass[] parameters, Javac drv) 1272 throws CannotCompileException 1273 { 1274 code.addAload(0); 1275 code.addLdc2w(value); 1276 code.addPutfield(Bytecode.THIS, name, Descriptor.of(type)); 1277 return 3; // stack size 1278 } 1279 compileIfStatic(CtClass type, String name, Bytecode code, Javac drv)1280 int compileIfStatic(CtClass type, String name, Bytecode code, 1281 Javac drv) throws CannotCompileException 1282 { 1283 code.addLdc2w(value); 1284 code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type)); 1285 return 2; // stack size 1286 } 1287 getConstantValue(ConstPool cp, CtClass type)1288 int getConstantValue(ConstPool cp, CtClass type) { 1289 if (type == CtClass.doubleType) 1290 return cp.addDoubleInfo(value); 1291 else 1292 return 0; 1293 } 1294 } 1295 1296 static class StringInitializer extends Initializer { 1297 String value; 1298 StringInitializer(String v)1299 StringInitializer(String v) { value = v; } 1300 compile(CtClass type, String name, Bytecode code, CtClass[] parameters, Javac drv)1301 int compile(CtClass type, String name, Bytecode code, 1302 CtClass[] parameters, Javac drv) 1303 throws CannotCompileException 1304 { 1305 code.addAload(0); 1306 code.addLdc(value); 1307 code.addPutfield(Bytecode.THIS, name, Descriptor.of(type)); 1308 return 2; // stack size 1309 } 1310 compileIfStatic(CtClass type, String name, Bytecode code, Javac drv)1311 int compileIfStatic(CtClass type, String name, Bytecode code, 1312 Javac drv) throws CannotCompileException 1313 { 1314 code.addLdc(value); 1315 code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type)); 1316 return 1; // stack size 1317 } 1318 getConstantValue(ConstPool cp, CtClass type)1319 int getConstantValue(ConstPool cp, CtClass type) { 1320 if (type.getName().equals(javaLangString)) 1321 return cp.addStringInfo(value); 1322 else 1323 return 0; 1324 } 1325 } 1326 1327 static class ArrayInitializer extends Initializer { 1328 CtClass type; 1329 int size; 1330 ArrayInitializer(CtClass t, int s)1331 ArrayInitializer(CtClass t, int s) { type = t; size = s; } 1332 addNewarray(Bytecode code)1333 private void addNewarray(Bytecode code) { 1334 if (type.isPrimitive()) 1335 code.addNewarray(((CtPrimitiveType)type).getArrayType(), 1336 size); 1337 else 1338 code.addAnewarray(type, size); 1339 } 1340 compile(CtClass type, String name, Bytecode code, CtClass[] parameters, Javac drv)1341 int compile(CtClass type, String name, Bytecode code, 1342 CtClass[] parameters, Javac drv) 1343 throws CannotCompileException 1344 { 1345 code.addAload(0); 1346 addNewarray(code); 1347 code.addPutfield(Bytecode.THIS, name, Descriptor.of(type)); 1348 return 2; // stack size 1349 } 1350 compileIfStatic(CtClass type, String name, Bytecode code, Javac drv)1351 int compileIfStatic(CtClass type, String name, Bytecode code, 1352 Javac drv) throws CannotCompileException 1353 { 1354 addNewarray(code); 1355 code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type)); 1356 return 1; // stack size 1357 } 1358 } 1359 1360 static class MultiArrayInitializer extends Initializer { 1361 CtClass type; 1362 int[] dim; 1363 MultiArrayInitializer(CtClass t, int[] d)1364 MultiArrayInitializer(CtClass t, int[] d) { type = t; dim = d; } 1365 check(String desc)1366 void check(String desc) throws CannotCompileException { 1367 if (desc.charAt(0) != '[') 1368 throw new CannotCompileException("type mismatch"); 1369 } 1370 compile(CtClass type, String name, Bytecode code, CtClass[] parameters, Javac drv)1371 int compile(CtClass type, String name, Bytecode code, 1372 CtClass[] parameters, Javac drv) 1373 throws CannotCompileException 1374 { 1375 code.addAload(0); 1376 int s = code.addMultiNewarray(type, dim); 1377 code.addPutfield(Bytecode.THIS, name, Descriptor.of(type)); 1378 return s + 1; // stack size 1379 } 1380 compileIfStatic(CtClass type, String name, Bytecode code, Javac drv)1381 int compileIfStatic(CtClass type, String name, Bytecode code, 1382 Javac drv) throws CannotCompileException 1383 { 1384 int s = code.addMultiNewarray(type, dim); 1385 code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type)); 1386 return s; // stack size 1387 } 1388 } 1389 } 1390