001package serp.bytecode; 002 003import java.io.*; 004import java.lang.reflect.*; 005 006import serp.bytecode.lowlevel.*; 007import serp.bytecode.visitor.*; 008import serp.util.*; 009 010/** 011 * An instruction that invokes a method. 012 * 013 * @author Abe White 014 */ 015public 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}