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 }