View Javadoc

1   package serp.bytecode;
2   
3   import java.io.*;
4   import java.lang.reflect.*;
5   
6   import serp.bytecode.lowlevel.*;
7   import serp.bytecode.visitor.*;
8   import serp.util.*;
9   
10  /***
11   * Instruction that takes as an argument a field to operate
12   * on. Examples include <code>getfield, getstatic, setfield, setstatic</code>.
13   *
14   * @author Abe White
15   */
16  public abstract class FieldInstruction extends Instruction {
17      private int _index = 0;
18  
19      FieldInstruction(Code owner, int opcode) {
20          super(owner, opcode);
21      }
22  
23      int getLength() {
24          return super.getLength() + 2;
25      }
26  
27      ////////////////////
28      // Field operations
29      ////////////////////
30  
31      /***
32       * Return the index in the class {@link ConstantPool} of the
33       * {@link ComplexEntry} describing the field to operate on.
34       */
35      public int getFieldIndex() {
36          return _index;
37      }
38  
39      /***
40       * Set the index in the class {@link ConstantPool} of the
41       * {@link ComplexEntry} describing the field to operate on.
42       *
43       * @return this instruction, for method chaining
44       */
45      public FieldInstruction setFieldIndex(int index) {
46          _index = index;
47          return this;
48      }
49  
50      /***
51       * Return the field this instruction operates on, or null if not set.
52       */
53      public BCField getField() {
54          String dec = getFieldDeclarerName();
55          if (dec == null) 
56              return null;
57  
58          BCClass bc = getProject().loadClass(dec, getClassLoader());
59          BCField[] fields = bc.getFields(getFieldName());
60          if (fields.length == 0)
61              return null;
62          return fields[0];
63      }
64  
65      /***
66       * Set the field this instruction operates on.
67       *
68       * @return this instruction, for method chaining
69       */
70      public FieldInstruction setField(BCField field) {
71          if (field == null)
72              return setFieldIndex(0);
73          return setField(field.getDeclarer().getName(), field.getName(),
74              field.getTypeName());
75      }
76  
77      /***
78       * Set the field this instruction operates on.
79       *
80       * @return this instruction, for method chaining
81       */
82      public FieldInstruction setField(Field field) {
83          if (field == null)
84              return setFieldIndex(0);
85          return setField(field.getDeclaringClass(), field.getName(),
86              field.getType());
87      }
88  
89      /***
90       * Set the field this instruction operates on.
91       *
92       * @param dec the full class name of the field's declaring class
93       * @param name the field name
94       * @param type the full class name of the field type
95       * @return this instruction, for method chaining
96       */
97      public FieldInstruction setField(String dec, String name, String type) {
98          if (dec == null && name == null && type == null)
99              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 }