001    package serp.bytecode;
002    
003    import java.io.*;
004    import java.lang.reflect.*;
005    
006    import serp.bytecode.lowlevel.*;
007    import serp.bytecode.visitor.*;
008    import 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     */
016    public 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    }