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
16package javassist;
17
18import javassist.bytecode.*;
19import javassist.compiler.Javac;
20import javassist.compiler.SymbolTable;
21import javassist.compiler.CompileError;
22import javassist.compiler.ast.ASTree;
23import javassist.compiler.ast.IntConst;
24import javassist.compiler.ast.DoubleConst;
25import javassist.compiler.ast.StringL;
26
27/**
28 * An instance of CtField represents a field.
29 *
30 * @see CtClass#getDeclaredFields()
31 */
32public 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