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
34 int stack = 0;
35 if (getOpcode() != Constants.INVOKESTATIC)
36 stack--;
37
38
39 String[] params = getMethodParamNames();
40 for (int i = 0; i < params.length; i++)
41 stack--;
42
43
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
55 int stack = 0;
56 if (getOpcode() != Constants.INVOKESTATIC)
57 stack--;
58
59
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
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
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
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 }