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 * An instruction that invokes a method.
012 *
013 * @author Abe White
014 */
015 public class MethodInstruction extends Instruction {
016 private int _index = 0;
017
018 MethodInstruction(Code owner, int opcode) {
019 super(owner, opcode);
020 }
021
022 int getLength() {
023 if (getOpcode() == Constants.INVOKEINTERFACE)
024 return super.getLength() + 4;
025 return super.getLength() + 2;
026 }
027
028 public int getLogicalStackChange() {
029 String ret = getMethodReturnName();
030 if (ret == null)
031 return 0;
032
033 // subtract a stack pos for the this ptr
034 int stack = 0;
035 if (getOpcode() != Constants.INVOKESTATIC)
036 stack--;
037
038 // and for each arg
039 String[] params = getMethodParamNames();
040 for (int i = 0; i < params.length; i++)
041 stack--;
042
043 // add for the return value, if any
044 if (!void.class.getName().equals(ret))
045 stack++;
046 return stack;
047 }
048
049 public int getStackChange() {
050 String ret = getMethodReturnName();
051 if (ret == null)
052 return 0;
053
054 // subtract a stack pos for the this ptr
055 int stack = 0;
056 if (getOpcode() != Constants.INVOKESTATIC)
057 stack--;
058
059 // and for each arg (2 for longs, doubles)
060 String[] params = getMethodParamNames();
061 for (int i = 0; i < params.length; i++, stack--)
062 if (long.class.getName().equals(params[i])
063 || double.class.getName().equals(params[i]))
064 stack--;
065
066 // add for the return value, if any
067 if (!void.class.getName().equals(ret))
068 stack++;
069 if (long.class.getName().equals(ret)
070 || double.class.getName().equals(ret))
071 stack++;
072 return stack;
073 }
074
075 /////////////////////
076 // Method operations
077 /////////////////////
078
079 /**
080 * Return the index in the class {@link ConstantPool} of the
081 * {@link ComplexEntry} describing the method to operate on.
082 */
083 public int getMethodIndex() {
084 return _index;
085 }
086
087 /**
088 * Set the index in the class {@link ConstantPool} of the
089 * {@link ComplexEntry} describing the method to operate on.
090 *
091 * @return this instruction, for method chaining
092 */
093 public MethodInstruction setMethodIndex(int index) {
094 _index = index;
095 return this;
096 }
097
098 /**
099 * 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 }