Home | History | Annotate | Download | only in javassist
      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      */
     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      */
     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 
     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 
    108     CtField(FieldInfo fi, CtClass clazz) {
    109         super(clazz);
    110         fieldInfo = fi;
    111     }
    112 
    113     /**
    114      * Returns a String representation of the object.
    115      */
    116     public String toString() {
    117         return getDeclaringClass().getName() + "." + getName()
    118                + ":" + fieldInfo.getDescriptor();
    119     }
    120 
    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      */
    130     protected ASTree getInitAST() { return null; }
    131 
    132     /* Called by CtClassType.addField().
    133      */
    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      */
    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      */
    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      */
    199     public FieldInfo getFieldInfo2() { return fieldInfo; }
    200 
    201     /**
    202      * Returns the class declaring the field.
    203      */
    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      */
    212     public String getName() {
    213         return fieldInfo.getName();
    214     }
    215 
    216     /**
    217      * Changes the name of the field.
    218      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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 
    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      */
    338     public String getSignature() {
    339         return fieldInfo.getDescriptor();
    340     }
    341 
    342     /**
    343      * Returns the type of the field.
    344      */
    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      */
    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      */
    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      */
    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      */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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 (&gt;= 0) parameter is used as
    512          *                      the initial value.
    513          *                      If nth is 0, then the first parameter is
    514          *                      used.
    515          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    800         public static Initializer byExpr(String source) {
    801             return new CodeInitializer(source);
    802         }
    803 
    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.
    810         void check(String desc) throws CannotCompileException {}
    811 
    812         // produce codes for initialization
    813         abstract int compile(CtClass type, String name, Bytecode code,
    814                              CtClass[] parameters, Javac drv)
    815             throws CannotCompileException;
    816 
    817         // produce codes for initialization
    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.
    823         int getConstantValue(ConstPool cp, CtClass type) { return 0; }
    824     }
    825 
    826     static abstract class CodeInitializer0 extends Initializer {
    827         abstract void compileExpr(Javac drv) throws CompileError;
    828 
    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 
    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 
    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 
    889         CodeInitializer(String expr) { expression = expr; }
    890 
    891         void compileExpr(Javac drv) throws CompileError {
    892             drv.compileExpr(expression);
    893         }
    894 
    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 
    909         PtreeInitializer(ASTree expr) { expression = expr; }
    910 
    911         void compileExpr(Javac drv) throws CompileError {
    912             drv.compileExpr(expression);
    913         }
    914 
    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 
    927         ParamInitializer() {}
    928 
    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          */
    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 
    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 
    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          */
    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 
   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          */
   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 
   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 
   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          */
   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 
   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          */
   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 
   1157         IntInitializer(int v) { value = v; }
   1158 
   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 
   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 
   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 
   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 
   1191         LongInitializer(long v) { value = v; }
   1192 
   1193         void check(String desc) throws CannotCompileException {
   1194             if (!desc.equals("J"))
   1195                 throw new CannotCompileException("type mismatch");
   1196         }
   1197 
   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 
   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 
   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 
   1227         FloatInitializer(float v) { value = v; }
   1228 
   1229         void check(String desc) throws CannotCompileException {
   1230             if (!desc.equals("F"))
   1231                 throw new CannotCompileException("type mismatch");
   1232         }
   1233 
   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 
   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 
   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 
   1263         DoubleInitializer(double v) { value = v; }
   1264 
   1265         void check(String desc) throws CannotCompileException {
   1266             if (!desc.equals("D"))
   1267                 throw new CannotCompileException("type mismatch");
   1268         }
   1269 
   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 
   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 
   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 
   1299         StringInitializer(String v) { value = v; }
   1300 
   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 
   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 
   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 
   1331         ArrayInitializer(CtClass t, int s) { type = t; size = s; }
   1332 
   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 
   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 
   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 
   1364         MultiArrayInitializer(CtClass t, int[] d) { type = t; dim = d; }
   1365 
   1366         void check(String desc) throws CannotCompileException {
   1367             if (desc.charAt(0) != '[')
   1368                 throw new CannotCompileException("type mismatch");
   1369         }
   1370 
   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 
   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