• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 (&gt;= 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