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 if (getOpcode() == Constants.INVOKEDYNAMIC)
026 return super.getLength() + 4;
027 return super.getLength() + 2;
028 }
029
030 public int getLogicalStackChange() {
031 String ret = getMethodReturnName();
032 if (ret == null)
033 return 0;
034
035 // subtract a stack pos for the this ptr
036 int stack = 0;
037 if (getOpcode() != Constants.INVOKESTATIC)
038 stack--;
039
040 // and for each arg
041 String[] params = getMethodParamNames();
042 for (int i = 0; i < params.length; i++)
043 stack--;
044
045 // add for the return value, if any
046 if (!void.class.getName().equals(ret))
047 stack++;
048 return stack;
049 }
050
051 public int getStackChange() {
052 String ret = getMethodReturnName();
053 if (ret == null)
054 return 0;
055
056 // subtract a stack pos for the this ptr
057 int stack = 0;
058 if (getOpcode() != Constants.INVOKESTATIC)
059 stack--;
060
061 // and for each arg (2 for longs, doubles)
062 String[] params = getMethodParamNames();
063 for (int i = 0; i < params.length; i++, stack--)
064 if (long.class.getName().equals(params[i])
065 || double.class.getName().equals(params[i]))
066 stack--;
067
068 // add for the return value, if any
069 if (!void.class.getName().equals(ret))
070 stack++;
071 if (long.class.getName().equals(ret)
072 || double.class.getName().equals(ret))
073 stack++;
074 return stack;
075 }
076
077 /////////////////////
078 // Method operations
079 /////////////////////
080
081 /**
082 * Return the index in the class {@link ConstantPool} of the
083 * {@link ComplexEntry} describing the method to operate on.
084 */
085 public int getMethodIndex() {
086 return _index;
087 }
088
089 /**
090 * Set the index in the class {@link ConstantPool} of the
091 * {@link ComplexEntry} describing the method to operate on.
092 *
093 * @return this instruction, for method chaining
094 */
095 public MethodInstruction setMethodIndex(int index) {
096 _index = index;
097 return this;
098 }
099
100 /**
101 * Return the method this instruction operates on, or null if not set.
102 */
103 public BCMethod getMethod() {
104 String dec = getMethodDeclarerName();
105 if (dec == null)
106 return null;
107
108 BCClass bc = getProject().loadClass(dec, getClassLoader());
109 BCMethod[] meths = bc.getMethods(getMethodName(),getMethodParamNames());
110 if (meths.length == 0)
111 return null;
112 return meths[0];
113 }
114
115 /**
116 * Set the method this instruction operates on.
117 *
118 * @return this instruction, for method chaining
119 */
120 public MethodInstruction setMethod(BCMethod method) {
121 if (method == null)
122 return setMethodIndex(0);
123 return setMethod(method.getDeclarer().getName(), method.getName(),
124 method.getReturnName(), method.getParamNames(), false);
125 }
126
127 /**
128 * Set the method this instruction operates on.
129 *
130 * @return this instruction, for method chaining
131 */
132 public MethodInstruction setMethod(Method method) {
133 if (method == null)
134 return setMethodIndex(0);
135 return setMethod(method.getDeclaringClass(), method.getName(),
136 method.getReturnType(), method.getParameterTypes());
137 }
138
139 /**
140 * Set the method this instruction operates on.
141 *
142 * @return this instruction, for method chaining
143 */
144 public MethodInstruction setMethod(Constructor method) {
145 if (method == null)
146 return setMethodIndex(0);
147 setOpcode(Constants.INVOKESPECIAL);
148 return setMethod(method.getDeclaringClass(), "<init>", void.class,
149 method.getParameterTypes());
150 }
151
152 /**
153 * Set the method this instruction operates on.
154 *
155 * @param dec the full class name of the method's declaring class
156 * @param name the method name
157 * @param returnType the full class name of the method return type
158 * @param param the full class names of the method param types
159 * @return this instruction, for method chaining
160 */
161 public MethodInstruction setMethod(String dec, String name,
162 String returnType, String[] params) {
163 return setMethod(dec, name, returnType, params, true);
164 }
165
166 /**
167 * Set the method this instruction operates on.
168 *
169 * @param dec the full class name of the method's declaring class, or the bootstrap index for InvokeDynamic
170 * @param name the method name
171 * @param returnType the full class name of the method return type
172 * @param param the full class names of the method param types
173 * @param copy whether to copy the the parameter array
174 * @return this instruction, for method chaining
175 */
176 private MethodInstruction setMethod(String dec, String name,
177 String returnType, String[] params, boolean copy) {
178 if (name == null && returnType == null && dec == null
179 && (params == null || params.length == 0))
180 return setMethodIndex(0);
181
182 if (dec == null)
183 dec = "";
184 if (name == null)
185 name = "";
186 if (returnType == null)
187 returnType = "";
188 if (params == null)
189 params = new String[0];
190 else if (copy) {
191 String[] pcopy = new String[params.length];
192 System.arraycopy(params, 0, pcopy, 0, params.length);
193 params = pcopy;
194 }
195
196 NameCache cache = getProject().getNameCache();
197 returnType = cache.getInternalForm(returnType, true);
198 dec = cache.getInternalForm(dec, false);
199 for (int i = 0; i < params.length; i++)
200 params[i] = cache.getInternalForm(params[i], true);
201
202 String desc = cache.getDescriptor(returnType, params);
203 if (getOpcode() == Constants.INVOKEINTERFACE)
204 return setMethodIndex(getPool().findInterfaceMethodEntry(dec, name,
205 desc, true));
206 if (getOpcode() == Constants.INVOKEDYNAMIC) {
207 int bootstrapindex = Integer.parseInt(dec); // Dec represents the bootstrap index
208 return setMethodIndex(getPool().findInvokeDynamicEntry(bootstrapindex, name, desc, true));
209 }
210 return setMethodIndex(getPool().findMethodEntry(dec, name, desc, true));
211 }
212
213 /**
214 * Set the method this instruction operates on, for methods that are
215 * declared by the current class.
216 *
217 * @param name the method name
218 * @param returnType the full class name of the method return type
219 * @param param the full class names of the method param types
220 * @return this instruction, for method chaining
221 */
222 public MethodInstruction setMethod(String name, String returnType,
223 String[] params) {
224 BCClass owner = getCode().getMethod().getDeclarer();
225 return setMethod(owner.getName(), name, returnType, params);
226 }
227
228 /**
229 * Set the method this instruction operates on.
230 *
231 * @param dec the method's declaring class
232 * @param name the method name
233 * @param returnType the class of the method return type
234 * @param param the class of the method param types
235 * @return this instruction, for method chaining
236 */
237 public MethodInstruction setMethod(Class dec, String name,
238 Class returnType, Class[] params) {
239 String decName = (dec == null) ? null : dec.getName();
240 String returnName = (returnType == null) ? null : returnType.getName();
241 String[] paramNames = null;
242 if (params != null) {
243 paramNames = new String[params.length];
244 for (int i = 0; i < params.length; i++)
245 paramNames[i] = params[i].getName();
246 }
247 return setMethod(decName, name, returnName, paramNames, false);
248 }
249
250 /**
251 * Set the method this instruction operates on, for methods that are
252 * declared by the current class.
253 *
254 * @param name the method name
255 * @param returnType the class of the method return type
256 * @param param the class of the method param types
257 * @return this instruction, for method chaining
258 */
259 public MethodInstruction setMethod(String name, Class returnType,
260 Class[] params) {
261 BCClass owner = getCode().getMethod().getDeclarer();
262 String returnName = (returnType == null) ? null : returnType.getName();
263 String[] paramNames = null;
264 if (params != null) {
265 paramNames = new String[params.length];
266 for (int i = 0; i < params.length; i++)
267 paramNames[i] = params[i].getName();
268 }
269 return setMethod(owner.getName(), name, returnName, paramNames, false);
270 }
271
272 /**
273 * Set the method this instruction operates on.
274 *
275 * @param dec the method's declaring class
276 * @param name the method name
277 * @param returnType the class of the method return type
278 * @param param the class of the method param types
279 * @return this instruction, for method chaining
280 */
281 public MethodInstruction setMethod(BCClass dec, String name,
282 BCClass returnType, BCClass[] params) {
283 String decName = (dec == null) ? null : dec.getName();
284 String returnName = (returnType == null) ? null : returnType.getName();
285 String[] paramNames = null;
286 if (params != null) {
287 paramNames = new String[params.length];
288 for (int i = 0; i < params.length; i++)
289 paramNames[i] = params[i].getName();
290 }
291 return setMethod(decName, name, returnName, paramNames, false);
292 }
293
294 /**
295 * Set the method this instruction operates on, for methods that are
296 * declared by the current class.
297 *
298 * @param name the method name
299 * @param returnType the class of the method return type
300 * @param param the class of the method param types
301 * @return this instruction, for method chaining
302 */
303 public MethodInstruction setMethod(String name, BCClass returnType,
304 BCClass[] params) {
305 BCClass owner = getCode().getMethod().getDeclarer();
306 String returnName = (returnType == null) ? null : returnType.getName();
307 String[] paramNames = null;
308 if (params != null) {
309 paramNames = new String[params.length];
310 for (int i = 0; i < params.length; i++)
311 paramNames[i] = params[i].getName();
312 }
313 return setMethod(owner.getName(), name, returnName, paramNames, false);
314 }
315
316 /////////////////////////////////////////
317 // Name, Return, Param, Owner operations
318 /////////////////////////////////////////
319
320 /**
321 * Return the name of the method this instruction operates on, or null
322 * if not set.
323 */
324 public String getMethodName() {
325 if (_index == 0)
326 return null;
327
328 String name = null;
329
330 if (getOpcode() == Constants.INVOKEDYNAMIC) {
331 InvokeDynamicEntry ide = (InvokeDynamicEntry) getPool().getEntry(_index);
332 name = ide.getNameAndTypeEntry().getNameEntry().getValue();
333 } else {
334 ComplexEntry entry = (ComplexEntry) getPool().getEntry(_index);
335 name = entry.getNameAndTypeEntry().getNameEntry().getValue();
336 }
337
338 if (name.length() == 0) {
339 name = null;
340 }
341
342 return name;
343 }
344
345 /**
346 * Set the name of the method this instruction operates on.
347 *
348 * @return this instruction, for method chaining
349 */
350 public MethodInstruction setMethodName(String name) {
351 return setMethod(getMethodDeclarerName(), name, getMethodReturnName(),
352 getMethodParamNames());
353 }
354
355 /**
356 * Return the return type of the method this instruction operates on,
357 * or null if not set.
358 */
359 public String getMethodReturnName() {
360 if (_index == 0)
361 return null;
362
363 String desc = null;
364 if (getOpcode() == Constants.INVOKEDYNAMIC) {
365 InvokeDynamicEntry ide = (InvokeDynamicEntry) getPool().getEntry(_index);
366 desc = ide.getNameAndTypeEntry().getDescriptorEntry().getValue();
367 } else {
368 ComplexEntry entry = (ComplexEntry) getPool().getEntry(_index);
369 desc = entry.getNameAndTypeEntry().getDescriptorEntry().getValue();
370 }
371
372 NameCache cache = getProject().getNameCache();
373 String name = cache.getExternalForm(cache.getDescriptorReturnName(desc), false);
374 if (name.length() == 0)
375 return null;
376 return name;
377 }
378
379 /**
380 * Return the return type of the method this instruction operates on,
381 * or null if not set.
382 */
383 public Class getMethodReturnType() {
384 String type = getMethodReturnName();
385 if (type == null)
386 return null;
387 return Strings.toClass(type, getClassLoader());
388 }
389
390 /**
391 * Return the return type of the method this instruction operates on,
392 * or null if not set.
393 */
394 public BCClass getMethodReturnBC() {
395 String type = getMethodReturnName();
396 if (type == null)
397 return null;
398 return getProject().loadClass(type, getClassLoader());
399 }
400
401 /**
402 * Set the return type of the method this instruction operates on.
403 *
404 * @return this instruction, for method chaining
405 */
406 public MethodInstruction setMethodReturn(String type) {
407 return setMethod(getMethodDeclarerName(), getMethodName(), type,
408 getMethodParamNames());
409 }
410
411 /**
412 * Set the return type of the method this instruction operates on.
413 *
414 * @return this instruction, for method chaining
415 */
416 public MethodInstruction setMethodReturn(Class type) {
417 String name = null;
418 if (type != null)
419 name = type.getName();
420 return setMethodReturn(name);
421 }
422
423 /**
424 * Set the return type of the method this instruction operates on.
425 *
426 * @return this instruction, for method chaining
427 */
428 public MethodInstruction setMethodReturn(BCClass type) {
429 String name = null;
430 if (type != null)
431 name = type.getName();
432 return setMethodReturn(name);
433 }
434
435 /**
436 * Return the param types of the method this instruction operates on,
437 * or empty array if none.
438 */
439 public String[] getMethodParamNames() {
440 if (_index == 0)
441 return new String[0];
442
443 String desc = null;
444 if (getOpcode() == Constants.INVOKEDYNAMIC) {
445 InvokeDynamicEntry ide = (InvokeDynamicEntry) getPool().getEntry(_index);
446 desc = ide.getNameAndTypeEntry().getDescriptorEntry().getValue();
447 } else {
448 ComplexEntry entry = (ComplexEntry) getPool().getEntry(_index);
449 desc = entry.getNameAndTypeEntry().getDescriptorEntry().
450 getValue();
451 }
452
453 NameCache cache = getProject().getNameCache();
454 String[] names = cache.getDescriptorParamNames(desc);
455 for (int i = 0; i < names.length; i++)
456 names[i] = cache.getExternalForm(names[i], false);
457 return names;
458 }
459
460 /**
461 * Return the param types of the method this instruction operates on,
462 * or empty array if none.
463 */
464 public Class[] getMethodParamTypes() {
465 String[] paramNames = getMethodParamNames();
466 Class[] params = new Class[paramNames.length];
467 for (int i = 0; i < paramNames.length; i++)
468 params[i] = Strings.toClass(paramNames[i], getClassLoader());
469 return params;
470 }
471
472 /**
473 * Return the param types of the method this instruction operates on,
474 * or empty array if none.
475 */
476 public BCClass[] getMethodParamBCs() {
477 String[] paramNames = getMethodParamNames();
478 BCClass[] params = new BCClass[paramNames.length];
479 for (int i = 0; i < paramNames.length; i++)
480 params[i] = getProject().loadClass(paramNames[i], getClassLoader());
481 return params;
482 }
483
484 /**
485 * Set the param types of the method this instruction operates on.
486 *
487 * @return this instruction, for method chaining
488 */
489 public MethodInstruction setMethodParams(String[] types) {
490 return setMethod(getMethodDeclarerName(), getMethodName(),
491 getMethodReturnName(), types);
492 }
493
494 /**
495 * Set the param types of the method this instruction operates on.
496 *
497 * @return this instruction, for method chaining
498 */
499 public void setMethodParams(Class[] types) {
500 if (types == null)
501 setMethodParams((String[]) null);
502 else {
503 String[] names = new String[types.length];
504 for (int i = 0; i < types.length; i++)
505 names[i] = types[i].getName();
506 setMethodParams(names);
507 }
508 }
509
510 /**
511 * Set the param types of the method this instruction operates on.
512 *
513 * @return this instruction, for method chaining
514 */
515 public void setMethodParams(BCClass[] types) {
516 if (types == null)
517 setMethodParams((String[]) null);
518 else {
519 String[] names = new String[types.length];
520 for (int i = 0; i < types.length; i++)
521 names[i] = types[i].getName();
522 setMethodParams(names);
523 }
524 }
525
526 /**
527 * Return the declaring type of the method this instruction operates on,
528 * or null if not set.
529 */
530 public String getMethodDeclarerName() {
531 if (_index == 0)
532 return null;
533
534 String name = null;
535 if (getOpcode() == Constants.INVOKEDYNAMIC) {
536 // InvokeDynamic doesn't hvae a method declarer, but it does have a bootstrap index.
537 InvokeDynamicEntry ide = (InvokeDynamicEntry) getPool().getEntry(_index);
538 name = String.valueOf(ide.getBootstrapMethodAttrIndex());
539 } else {
540 ComplexEntry entry = (ComplexEntry) getPool().getEntry(_index);
541 name = getProject().getNameCache().getExternalForm
542 (entry.getClassEntry().getNameEntry().getValue(), false);
543 }
544
545 if (name.length() == 0)
546 return null;
547 return name;
548 }
549
550 /**
551 * Return the declaring type of the method this instruction operates on,
552 * or null if not set.
553 */
554 public Class getMethodDeclarerType() {
555 String type = getMethodDeclarerName();
556 if (type == null)
557 return null;
558 return Strings.toClass(type, getClassLoader());
559 }
560
561 /**
562 * Return the declaring type of the method this instruction operates on,
563 * or null if not set.
564 */
565 public BCClass getMethodDeclarerBC() {
566 String type = getMethodDeclarerName();
567 if (type == null)
568 return null;
569 return getProject().loadClass(type, getClassLoader());
570 }
571
572 /**
573 * Set the declaring type of the method this instruction operates on.
574 *
575 * @return this instruction, for method chaining
576 */
577 public MethodInstruction setMethodDeclarer(String type) {
578 return setMethod(type, getMethodName(), getMethodReturnName(),
579 getMethodParamNames());
580 }
581
582 /**
583 * Set the declaring type of the method this instruction operates on.
584 *
585 * @return this instruction, for method chaining
586 */
587 public MethodInstruction setMethodDeclarer(Class type) {
588 String name = null;
589 if (type != null)
590 name = type.getName();
591 return setMethodDeclarer(name);
592 }
593
594 /**
595 * Set the declaring type of the method this instruction operates on.
596 *
597 * @return this instruction, for method chaining
598 */
599 public MethodInstruction setMethodDeclarer(BCClass type) {
600 String name = null;
601 if (type != null)
602 name = type.getName();
603 return setMethodDeclarer(name);
604 }
605
606 /**
607 * MethodInstructions are equal if the method they reference is the same,
608 * or if the method of either is unset.
609 */
610 public boolean equalsInstruction(Instruction other) {
611 if (other == this)
612 return true;
613 if (!(other instanceof MethodInstruction))
614 return false;
615 if (!super.equalsInstruction(other))
616 return false;
617
618 MethodInstruction ins = (MethodInstruction) other;
619 String s1 = getMethodName();
620 String s2 = ins.getMethodName();
621 if (!(s1 == null || s2 == null || s1.equals(s2)))
622 return false;
623
624 s1 = getMethodReturnName();
625 s2 = ins.getMethodReturnName();
626 if (!(s1 == null || s2 == null || s1.equals(s2)))
627 return false;
628
629 s1 = getMethodDeclarerName();
630 s2 = ins.getMethodDeclarerName();
631 if (!(s1 == null || s2 == null || s1.equals(s2)))
632 return false;
633
634 String[] p1 = getMethodParamNames();
635 String[] p2 = ins.getMethodParamNames();
636 if (!(p1.length == 0 || p2.length == 0 || p1.length == p2.length))
637 return false;
638
639 for (int i = 0; i < p1.length; i++)
640 if (!(p1[i] == null || p2[i] == null || p1[i].equals(p2[i])))
641 return false;
642 return true;
643 }
644
645 public void acceptVisit(BCVisitor visit) {
646 visit.enterMethodInstruction(this);
647 visit.exitMethodInstruction(this);
648 }
649
650 void read(Instruction orig) {
651 super.read(orig);
652 MethodInstruction ins = (MethodInstruction) orig;
653 setMethod(ins.getMethodDeclarerName(), ins.getMethodName(),
654 ins.getMethodReturnName(), ins.getMethodParamNames());
655 }
656
657 void read(DataInput in) throws IOException {
658 super.read(in);
659 _index = in.readUnsignedShort();
660 if (getOpcode() == Constants.INVOKEINTERFACE || getOpcode() == Constants.INVOKEDYNAMIC) {
661 in.readByte();
662 in.readByte();
663 }
664 }
665
666 void write(DataOutput out) throws IOException {
667 super.write(out);
668 out.writeShort(_index);
669 if (getOpcode() == Constants.INVOKEINTERFACE) {
670 String[] args = getMethodParamNames();
671 int count = 1;
672 for (int i = 0; i < args.length; i++, count++)
673 if (long.class.getName().equals(args[i])
674 || double.class.getName().equals(args[i]))
675 count++;
676
677 out.writeByte(count);
678 out.writeByte(0);
679 } else if (getOpcode() == Constants.INVOKEDYNAMIC) {
680 out.writeByte(0);
681 out.writeByte(0);
682 }
683 }
684 }