001package serp.bytecode;
002
003import java.io.*;
004import java.lang.reflect.*;
005
006import serp.bytecode.lowlevel.*;
007import serp.bytecode.visitor.*;
008import serp.util.*;
009
010/**
011 * Instruction that takes as an argument a field to operate
012 * on. Examples include <code>getfield, getstatic, setfield, setstatic</code>.
013 *
014 * @author Abe White
015 */
016public abstract class FieldInstruction extends Instruction {
017    private int _index = 0;
018
019    FieldInstruction(Code owner, int opcode) {
020        super(owner, opcode);
021    }
022
023    int getLength() {
024        return super.getLength() + 2;
025    }
026
027    ////////////////////
028    // Field operations
029    ////////////////////
030
031    /**
032     * Return the index in the class {@link ConstantPool} of the
033     * {@link ComplexEntry} describing the field to operate on.
034     */
035    public int getFieldIndex() {
036        return _index;
037    }
038
039    /**
040     * Set the index in the class {@link ConstantPool} of the
041     * {@link ComplexEntry} describing the field to operate on.
042     *
043     * @return this instruction, for method chaining
044     */
045    public FieldInstruction setFieldIndex(int index) {
046        _index = index;
047        return this;
048    }
049
050    /**
051     * Return the field this instruction operates on, or null if not set.
052     */
053    public BCField getField() {
054        String dec = getFieldDeclarerName();
055        if (dec == null) 
056            return null;
057
058        BCClass bc = getProject().loadClass(dec, getClassLoader());
059        BCField[] fields = bc.getFields(getFieldName());
060        if (fields.length == 0)
061            return null;
062        return fields[0];
063    }
064
065    /**
066     * Set the field this instruction operates on.
067     *
068     * @return this instruction, for method chaining
069     */
070    public FieldInstruction setField(BCField field) {
071        if (field == null)
072            return setFieldIndex(0);
073        return setField(field.getDeclarer().getName(), field.getName(),
074            field.getTypeName());
075    }
076
077    /**
078     * Set the field this instruction operates on.
079     *
080     * @return this instruction, for method chaining
081     */
082    public FieldInstruction setField(Field field) {
083        if (field == null)
084            return setFieldIndex(0);
085        return setField(field.getDeclaringClass(), field.getName(),
086            field.getType());
087    }
088
089    /**
090     * Set the field this instruction operates on.
091     *
092     * @param dec the full class name of the field's declaring class
093     * @param name the field name
094     * @param type the full class name of the field type
095     * @return this instruction, for method chaining
096     */
097    public FieldInstruction setField(String dec, String name, String type) {
098        if (dec == null && name == null && type == null)
099            return setFieldIndex(0);
100        if (dec == null)
101            dec = "";
102        if (name == null)
103            name = "";
104        if (type == null)
105            type = "";
106
107        dec = getProject().getNameCache().getInternalForm(dec, false);
108        type = getProject().getNameCache().getInternalForm(type, true);
109        return setFieldIndex(getPool().findFieldEntry(dec, name, type, true));
110    }
111
112    /**
113     * Set the field this instruction operates on, for fields that are
114     * declared by the current class.
115     *
116     * @param name the field name
117     * @param type the full class name of the field type
118     * @return this instruction, for method chaining
119     */
120    public FieldInstruction setField(String name, String type) {
121        BCClass owner = getCode().getMethod().getDeclarer();
122        return setField(owner.getName(), name, type);
123    }
124
125    /**
126     * Set the field this instruction operates on.
127     *
128     * @param dec the field's declaring class
129     * @param name the field name
130     * @param type the class of the field type
131     * @return this instruction, for method chaining
132     */
133    public FieldInstruction setField(Class dec, String name, Class type) {
134        String decName = (dec == null) ? null : dec.getName();
135        String typeName = (type == null) ? null : type.getName();
136        return setField(decName, name, typeName);
137    }
138
139    /**
140     * Set the field this instruction operates on, for fields that are
141     * declared by the current class.
142     *
143     * @param name the field name
144     * @param type the class of the field type
145     * @return this instruction, for method chaining
146     */
147    public FieldInstruction setField(String name, Class type) {
148        BCClass owner = getCode().getMethod().getDeclarer();
149        String typeName = (type == null) ? null : type.getName();
150        return setField(owner.getName(), name, typeName);
151    }
152
153    /**
154     * Set the field this instruction operates on.
155     *
156     * @param dec the field's declaring class
157     * @param name the field name
158     * @param type the class of the field type
159     * @return this instruction, for method chaining
160     */
161    public FieldInstruction setField(BCClass dec, String name, BCClass type) {
162        String decName = (dec == null) ? null : dec.getName();
163        String typeName = (type == null) ? null : type.getName();
164        return setField(decName, name, typeName);
165    }
166
167    /**
168     * Set the field this instruction operates on, for fields that are
169     * declared by the current class.
170     *
171     * @param name the field name
172     * @param type the class of the field type
173     * @return this instruction, for method chaining
174     */
175    public FieldInstruction setField(String name, BCClass type) {
176        BCClass owner = getCode().getMethod().getDeclarer();
177        String typeName = (type == null) ? null : type.getName();
178        return setField(owner.getName(), name, typeName);
179    }
180
181    ////////////////////////////////
182    // Name, Type, Owner operations
183    ////////////////////////////////
184
185    /**
186     * Return the name of the field this instruction operates on, or null
187     * if not set.
188     */
189    public String getFieldName() {
190        int index = getFieldIndex();
191        if (index == 0)
192            return null;
193
194        ComplexEntry entry = (ComplexEntry) getPool().getEntry(index);
195        String name = entry.getNameAndTypeEntry().getNameEntry().getValue();
196        if (name.length() == 0)
197            return null;
198        return name;
199    }
200
201    /**
202     * Set the name of the field this instruction operates on.
203     *
204     * @return this instruction, for method chaining
205     */
206    public FieldInstruction setFieldName(String name) {
207        return setField(getFieldDeclarerName(), name, getFieldTypeName());
208    }
209
210    /**
211     * Return the type of the field this instruction operates on, or null
212     * if not set.
213     */
214    public String getFieldTypeName() {
215        int index = getFieldIndex();
216        if (index == 0)
217            return null;
218
219        ComplexEntry entry = (ComplexEntry) getPool().getEntry(index);
220        String name = getProject().getNameCache().getExternalForm(entry.
221            getNameAndTypeEntry().getDescriptorEntry().getValue(), false);
222        if (name.length() == 0)
223            return null;
224        return name;
225    }
226
227    /**
228     * Return the type of the field this instruction operates on, or null
229     * if not set.
230     */
231    public Class getFieldType() {
232        String type = getFieldTypeName();
233        if (type == null)
234            return null;
235        return Strings.toClass(type, getClassLoader());
236    }
237
238    /**
239     * Return the type of the field this instruction operates on, or null
240     * if not set.
241     */
242    public BCClass getFieldTypeBC() {
243        String type = getFieldTypeName();
244        if (type == null)
245            return null;
246        return getProject().loadClass(type, getClassLoader());
247    }
248
249    /**
250     * Set the type of the field this instruction operates on.
251     *
252     * @return this instruction, for method chaining
253     */
254    public FieldInstruction setFieldType(String type) {
255        return setField(getFieldDeclarerName(), getFieldName(), type);
256    }
257
258    /**
259     * Set the type of the field this instruction operates on.
260     *
261     * @return this instruction, for method chaining
262     */
263    public FieldInstruction setFieldType(Class type) {
264        String name = null;
265        if (type != null)
266            name = type.getName();
267        return setFieldType(name);
268    }
269
270    /**
271     * Set the type of the field this instruction operates on.
272     *
273     * @return this instruction, for method chaining
274     */
275    public FieldInstruction setFieldType(BCClass type) {
276        String name = null;
277        if (type != null)
278            name = type.getName();
279        return setFieldType(name);
280    }
281
282    /**
283     * Return the declaring class of the field this instruction operates on,
284     * or null if not set.
285     */
286    public String getFieldDeclarerName() {
287        int index = getFieldIndex();
288        if (index == 0)
289            return null;
290
291        ComplexEntry entry = (ComplexEntry) getPool().getEntry(index);
292        String name = getProject().getNameCache().getExternalForm(entry.
293            getClassEntry().getNameEntry().getValue(), false);
294        if (name.length() == 0)
295            return null;
296        return name;
297    }
298
299    /**
300     * Return the declaring class of the field this instruction operates on,
301     * or null if not set.
302     */
303    public Class getFieldDeclarerType() {
304        String type = getFieldDeclarerName();
305        if (type == null)
306            return null;
307        return Strings.toClass(type, getClassLoader());
308    }
309
310    /**
311     * Return the declaring class of the field this instruction operates on,
312     * or null if not set.
313     */
314    public BCClass getFieldDeclarerBC() {
315        String type = getFieldDeclarerName();
316        if (type == null)
317            return null;
318        return getProject().loadClass(type, getClassLoader());
319    }
320
321    /**
322     * Set the declaring class of the field this instruction operates on.
323     *
324     * @return this instruction, for method chaining
325     */
326    public FieldInstruction setFieldDeclarer(String type) {
327        return setField(type, getFieldName(), getFieldTypeName());
328    }
329
330    /**
331     * Set the declaring class of the field this instruction operates on.
332     *
333     * @return this instruction, for method chaining
334     */
335    public FieldInstruction setFieldDeclarer(Class type) {
336        String name = null;
337        if (type != null)
338            name = type.getName();
339        return setFieldDeclarer(name);
340    }
341
342    /**
343     * Set the declaring class of the field this instruction operates on.
344     *
345     * @return this instruction, for method chaining
346     */
347    public FieldInstruction setFieldDeclarer(BCClass type) {
348        String name = null;
349        if (type != null)
350            name = type.getName();
351        return setFieldDeclarer(name);
352    }
353
354    /**
355     * FieldInstructions are equal if the field they reference is the same,
356     * or if the field of either is unset.
357     */
358    public boolean equalsInstruction(Instruction other) {
359        if (other == this)
360            return true;
361        if (!(other instanceof FieldInstruction))
362            return false;
363        if (!super.equalsInstruction(other))
364            return false;
365
366        FieldInstruction ins = (FieldInstruction) other;
367        String s1 = getFieldName();
368        String s2 = ins.getFieldName();
369        if (!(s1 == null || s2 == null || s1.equals(s2)))
370            return false;
371
372        s1 = getFieldTypeName();
373        s2 = ins.getFieldTypeName();
374        if (!(s1 == null || s2 == null || s1.equals(s2)))
375            return false;
376
377        s1 = getFieldDeclarerName();
378        s2 = ins.getFieldDeclarerName();
379        if (!(s1 == null || s2 == null || s1.equals(s2)))
380            return false;
381        return true;
382    }
383
384    void read(Instruction orig) {
385        super.read(orig);
386        FieldInstruction ins = (FieldInstruction) orig;
387        setField(ins.getFieldDeclarerName(), ins.getFieldName(),
388            ins.getFieldTypeName());
389    }
390
391    void read(DataInput in) throws IOException {
392        super.read(in);
393        setFieldIndex(in.readUnsignedShort());
394    }
395
396    void write(DataOutput out) throws IOException {
397        super.write(out);
398        out.writeShort(getFieldIndex());
399    }
400}