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   * An instruction that invokes a method.
12   *
13   * @author Abe White
14   */
15  public class MethodInstruction extends Instruction {
16      private int _index = 0;
17  
18      MethodInstruction(Code owner, int opcode) {
19          super(owner, opcode);
20      }
21  
22      int getLength() {
23          if (getOpcode() == Constants.INVOKEINTERFACE)
24              return super.getLength() + 4;
25          return super.getLength() + 2;
26      }
27  
28      public int getLogicalStackChange() {
29          String ret = getMethodReturnName();
30          if (ret == null)
31              return 0;
32  
33          // subtract a stack pos for the this ptr
34          int stack = 0;
35          if (getOpcode() != Constants.INVOKESTATIC)
36              stack--;
37  
38          // and for each arg
39          String[] params = getMethodParamNames();
40          for (int i = 0; i < params.length; i++)
41              stack--;
42  
43          // add for the return value, if any
44          if (!void.class.getName().equals(ret))
45              stack++;
46          return stack;
47      }
48  
49      public int getStackChange() {
50          String ret = getMethodReturnName();
51          if (ret == null)
52              return 0;
53  
54          // subtract a stack pos for the this ptr
55          int stack = 0;
56          if (getOpcode() != Constants.INVOKESTATIC)
57              stack--;
58  
59          // and for each arg (2 for longs, doubles)
60          String[] params = getMethodParamNames();
61          for (int i = 0; i < params.length; i++, stack--)
62              if (long.class.getName().equals(params[i]) 
63                  || double.class.getName().equals(params[i]))
64                  stack--;
65  
66          // add for the return value, if any
67          if (!void.class.getName().equals(ret))
68              stack++;
69          if (long.class.getName().equals(ret) 
70              || double.class.getName().equals(ret))
71              stack++;
72          return stack;
73      }
74  
75      /////////////////////
76      // Method operations
77      /////////////////////
78  
79      /***
80       * Return the index in the class {@link ConstantPool} of the
81       * {@link ComplexEntry} describing the method to operate on.
82       */
83      public int getMethodIndex() {
84          return _index;
85      }
86  
87      /***
88       * Set the index in the class {@link ConstantPool} of the
89       * {@link ComplexEntry} describing the method to operate on.
90       *
91       * @return this instruction, for method chaining
92       */
93      public MethodInstruction setMethodIndex(int index) {
94          _index = index;
95          return this;
96      }
97  
98      /***
99       * Return the method this instruction operates on, or null if not set.
100      */
101     public BCMethod getMethod() {
102         String dec = getMethodDeclarerName();
103         if (dec == null)
104             return null;
105 
106         BCClass bc = getProject().loadClass(dec, getClassLoader());
107         BCMethod[] meths = bc.getMethods(getMethodName(),getMethodParamNames());
108         if (meths.length == 0)
109             return null;
110         return meths[0];
111     }
112 
113     /***
114      * Set the method this instruction operates on.
115      *
116      * @return this instruction, for method chaining
117      */
118     public MethodInstruction setMethod(BCMethod method) {
119         if (method == null)
120             return setMethodIndex(0);
121         return setMethod(method.getDeclarer().getName(), method.getName(),
122             method.getReturnName(), method.getParamNames(), false);
123     }
124 
125     /***
126      * Set the method this instruction operates on.
127      *
128      * @return this instruction, for method chaining
129      */
130     public MethodInstruction setMethod(Method method) {
131         if (method == null)
132             return setMethodIndex(0);
133         return setMethod(method.getDeclaringClass(), method.getName(),
134             method.getReturnType(), method.getParameterTypes());
135     }
136 
137     /***
138      * Set the method this instruction operates on.
139      *
140      * @return this instruction, for method chaining
141      */
142     public MethodInstruction setMethod(Constructor method) {
143         if (method == null)
144             return setMethodIndex(0);
145         setOpcode(Constants.INVOKESPECIAL);
146         return setMethod(method.getDeclaringClass(), "<init>", void.class,
147             method.getParameterTypes());
148     }
149 
150     /***
151      * Set the method this instruction operates on.
152      *
153      * @param dec the full class name of the method's declaring class
154      * @param name the method name
155      * @param returnType the full class name of the method return type
156      * @param param the full class names of the method param types
157      * @return this instruction, for method chaining
158      */
159     public MethodInstruction setMethod(String dec, String name,
160         String returnType, String[] params) {
161         return setMethod(dec, name, returnType, params, true);
162     }
163 
164     /***
165      * Set the method this instruction operates on.
166      *
167      * @param dec the full class name of the method's declaring class
168      * @param name the method name
169      * @param returnType the full class name of the method return type
170      * @param param the full class names of the method param types
171      * @param copy whether to copy the the parameter array
172      * @return this instruction, for method chaining
173      */
174     private MethodInstruction setMethod(String dec, String name,
175         String returnType, String[] params, boolean copy) {
176         if (name == null && returnType == null && dec == null 
177             && (params == null || params.length == 0))
178             return setMethodIndex(0);
179 
180         if (dec == null)
181             dec = "";
182         if (name == null)
183             name = "";
184         if (returnType == null)
185             returnType = "";
186         if (params == null)
187             params = new String[0];
188         else if (copy) {
189             String[] pcopy = new String[params.length];
190             System.arraycopy(params, 0, pcopy, 0, params.length);
191             params = pcopy;
192         }
193 
194         NameCache cache = getProject().getNameCache();
195         returnType = cache.getInternalForm(returnType, true);
196         dec = cache.getInternalForm(dec, false);
197         for (int i = 0; i < params.length; i++)
198             params[i] = cache.getInternalForm(params[i], true);
199 
200         String desc = cache.getDescriptor(returnType, params);
201         if (getOpcode() == Constants.INVOKEINTERFACE)
202             return setMethodIndex(getPool().findInterfaceMethodEntry(dec, name,
203                 desc, true));
204         return setMethodIndex(getPool().findMethodEntry(dec, name, desc, true));
205     }
206 
207     /***
208      * Set the method this instruction operates on, for methods that are
209      * declared by the current class.
210      *
211      * @param name the method name
212      * @param returnType the full class name of the method return type
213      * @param param the full class names of the method param types
214      * @return this instruction, for method chaining
215      */
216     public MethodInstruction setMethod(String name, String returnType,
217         String[] params) {
218         BCClass owner = getCode().getMethod().getDeclarer();
219         return setMethod(owner.getName(), name, returnType, params);
220     }
221 
222     /***
223      * Set the method this instruction operates on.
224      *
225      * @param dec the method's declaring class
226      * @param name the method name
227      * @param returnType the class of the method return type
228      * @param param the class of the method param types
229      * @return this instruction, for method chaining
230      */
231     public MethodInstruction setMethod(Class dec, String name,
232         Class returnType, Class[] params) {
233         String decName = (dec == null) ? null : dec.getName();
234         String returnName = (returnType == null) ? null : returnType.getName();
235         String[] paramNames = null;
236         if (params != null) {
237             paramNames = new String[params.length];
238             for (int i = 0; i < params.length; i++)
239                 paramNames[i] = params[i].getName();
240         }
241         return setMethod(decName, name, returnName, paramNames, false);
242     }
243 
244     /***
245      * Set the method this instruction operates on, for methods that are
246      * declared by the current class.
247      *
248      * @param name the method name
249      * @param returnType the class of the method return type
250      * @param param the class of the method param types
251      * @return this instruction, for method chaining
252      */
253     public MethodInstruction setMethod(String name, Class returnType,
254         Class[] params) {
255         BCClass owner = getCode().getMethod().getDeclarer();
256         String returnName = (returnType == null) ? null : returnType.getName();
257         String[] paramNames = null;
258         if (params != null) {
259             paramNames = new String[params.length];
260             for (int i = 0; i < params.length; i++)
261                 paramNames[i] = params[i].getName();
262         }
263         return setMethod(owner.getName(), name, returnName, paramNames, false);
264     }
265 
266     /***
267      * Set the method this instruction operates on.
268      *
269      * @param dec the method's declaring class
270      * @param name the method name
271      * @param returnType the class of the method return type
272      * @param param the class of the method param types
273      * @return this instruction, for method chaining
274      */
275     public MethodInstruction setMethod(BCClass dec, String name,
276         BCClass returnType, BCClass[] params) {
277         String decName = (dec == null) ? null : dec.getName();
278         String returnName = (returnType == null) ? null : returnType.getName();
279         String[] paramNames = null;
280         if (params != null) {
281             paramNames = new String[params.length];
282             for (int i = 0; i < params.length; i++)
283                 paramNames[i] = params[i].getName();
284         }
285         return setMethod(decName, name, returnName, paramNames, false);
286     }
287 
288     /***
289      * Set the method this instruction operates on, for methods that are
290      * declared by the current class.
291      *
292      * @param name the method name
293      * @param returnType the class of the method return type
294      * @param param the class of the method param types
295      * @return this instruction, for method chaining
296      */
297     public MethodInstruction setMethod(String name, BCClass returnType,
298         BCClass[] params) {
299         BCClass owner = getCode().getMethod().getDeclarer();
300         String returnName = (returnType == null) ? null : returnType.getName();
301         String[] paramNames = null;
302         if (params != null) {
303             paramNames = new String[params.length];
304             for (int i = 0; i < params.length; i++)
305                 paramNames[i] = params[i].getName();
306         }
307         return setMethod(owner.getName(), name, returnName, paramNames, false);
308     }
309 
310     /////////////////////////////////////////
311     // Name, Return, Param, Owner operations
312     /////////////////////////////////////////
313 
314     /***
315      * Return the name of the method this instruction operates on, or null
316      * if not set.
317      */
318     public String getMethodName() {
319         if (_index == 0)
320             return null;
321 
322         ComplexEntry entry = (ComplexEntry) getPool().getEntry(_index);
323         String name = entry.getNameAndTypeEntry().getNameEntry().getValue();
324         if (name.length() == 0)
325             return null;
326         return name;
327     }
328 
329     /***
330      * Set the name of the method this instruction operates on.
331      *
332      * @return this instruction, for method chaining
333      */
334     public MethodInstruction setMethodName(String name) {
335         return setMethod(getMethodDeclarerName(), name, getMethodReturnName(),
336             getMethodParamNames());
337     }
338 
339     /***
340      * Return the return type of the method this instruction operates on,
341      * or null if not set.
342      */
343     public String getMethodReturnName() {
344         if (_index == 0)
345             return null;
346 
347         ComplexEntry entry = (ComplexEntry) getPool().getEntry(_index);
348         String desc = entry.getNameAndTypeEntry().getDescriptorEntry().
349             getValue();
350         NameCache cache = getProject().getNameCache();
351         String name = cache.getExternalForm(cache.getDescriptorReturnName(desc),
352             false);
353         if (name.length() == 0)
354             return null;
355         return name;
356     }
357 
358     /***
359      * Return the return type of the method this instruction operates on,
360      * or null if not set.
361      */
362     public Class getMethodReturnType() {
363         String type = getMethodReturnName();
364         if (type == null)
365             return null;
366         return Strings.toClass(type, getClassLoader());
367     }
368 
369     /***
370      * Return the return type of the method this instruction operates on,
371      * or null if not set.
372      */
373     public BCClass getMethodReturnBC() {
374         String type = getMethodReturnName();
375         if (type == null)
376             return null;
377         return getProject().loadClass(type, getClassLoader());
378     }
379 
380     /***
381      * Set the return type of the method this instruction operates on.
382      *
383      * @return this instruction, for method chaining
384      */
385     public MethodInstruction setMethodReturn(String type) {
386         return setMethod(getMethodDeclarerName(), getMethodName(), type,
387             getMethodParamNames());
388     }
389 
390     /***
391      * Set the return type of the method this instruction operates on.
392      *
393      * @return this instruction, for method chaining
394      */
395     public MethodInstruction setMethodReturn(Class type) {
396         String name = null;
397         if (type != null)
398             name = type.getName();
399         return setMethodReturn(name);
400     }
401 
402     /***
403      * Set the return type of the method this instruction operates on.
404      *
405      * @return this instruction, for method chaining
406      */
407     public MethodInstruction setMethodReturn(BCClass type) {
408         String name = null;
409         if (type != null)
410             name = type.getName();
411         return setMethodReturn(name);
412     }
413 
414     /***
415      * Return the param types of the method this instruction operates on,
416      * or empty array if none.
417      */
418     public String[] getMethodParamNames() {
419         if (_index == 0)
420             return new String[0];
421 
422         ComplexEntry entry = (ComplexEntry) getPool().getEntry(_index);
423         String desc = entry.getNameAndTypeEntry().getDescriptorEntry().
424             getValue();
425         NameCache cache = getProject().getNameCache();
426         String[] names = cache.getDescriptorParamNames(desc);
427         for (int i = 0; i < names.length; i++)
428             names[i] = cache.getExternalForm(names[i], false);
429         return names;
430     }
431 
432     /***
433      * Return the param types of the method this instruction operates on,
434      * or empty array if none.
435      */
436     public Class[] getMethodParamTypes() {
437         String[] paramNames = getMethodParamNames();
438         Class[] params = new Class[paramNames.length];
439         for (int i = 0; i < paramNames.length; i++)
440             params[i] = Strings.toClass(paramNames[i], getClassLoader());
441         return params;
442     }
443 
444     /***
445      * Return the param types of the method this instruction operates on,
446      * or empty array if none.
447      */
448     public BCClass[] getMethodParamBCs() {
449         String[] paramNames = getMethodParamNames();
450         BCClass[] params = new BCClass[paramNames.length];
451         for (int i = 0; i < paramNames.length; i++)
452             params[i] = getProject().loadClass(paramNames[i], getClassLoader());
453         return params;
454     }
455 
456     /***
457      * Set the param types of the method this instruction operates on.
458      *
459      * @return this instruction, for method chaining
460      */
461     public MethodInstruction setMethodParams(String[] types) {
462         return setMethod(getMethodDeclarerName(), getMethodName(),
463             getMethodReturnName(), types);
464     }
465 
466     /***
467      * Set the param types of the method this instruction operates on.
468      *
469      * @return this instruction, for method chaining
470      */
471     public void setMethodParams(Class[] types) {
472         if (types == null)
473             setMethodParams((String[]) null);
474         else {
475             String[] names = new String[types.length];
476             for (int i = 0; i < types.length; i++)
477                 names[i] = types[i].getName();
478             setMethodParams(names);
479         }
480     }
481 
482     /***
483      * Set the param types of the method this instruction operates on.
484      *
485      * @return this instruction, for method chaining
486      */
487     public void setMethodParams(BCClass[] types) {
488         if (types == null)
489             setMethodParams((String[]) null);
490         else {
491             String[] names = new String[types.length];
492             for (int i = 0; i < types.length; i++)
493                 names[i] = types[i].getName();
494             setMethodParams(names);
495         }
496     }
497 
498     /***
499      * Return the declaring type of the method this instruction operates on,
500      * or null if not set.
501      */
502     public String getMethodDeclarerName() {
503         if (_index == 0)
504             return null;
505 
506         ComplexEntry entry = (ComplexEntry) getPool().getEntry(_index);
507         String name = getProject().getNameCache().getExternalForm
508             (entry.getClassEntry().getNameEntry().getValue(), false);
509         if (name.length() == 0)
510             return null;
511         return name;
512     }
513 
514     /***
515      * Return the declaring type of the method this instruction operates on,
516      * or null if not set.
517      */
518     public Class getMethodDeclarerType() {
519         String type = getMethodDeclarerName();
520         if (type == null)
521             return null;
522         return Strings.toClass(type, getClassLoader());
523     }
524 
525     /***
526      * Return the declaring type of the method this instruction operates on,
527      * or null if not set.
528      */
529     public BCClass getMethodDeclarerBC() {
530         String type = getMethodDeclarerName();
531         if (type == null)
532             return null;
533         return getProject().loadClass(type, getClassLoader());
534     }
535 
536     /***
537      * Set the declaring type of the method this instruction operates on.
538      *
539      * @return this instruction, for method chaining
540      */
541     public MethodInstruction setMethodDeclarer(String type) {
542         return setMethod(type, getMethodName(), getMethodReturnName(),
543             getMethodParamNames());
544     }
545 
546     /***
547      * Set the declaring type of the method this instruction operates on.
548      *
549      * @return this instruction, for method chaining
550      */
551     public MethodInstruction setMethodDeclarer(Class type) {
552         String name = null;
553         if (type != null)
554             name = type.getName();
555         return setMethodDeclarer(name);
556     }
557 
558     /***
559      * Set the declaring type of the method this instruction operates on.
560      *
561      * @return this instruction, for method chaining
562      */
563     public MethodInstruction setMethodDeclarer(BCClass type) {
564         String name = null;
565         if (type != null)
566             name = type.getName();
567         return setMethodDeclarer(name);
568     }
569 
570     /***
571      * MethodInstructions are equal if the method they reference is the same,
572      * or if the method of either is unset.
573      */
574     public boolean equalsInstruction(Instruction other) {
575         if (other == this)
576             return true;
577         if (!(other instanceof MethodInstruction))
578             return false;
579         if (!super.equalsInstruction(other))
580             return false;
581 
582         MethodInstruction ins = (MethodInstruction) other;
583         String s1 = getMethodName();
584         String s2 = ins.getMethodName();
585         if (!(s1 == null || s2 == null || s1.equals(s2)))
586             return false;
587 
588         s1 = getMethodReturnName();
589         s2 = ins.getMethodReturnName();
590         if (!(s1 == null || s2 == null || s1.equals(s2)))
591             return false;
592 
593         s1 = getMethodDeclarerName();
594         s2 = ins.getMethodDeclarerName();
595         if (!(s1 == null || s2 == null || s1.equals(s2)))
596             return false;
597 
598         String[] p1 = getMethodParamNames();
599         String[] p2 = ins.getMethodParamNames();
600         if (!(p1.length == 0 || p2.length == 0 || p1.length == p2.length))
601             return false;
602 
603         for (int i = 0; i < p1.length; i++)
604             if (!(p1[i] == null || p2[i] == null || p1[i].equals(p2[i])))
605                 return false;
606         return true;
607     }
608 
609     public void acceptVisit(BCVisitor visit) {
610         visit.enterMethodInstruction(this);
611         visit.exitMethodInstruction(this);
612     }
613 
614     void read(Instruction orig) {
615         super.read(orig);
616         MethodInstruction ins = (MethodInstruction) orig;
617         setMethod(ins.getMethodDeclarerName(), ins.getMethodName(),
618             ins.getMethodReturnName(), ins.getMethodParamNames());
619     }
620 
621     void read(DataInput in) throws IOException {
622         super.read(in);
623         _index = in.readUnsignedShort();
624         if (getOpcode() == Constants.INVOKEINTERFACE) {
625             in.readByte();
626             in.readByte();
627         }
628     }
629 
630     void write(DataOutput out) throws IOException {
631         super.write(out);
632         out.writeShort(_index);
633         if (getOpcode() == Constants.INVOKEINTERFACE) {
634             String[] args = getMethodParamNames();
635             int count = 1;
636             for (int i = 0; i < args.length; i++, count++)
637                 if (long.class.getName().equals(args[i]) 
638                     || double.class.getName().equals(args[i]))
639                     count++;
640 
641             out.writeByte(count);
642             out.writeByte(0);
643         }
644     }
645 }