001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 * 017 */ 018package org.apache.bcel.generic; 019 020import java.util.ArrayList; 021import java.util.Arrays; 022import java.util.Comparator; 023import java.util.Hashtable; 024import java.util.List; 025import java.util.Stack; 026 027import org.apache.bcel.Const; 028import org.apache.bcel.classfile.AnnotationEntry; 029import org.apache.bcel.classfile.Annotations; 030import org.apache.bcel.classfile.Attribute; 031import org.apache.bcel.classfile.Code; 032import org.apache.bcel.classfile.CodeException; 033import org.apache.bcel.classfile.ExceptionTable; 034import org.apache.bcel.classfile.LineNumber; 035import org.apache.bcel.classfile.LineNumberTable; 036import org.apache.bcel.classfile.LocalVariable; 037import org.apache.bcel.classfile.LocalVariableTable; 038import org.apache.bcel.classfile.Method; 039import org.apache.bcel.classfile.ParameterAnnotationEntry; 040import org.apache.bcel.classfile.ParameterAnnotations; 041import org.apache.bcel.classfile.RuntimeVisibleParameterAnnotations; 042import org.apache.bcel.classfile.Utility; 043import org.apache.bcel.util.BCELComparator; 044 045/** 046 * Template class for building up a method. This is done by defining exception 047 * handlers, adding thrown exceptions, local variables and attributes, whereas 048 * the `LocalVariableTable' and `LineNumberTable' attributes will be set 049 * automatically for the code. Use stripAttributes() if you don't like this. 050 * 051 * While generating code it may be necessary to insert NOP operations. You can 052 * use the `removeNOPs' method to get rid off them. 053 * The resulting method object can be obtained via the `getMethod()' method. 054 * 055 * @version $Id: MethodGen.java 1749603 2016-06-21 20:50:19Z ggregory $ 056 * @see InstructionList 057 * @see Method 058 */ 059public class MethodGen extends FieldGenOrMethodGen { 060 061 private String class_name; 062 private Type[] arg_types; 063 private String[] arg_names; 064 private int max_locals; 065 private int max_stack; 066 private InstructionList il; 067 private boolean strip_attributes; 068 private final List<LocalVariableGen> variable_vec = new ArrayList<>(); 069 private final List<LineNumberGen> line_number_vec = new ArrayList<>(); 070 private final List<CodeExceptionGen> exception_vec = new ArrayList<>(); 071 private final List<String> throws_vec = new ArrayList<>(); 072 private final List<Attribute> code_attrs_vec = new ArrayList<>(); 073 074 private List<AnnotationEntryGen>[] param_annotations; // Array of lists containing AnnotationGen objects 075 private boolean hasParameterAnnotations = false; 076 private boolean haveUnpackedParameterAnnotations = false; 077 078 private static BCELComparator bcelComparator = new BCELComparator() { 079 080 @Override 081 public boolean equals( final Object o1, final Object o2 ) { 082 final MethodGen THIS = (MethodGen) o1; 083 final MethodGen THAT = (MethodGen) o2; 084 return THIS.getName().equals(THAT.getName()) 085 && THIS.getSignature().equals(THAT.getSignature()); 086 } 087 088 089 @Override 090 public int hashCode( final Object o ) { 091 final MethodGen THIS = (MethodGen) o; 092 return THIS.getSignature().hashCode() ^ THIS.getName().hashCode(); 093 } 094 }; 095 096 097 /** 098 * Declare method. If the method is non-static the constructor 099 * automatically declares a local variable `$this' in slot 0. The 100 * actual code is contained in the `il' parameter, which may further 101 * manipulated by the user. But he must take care not to remove any 102 * instruction (handles) that are still referenced from this object. 103 * 104 * For example one may not add a local variable and later remove the 105 * instructions it refers to without causing havoc. It is safe 106 * however if you remove that local variable, too. 107 * 108 * @param access_flags access qualifiers 109 * @param return_type method type 110 * @param arg_types argument types 111 * @param arg_names argument names (if this is null, default names will be provided 112 * for them) 113 * @param method_name name of method 114 * @param class_name class name containing this method (may be null, if you don't care) 115 * @param il instruction list associated with this method, may be null only for 116 * abstract or native methods 117 * @param cp constant pool 118 */ 119 public MethodGen(final int access_flags, final Type return_type, final Type[] arg_types, String[] arg_names, 120 final String method_name, final String class_name, final InstructionList il, final ConstantPoolGen cp) { 121 super(access_flags); 122 setType(return_type); 123 setArgumentTypes(arg_types); 124 setArgumentNames(arg_names); 125 setName(method_name); 126 setClassName(class_name); 127 setInstructionList(il); 128 setConstantPool(cp); 129 final boolean abstract_ = isAbstract() || isNative(); 130 InstructionHandle start = null; 131 InstructionHandle end = null; 132 if (!abstract_) { 133 start = il.getStart(); 134 end = il.getEnd(); 135 /* Add local variables, namely the implicit `this' and the arguments 136 */ 137 if (!isStatic() && (class_name != null)) { // Instance method -> `this' is local var 0 138 addLocalVariable("this", ObjectType.getInstance(class_name), start, end); 139 } 140 } 141 if (arg_types != null) { 142 final int size = arg_types.length; 143 for (final Type arg_type : arg_types) { 144 if (Type.VOID == arg_type) { 145 throw new ClassGenException("'void' is an illegal argument type for a method"); 146 } 147 } 148 if (arg_names != null) { // Names for variables provided? 149 if (size != arg_names.length) { 150 throw new ClassGenException("Mismatch in argument array lengths: " + size 151 + " vs. " + arg_names.length); 152 } 153 } else { // Give them dummy names 154 arg_names = new String[size]; 155 for (int i = 0; i < size; i++) { 156 arg_names[i] = "arg" + i; 157 } 158 setArgumentNames(arg_names); 159 } 160 if (!abstract_) { 161 for (int i = 0; i < size; i++) { 162 addLocalVariable(arg_names[i], arg_types[i], start, end); 163 } 164 } 165 } 166 } 167 168 169 /** 170 * Instantiate from existing method. 171 * 172 * @param m method 173 * @param class_name class name containing this method 174 * @param cp constant pool 175 */ 176 public MethodGen(final Method m, final String class_name, final ConstantPoolGen cp) { 177 this(m.getAccessFlags(), Type.getReturnType(m.getSignature()), Type.getArgumentTypes(m 178 .getSignature()), null /* may be overridden anyway */ 179 , m.getName(), class_name, 180 ((m.getAccessFlags() & (Const.ACC_ABSTRACT | Const.ACC_NATIVE)) == 0) 181 ? new InstructionList(m.getCode().getCode()) 182 : null, cp); 183 final Attribute[] attributes = m.getAttributes(); 184 for (final Attribute attribute : attributes) { 185 Attribute a = attribute; 186 if (a instanceof Code) { 187 final Code c = (Code) a; 188 setMaxStack(c.getMaxStack()); 189 setMaxLocals(c.getMaxLocals()); 190 final CodeException[] ces = c.getExceptionTable(); 191 if (ces != null) { 192 for (final CodeException ce : ces) { 193 final int type = ce.getCatchType(); 194 ObjectType c_type = null; 195 if (type > 0) { 196 final String cen = m.getConstantPool().getConstantString(type, 197 Const.CONSTANT_Class); 198 c_type = ObjectType.getInstance(cen); 199 } 200 final int end_pc = ce.getEndPC(); 201 final int length = m.getCode().getCode().length; 202 InstructionHandle end; 203 if (length == end_pc) { // May happen, because end_pc is exclusive 204 end = il.getEnd(); 205 } else { 206 end = il.findHandle(end_pc); 207 end = end.getPrev(); // Make it inclusive 208 } 209 addExceptionHandler(il.findHandle(ce.getStartPC()), end, il.findHandle(ce 210 .getHandlerPC()), c_type); 211 } 212 } 213 final Attribute[] c_attributes = c.getAttributes(); 214 for (final Attribute c_attribute : c_attributes) { 215 a = c_attribute; 216 if (a instanceof LineNumberTable) { 217 final LineNumber[] ln = ((LineNumberTable) a).getLineNumberTable(); 218 for (final LineNumber l : ln) { 219 final InstructionHandle ih = il.findHandle(l.getStartPC()); 220 if (ih != null) { 221 addLineNumber(ih, l.getLineNumber()); 222 } 223 } 224 } else if (a instanceof LocalVariableTable) { 225 final LocalVariable[] lv = ((LocalVariableTable) a).getLocalVariableTable(); 226 removeLocalVariables(); 227 for (final LocalVariable l : lv) { 228 InstructionHandle start = il.findHandle(l.getStartPC()); 229 InstructionHandle end = il.findHandle(l.getStartPC() + l.getLength()); 230 // Repair malformed handles 231 if (null == start) { 232 start = il.getStart(); 233 } 234 if (null == end) { 235 end = il.getEnd(); 236 } 237 addLocalVariable(l.getName(), Type.getType(l.getSignature()), l 238 .getIndex(), start, end); 239 } 240 } else { 241 addCodeAttribute(a); 242 } 243 } 244 } else if (a instanceof ExceptionTable) { 245 final String[] names = ((ExceptionTable) a).getExceptionNames(); 246 for (final String name2 : names) { 247 addException(name2); 248 } 249 } else if (a instanceof Annotations) { 250 final Annotations runtimeAnnotations = (Annotations) a; 251 final AnnotationEntry[] aes = runtimeAnnotations.getAnnotationEntries(); 252 for (final AnnotationEntry element : aes) { 253 addAnnotationEntry(new AnnotationEntryGen(element, cp, false)); 254 } 255 } else { 256 addAttribute(a); 257 } 258 } 259 } 260 261 262 /** 263 * Adds a local variable to this method. 264 * 265 * @param name variable name 266 * @param type variable type 267 * @param slot the index of the local variable, if type is long or double, the next available 268 * index is slot+2 269 * @param start from where the variable is valid 270 * @param end until where the variable is valid 271 * @return new local variable object 272 * @see LocalVariable 273 */ 274 public LocalVariableGen addLocalVariable( final String name, final Type type, final int slot, 275 final InstructionHandle start, final InstructionHandle end ) { 276 final byte t = type.getType(); 277 if (t != Const.T_ADDRESS) { 278 final int add = type.getSize(); 279 if (slot + add > max_locals) { 280 max_locals = slot + add; 281 } 282 final LocalVariableGen l = new LocalVariableGen(slot, name, type, start, end); 283 int i; 284 if ((i = variable_vec.indexOf(l)) >= 0) { 285 variable_vec.set(i, l); 286 } else { 287 variable_vec.add(l); 288 } 289 return l; 290 } 291 throw new IllegalArgumentException("Can not use " + type 292 + " as type for local variable"); 293 } 294 295 296 /** 297 * Adds a local variable to this method and assigns an index automatically. 298 * 299 * @param name variable name 300 * @param type variable type 301 * @param start from where the variable is valid, if this is null, 302 * it is valid from the start 303 * @param end until where the variable is valid, if this is null, 304 * it is valid to the end 305 * @return new local variable object 306 * @see LocalVariable 307 */ 308 public LocalVariableGen addLocalVariable( final String name, final Type type, final InstructionHandle start, 309 final InstructionHandle end ) { 310 return addLocalVariable(name, type, max_locals, start, end); 311 } 312 313 314 /** 315 * Remove a local variable, its slot will not be reused, if you do not use addLocalVariable 316 * with an explicit index argument. 317 */ 318 public void removeLocalVariable( final LocalVariableGen l ) { 319 l.dispose(); 320 variable_vec.remove(l); 321 } 322 323 324 /** 325 * Remove all local variables. 326 */ 327 public void removeLocalVariables() { 328 for (final LocalVariableGen lv : variable_vec) { 329 lv.dispose(); 330 } 331 variable_vec.clear(); 332 } 333 334 335 /* 336 * If the range of the variable has not been set yet, it will be set to be valid from 337 * the start to the end of the instruction list. 338 * 339 * @return array of declared local variables sorted by index 340 */ 341 public LocalVariableGen[] getLocalVariables() { 342 final int size = variable_vec.size(); 343 final LocalVariableGen[] lg = new LocalVariableGen[size]; 344 variable_vec.toArray(lg); 345 for (int i = 0; i < size; i++) { 346 if ((lg[i].getStart() == null) && (il != null)) { 347 lg[i].setStart(il.getStart()); 348 } 349 if ((lg[i].getEnd() == null) && (il != null)) { 350 lg[i].setEnd(il.getEnd()); 351 } 352 } 353 if (size > 1) { 354 Arrays.sort(lg, new Comparator<LocalVariableGen>() { 355 @Override 356 public int compare(final LocalVariableGen o1, final LocalVariableGen o2) { 357 return o1.getIndex() - o2.getIndex(); 358 } 359 }); 360 } 361 return lg; 362 } 363 364 365 /** 366 * @return `LocalVariableTable' attribute of all the local variables of this method. 367 */ 368 public LocalVariableTable getLocalVariableTable( final ConstantPoolGen cp ) { 369 final LocalVariableGen[] lg = getLocalVariables(); 370 final int size = lg.length; 371 final LocalVariable[] lv = new LocalVariable[size]; 372 for (int i = 0; i < size; i++) { 373 lv[i] = lg[i].getLocalVariable(cp); 374 } 375 return new LocalVariableTable(cp.addUtf8("LocalVariableTable"), 2 + lv.length * 10, lv, cp 376 .getConstantPool()); 377 } 378 379 380 /** 381 * Give an instruction a line number corresponding to the source code line. 382 * 383 * @param ih instruction to tag 384 * @return new line number object 385 * @see LineNumber 386 */ 387 public LineNumberGen addLineNumber( final InstructionHandle ih, final int src_line ) { 388 final LineNumberGen l = new LineNumberGen(ih, src_line); 389 line_number_vec.add(l); 390 return l; 391 } 392 393 394 /** 395 * Remove a line number. 396 */ 397 public void removeLineNumber( final LineNumberGen l ) { 398 line_number_vec.remove(l); 399 } 400 401 402 /** 403 * Remove all line numbers. 404 */ 405 public void removeLineNumbers() { 406 line_number_vec.clear(); 407 } 408 409 410 /* 411 * @return array of line numbers 412 */ 413 public LineNumberGen[] getLineNumbers() { 414 final LineNumberGen[] lg = new LineNumberGen[line_number_vec.size()]; 415 line_number_vec.toArray(lg); 416 return lg; 417 } 418 419 420 /** 421 * @return `LineNumberTable' attribute of all the local variables of this method. 422 */ 423 public LineNumberTable getLineNumberTable( final ConstantPoolGen cp ) { 424 final int size = line_number_vec.size(); 425 final LineNumber[] ln = new LineNumber[size]; 426 for (int i = 0; i < size; i++) { 427 ln[i] = line_number_vec.get(i).getLineNumber(); 428 } 429 return new LineNumberTable(cp.addUtf8("LineNumberTable"), 2 + ln.length * 4, ln, cp 430 .getConstantPool()); 431 } 432 433 434 /** 435 * Add an exception handler, i.e., specify region where a handler is active and an 436 * instruction where the actual handling is done. 437 * 438 * @param start_pc Start of region (inclusive) 439 * @param end_pc End of region (inclusive) 440 * @param handler_pc Where handling is done 441 * @param catch_type class type of handled exception or null if any 442 * exception is handled 443 * @return new exception handler object 444 */ 445 public CodeExceptionGen addExceptionHandler( final InstructionHandle start_pc, 446 final InstructionHandle end_pc, final InstructionHandle handler_pc, final ObjectType catch_type ) { 447 if ((start_pc == null) || (end_pc == null) || (handler_pc == null)) { 448 throw new ClassGenException("Exception handler target is null instruction"); 449 } 450 final CodeExceptionGen c = new CodeExceptionGen(start_pc, end_pc, handler_pc, catch_type); 451 exception_vec.add(c); 452 return c; 453 } 454 455 456 /** 457 * Remove an exception handler. 458 */ 459 public void removeExceptionHandler( final CodeExceptionGen c ) { 460 exception_vec.remove(c); 461 } 462 463 464 /** 465 * Remove all line numbers. 466 */ 467 public void removeExceptionHandlers() { 468 exception_vec.clear(); 469 } 470 471 472 /* 473 * @return array of declared exception handlers 474 */ 475 public CodeExceptionGen[] getExceptionHandlers() { 476 final CodeExceptionGen[] cg = new CodeExceptionGen[exception_vec.size()]; 477 exception_vec.toArray(cg); 478 return cg; 479 } 480 481 482 /** 483 * @return code exceptions for `Code' attribute 484 */ 485 private CodeException[] getCodeExceptions() { 486 final int size = exception_vec.size(); 487 final CodeException[] c_exc = new CodeException[size]; 488 for (int i = 0; i < size; i++) { 489 final CodeExceptionGen c = exception_vec.get(i); 490 c_exc[i] = c.getCodeException(super.getConstantPool()); 491 } 492 return c_exc; 493 } 494 495 496 /** 497 * Add an exception possibly thrown by this method. 498 * 499 * @param class_name (fully qualified) name of exception 500 */ 501 public void addException( final String class_name ) { 502 throws_vec.add(class_name); 503 } 504 505 506 /** 507 * Remove an exception. 508 */ 509 public void removeException( final String c ) { 510 throws_vec.remove(c); 511 } 512 513 514 /** 515 * Remove all exceptions. 516 */ 517 public void removeExceptions() { 518 throws_vec.clear(); 519 } 520 521 522 /* 523 * @return array of thrown exceptions 524 */ 525 public String[] getExceptions() { 526 final String[] e = new String[throws_vec.size()]; 527 throws_vec.toArray(e); 528 return e; 529 } 530 531 532 /** 533 * @return `Exceptions' attribute of all the exceptions thrown by this method. 534 */ 535 private ExceptionTable getExceptionTable( final ConstantPoolGen cp ) { 536 final int size = throws_vec.size(); 537 final int[] ex = new int[size]; 538 for (int i = 0; i < size; i++) { 539 ex[i] = cp.addClass(throws_vec.get(i)); 540 } 541 return new ExceptionTable(cp.addUtf8("Exceptions"), 2 + 2 * size, ex, cp.getConstantPool()); 542 } 543 544 545 /** 546 * Add an attribute to the code. Currently, the JVM knows about the 547 * LineNumberTable, LocalVariableTable and StackMap attributes, 548 * where the former two will be generated automatically and the 549 * latter is used for the MIDP only. Other attributes will be 550 * ignored by the JVM but do no harm. 551 * 552 * @param a attribute to be added 553 */ 554 public void addCodeAttribute( final Attribute a ) { 555 code_attrs_vec.add(a); 556 } 557 558 559 /** 560 * Remove a code attribute. 561 */ 562 public void removeCodeAttribute( final Attribute a ) { 563 code_attrs_vec.remove(a); 564 } 565 566 567 /** 568 * Remove all code attributes. 569 */ 570 public void removeCodeAttributes() { 571 code_attrs_vec.clear(); 572 } 573 574 575 /** 576 * @return all attributes of this method. 577 */ 578 public Attribute[] getCodeAttributes() { 579 final Attribute[] attributes = new Attribute[code_attrs_vec.size()]; 580 code_attrs_vec.toArray(attributes); 581 return attributes; 582 } 583 584 /** 585 * @since 6.0 586 */ 587 public void addAnnotationsAsAttribute(final ConstantPoolGen cp) { 588 final Attribute[] attrs = AnnotationEntryGen.getAnnotationAttributes(cp, super.getAnnotationEntries()); 589 for (final Attribute attr : attrs) { 590 addAttribute(attr); 591 } 592 } 593 594 /** 595 * @since 6.0 596 */ 597 public void addParameterAnnotationsAsAttribute(final ConstantPoolGen cp) { 598 if (!hasParameterAnnotations) { 599 return; 600 } 601 final Attribute[] attrs = AnnotationEntryGen.getParameterAnnotationAttributes(cp,param_annotations); 602 if (attrs!=null) { 603 for (final Attribute attr : attrs) { 604 addAttribute(attr); 605 } 606 } 607 } 608 609 610 /** 611 * Get method object. Never forget to call setMaxStack() or setMaxStack(max), respectively, 612 * before calling this method (the same applies for max locals). 613 * 614 * @return method object 615 */ 616 public Method getMethod() { 617 final String signature = getSignature(); 618 final ConstantPoolGen _cp = super.getConstantPool(); 619 final int name_index = _cp.addUtf8(super.getName()); 620 final int signature_index = _cp.addUtf8(signature); 621 /* Also updates positions of instructions, i.e., their indices 622 */ 623 byte[] byte_code = null; 624 if (il != null) { 625 byte_code = il.getByteCode(); 626 } 627 LineNumberTable lnt = null; 628 LocalVariableTable lvt = null; 629 /* Create LocalVariableTable and LineNumberTable attributes (for debuggers, e.g.) 630 */ 631 if ((variable_vec.size() > 0) && !strip_attributes) { 632 addCodeAttribute(lvt = getLocalVariableTable(_cp)); 633 } 634 if ((line_number_vec.size() > 0) && !strip_attributes) { 635 addCodeAttribute(lnt = getLineNumberTable(_cp)); 636 } 637 final Attribute[] code_attrs = getCodeAttributes(); 638 /* Each attribute causes 6 additional header bytes 639 */ 640 int attrs_len = 0; 641 for (final Attribute code_attr : code_attrs) { 642 attrs_len += code_attr.getLength() + 6; 643 } 644 final CodeException[] c_exc = getCodeExceptions(); 645 final int exc_len = c_exc.length * 8; // Every entry takes 8 bytes 646 Code code = null; 647 if ((il != null) && !isAbstract() && !isNative()) { 648 // Remove any stale code attribute 649 final Attribute[] attributes = getAttributes(); 650 for (final Attribute a : attributes) { 651 if (a instanceof Code) { 652 removeAttribute(a); 653 } 654 } 655 code = new Code(_cp.addUtf8("Code"), 8 + byte_code.length + // prologue byte code 656 2 + exc_len + // exceptions 657 2 + attrs_len, // attributes 658 max_stack, max_locals, byte_code, c_exc, code_attrs, _cp.getConstantPool()); 659 addAttribute(code); 660 } 661 addAnnotationsAsAttribute(_cp); 662 addParameterAnnotationsAsAttribute(_cp); 663 ExceptionTable et = null; 664 if (throws_vec.size() > 0) { 665 addAttribute(et = getExceptionTable(_cp)); 666 // Add `Exceptions' if there are "throws" clauses 667 } 668 final Method m = new Method(super.getAccessFlags(), name_index, signature_index, getAttributes(), _cp 669 .getConstantPool()); 670 // Undo effects of adding attributes 671 if (lvt != null) { 672 removeCodeAttribute(lvt); 673 } 674 if (lnt != null) { 675 removeCodeAttribute(lnt); 676 } 677 if (code != null) { 678 removeAttribute(code); 679 } 680 if (et != null) { 681 removeAttribute(et); 682 } 683 return m; 684 } 685 686 687 /** 688 * Remove all NOPs from the instruction list (if possible) and update every 689 * object referring to them, i.e., branch instructions, local variables and 690 * exception handlers. 691 */ 692 public void removeNOPs() { 693 if (il != null) { 694 InstructionHandle next; 695 /* Check branch instructions. 696 */ 697 for (InstructionHandle ih = il.getStart(); ih != null; ih = next) { 698 next = ih.getNext(); 699 if ((next != null) && (ih.getInstruction() instanceof NOP)) { 700 try { 701 il.delete(ih); 702 } catch (final TargetLostException e) { 703 for (final InstructionHandle target : e.getTargets()) { 704 for (final InstructionTargeter targeter : target.getTargeters()) { 705 targeter.updateTarget(target, next); 706 } 707 } 708 } 709 } 710 } 711 } 712 } 713 714 715 /** 716 * Set maximum number of local variables. 717 */ 718 public void setMaxLocals( final int m ) { 719 max_locals = m; 720 } 721 722 723 public int getMaxLocals() { 724 return max_locals; 725 } 726 727 728 /** 729 * Set maximum stack size for this method. 730 */ 731 public void setMaxStack( final int m ) { // TODO could be package-protected? 732 max_stack = m; 733 } 734 735 736 public int getMaxStack() { 737 return max_stack; 738 } 739 740 741 /** @return class that contains this method 742 */ 743 public String getClassName() { 744 return class_name; 745 } 746 747 748 public void setClassName( final String class_name ) { // TODO could be package-protected? 749 this.class_name = class_name; 750 } 751 752 753 public void setReturnType( final Type return_type ) { 754 setType(return_type); 755 } 756 757 758 public Type getReturnType() { 759 return getType(); 760 } 761 762 763 public void setArgumentTypes( final Type[] arg_types ) { 764 this.arg_types = arg_types; 765 } 766 767 768 public Type[] getArgumentTypes() { 769 return arg_types.clone(); 770 } 771 772 773 public void setArgumentType( final int i, final Type type ) { 774 arg_types[i] = type; 775 } 776 777 778 public Type getArgumentType( final int i ) { 779 return arg_types[i]; 780 } 781 782 783 public void setArgumentNames( final String[] arg_names ) { 784 this.arg_names = arg_names; 785 } 786 787 788 public String[] getArgumentNames() { 789 return arg_names.clone(); 790 } 791 792 793 public void setArgumentName( final int i, final String name ) { 794 arg_names[i] = name; 795 } 796 797 798 public String getArgumentName( final int i ) { 799 return arg_names[i]; 800 } 801 802 803 public InstructionList getInstructionList() { 804 return il; 805 } 806 807 808 public void setInstructionList( final InstructionList il ) { // TODO could be package-protected? 809 this.il = il; 810 } 811 812 813 @Override 814 public String getSignature() { 815 return Type.getMethodSignature(super.getType(), arg_types); 816 } 817 818 819 /** 820 * Computes max. stack size by performing control flow analysis. 821 */ 822 public void setMaxStack() { // TODO could be package-protected? (some tests would need repackaging) 823 if (il != null) { 824 max_stack = getMaxStack(super.getConstantPool(), il, getExceptionHandlers()); 825 } else { 826 max_stack = 0; 827 } 828 } 829 830 831 /** 832 * Compute maximum number of local variables. 833 */ 834 public void setMaxLocals() { // TODO could be package-protected? (some tests would need repackaging) 835 if (il != null) { 836 int max = isStatic() ? 0 : 1; 837 if (arg_types != null) { 838 for (final Type arg_type : arg_types) { 839 max += arg_type.getSize(); 840 } 841 } 842 for (InstructionHandle ih = il.getStart(); ih != null; ih = ih.getNext()) { 843 final Instruction ins = ih.getInstruction(); 844 if ((ins instanceof LocalVariableInstruction) || (ins instanceof RET) 845 || (ins instanceof IINC)) { 846 final int index = ((IndexedInstruction) ins).getIndex() 847 + ((TypedInstruction) ins).getType(super.getConstantPool()).getSize(); 848 if (index > max) { 849 max = index; 850 } 851 } 852 } 853 max_locals = max; 854 } else { 855 max_locals = 0; 856 } 857 } 858 859 860 /** Do not/Do produce attributes code attributesLineNumberTable and 861 * LocalVariableTable, like javac -O 862 */ 863 public void stripAttributes( final boolean flag ) { 864 strip_attributes = flag; 865 } 866 867 static final class BranchTarget { 868 869 final InstructionHandle target; 870 final int stackDepth; 871 872 873 BranchTarget(final InstructionHandle target, final int stackDepth) { 874 this.target = target; 875 this.stackDepth = stackDepth; 876 } 877 } 878 879 static final class BranchStack { 880 881 private final Stack<BranchTarget> branchTargets = new Stack<>(); 882 private final Hashtable<InstructionHandle, BranchTarget> visitedTargets = new Hashtable<>(); 883 884 885 public void push( final InstructionHandle target, final int stackDepth ) { 886 if (visited(target)) { 887 return; 888 } 889 branchTargets.push(visit(target, stackDepth)); 890 } 891 892 893 public BranchTarget pop() { 894 if (!branchTargets.empty()) { 895 final BranchTarget bt = branchTargets.pop(); 896 return bt; 897 } 898 return null; 899 } 900 901 902 private BranchTarget visit( final InstructionHandle target, final int stackDepth ) { 903 final BranchTarget bt = new BranchTarget(target, stackDepth); 904 visitedTargets.put(target, bt); 905 return bt; 906 } 907 908 909 private boolean visited( final InstructionHandle target ) { 910 return visitedTargets.get(target) != null; 911 } 912 } 913 914 915 /** 916 * Computes stack usage of an instruction list by performing control flow analysis. 917 * 918 * @return maximum stack depth used by method 919 */ 920 public static int getMaxStack( final ConstantPoolGen cp, final InstructionList il, final CodeExceptionGen[] et ) { 921 final BranchStack branchTargets = new BranchStack(); 922 /* Initially, populate the branch stack with the exception 923 * handlers, because these aren't (necessarily) branched to 924 * explicitly. in each case, the stack will have depth 1, 925 * containing the exception object. 926 */ 927 for (final CodeExceptionGen element : et) { 928 final InstructionHandle handler_pc = element.getHandlerPC(); 929 if (handler_pc != null) { 930 branchTargets.push(handler_pc, 1); 931 } 932 } 933 int stackDepth = 0; 934 int maxStackDepth = 0; 935 InstructionHandle ih = il.getStart(); 936 while (ih != null) { 937 final Instruction instruction = ih.getInstruction(); 938 final short opcode = instruction.getOpcode(); 939 final int delta = instruction.produceStack(cp) - instruction.consumeStack(cp); 940 stackDepth += delta; 941 if (stackDepth > maxStackDepth) { 942 maxStackDepth = stackDepth; 943 } 944 // choose the next instruction based on whether current is a branch. 945 if (instruction instanceof BranchInstruction) { 946 final BranchInstruction branch = (BranchInstruction) instruction; 947 if (instruction instanceof Select) { 948 // explore all of the select's targets. the default target is handled below. 949 final Select select = (Select) branch; 950 final InstructionHandle[] targets = select.getTargets(); 951 for (final InstructionHandle target : targets) { 952 branchTargets.push(target, stackDepth); 953 } 954 // nothing to fall through to. 955 ih = null; 956 } else if (!(branch instanceof IfInstruction)) { 957 // if an instruction that comes back to following PC, 958 // push next instruction, with stack depth reduced by 1. 959 if (opcode == Const.JSR || opcode == Const.JSR_W) { 960 branchTargets.push(ih.getNext(), stackDepth - 1); 961 } 962 ih = null; 963 } 964 // for all branches, the target of the branch is pushed on the branch stack. 965 // conditional branches have a fall through case, selects don't, and 966 // jsr/jsr_w return to the next instruction. 967 branchTargets.push(branch.getTarget(), stackDepth); 968 } else { 969 // check for instructions that terminate the method. 970 if (opcode == Const.ATHROW || opcode == Const.RET 971 || (opcode >= Const.IRETURN && opcode <= Const.RETURN)) { 972 ih = null; 973 } 974 } 975 // normal case, go to the next instruction. 976 if (ih != null) { 977 ih = ih.getNext(); 978 } 979 // if we have no more instructions, see if there are any deferred branches to explore. 980 if (ih == null) { 981 final BranchTarget bt = branchTargets.pop(); 982 if (bt != null) { 983 ih = bt.target; 984 stackDepth = bt.stackDepth; 985 } 986 } 987 } 988 return maxStackDepth; 989 } 990 991 private List<MethodObserver> observers; 992 993 994 /** Add observer for this object. 995 */ 996 public void addObserver( final MethodObserver o ) { 997 if (observers == null) { 998 observers = new ArrayList<>(); 999 } 1000 observers.add(o); 1001 } 1002 1003 1004 /** Remove observer for this object. 1005 */ 1006 public void removeObserver( final MethodObserver o ) { 1007 if (observers != null) { 1008 observers.remove(o); 1009 } 1010 } 1011 1012 1013 /** Call notify() method on all observers. This method is not called 1014 * automatically whenever the state has changed, but has to be 1015 * called by the user after he has finished editing the object. 1016 */ 1017 public void update() { 1018 if (observers != null) { 1019 for (final MethodObserver observer : observers) { 1020 observer.notify(this); 1021 } 1022 } 1023 } 1024 1025 1026 /** 1027 * Return string representation close to declaration format, 1028 * `public static void main(String[]) throws IOException', e.g. 1029 * 1030 * @return String representation of the method. 1031 */ 1032 @Override 1033 public final String toString() { 1034 final String access = Utility.accessToString(super.getAccessFlags()); 1035 String signature = Type.getMethodSignature(super.getType(), arg_types); 1036 signature = Utility.methodSignatureToString(signature, super.getName(), access, true, 1037 getLocalVariableTable(super.getConstantPool())); 1038 final StringBuilder buf = new StringBuilder(signature); 1039 for (final Attribute a : getAttributes()) { 1040 if (!((a instanceof Code) || (a instanceof ExceptionTable))) { 1041 buf.append(" [").append(a).append("]"); 1042 } 1043 } 1044 1045 if (throws_vec.size() > 0) { 1046 for (final String throwsDescriptor : throws_vec) { 1047 buf.append("\n\t\tthrows ").append(throwsDescriptor); 1048 } 1049 } 1050 return buf.toString(); 1051 } 1052 1053 1054 /** @return deep copy of this method 1055 */ 1056 public MethodGen copy( final String class_name, final ConstantPoolGen cp ) { 1057 final Method m = ((MethodGen) clone()).getMethod(); 1058 final MethodGen mg = new MethodGen(m, class_name, super.getConstantPool()); 1059 if (super.getConstantPool() != cp) { 1060 mg.setConstantPool(cp); 1061 mg.getInstructionList().replaceConstantPool(super.getConstantPool(), cp); 1062 } 1063 return mg; 1064 } 1065 1066 //J5TODO: Should param_annotations be an array of arrays? Rather than an array of lists, this 1067 // is more likely to suggest to the caller it is readonly (which a List does not). 1068 /** 1069 * Return a list of AnnotationGen objects representing parameter annotations 1070 * @since 6.0 1071 */ 1072 public List<AnnotationEntryGen> getAnnotationsOnParameter(final int i) { 1073 ensureExistingParameterAnnotationsUnpacked(); 1074 if (!hasParameterAnnotations || i>arg_types.length) { 1075 return null; 1076 } 1077 return param_annotations[i]; 1078 } 1079 1080 /** 1081 * Goes through the attributes on the method and identifies any that are 1082 * RuntimeParameterAnnotations, extracting their contents and storing them 1083 * as parameter annotations. There are two kinds of parameter annotation - 1084 * visible and invisible. Once they have been unpacked, these attributes are 1085 * deleted. (The annotations will be rebuilt as attributes when someone 1086 * builds a Method object out of this MethodGen object). 1087 */ 1088 private void ensureExistingParameterAnnotationsUnpacked() 1089 { 1090 if (haveUnpackedParameterAnnotations) { 1091 return; 1092 } 1093 // Find attributes that contain parameter annotation data 1094 final Attribute[] attrs = getAttributes(); 1095 ParameterAnnotations paramAnnVisAttr = null; 1096 ParameterAnnotations paramAnnInvisAttr = null; 1097 for (final Attribute attribute : attrs) { 1098 if (attribute instanceof ParameterAnnotations) 1099 { 1100 // Initialize param_annotations 1101 if (!hasParameterAnnotations) 1102 { 1103 @SuppressWarnings("unchecked") // OK 1104 final List<AnnotationEntryGen>[] parmList = new List[arg_types.length]; 1105 param_annotations = parmList; 1106 for (int j = 0; j < arg_types.length; j++) { 1107 param_annotations[j] = new ArrayList<>(); 1108 } 1109 } 1110 hasParameterAnnotations = true; 1111 final ParameterAnnotations rpa = (ParameterAnnotations) attribute; 1112 if (rpa instanceof RuntimeVisibleParameterAnnotations) { 1113 paramAnnVisAttr = rpa; 1114 } else { 1115 paramAnnInvisAttr = rpa; 1116 } 1117 for (int j = 0; j < arg_types.length; j++) 1118 { 1119 // This returns Annotation[] ... 1120 final ParameterAnnotationEntry immutableArray = rpa 1121 .getParameterAnnotationEntries()[j]; 1122 // ... which needs transforming into an AnnotationGen[] ... 1123 final List<AnnotationEntryGen> mutable = makeMutableVersion(immutableArray.getAnnotationEntries()); 1124 // ... then add these to any we already know about 1125 param_annotations[j].addAll(mutable); 1126 } 1127 } 1128 } 1129 if (paramAnnVisAttr != null) { 1130 removeAttribute(paramAnnVisAttr); 1131 } 1132 if (paramAnnInvisAttr != null) { 1133 removeAttribute(paramAnnInvisAttr); 1134 } 1135 haveUnpackedParameterAnnotations = true; 1136 } 1137 1138 private List<AnnotationEntryGen> makeMutableVersion(final AnnotationEntry[] mutableArray) 1139 { 1140 final List<AnnotationEntryGen> result = new ArrayList<>(); 1141 for (final AnnotationEntry element : mutableArray) { 1142 result.add(new AnnotationEntryGen(element, getConstantPool(), 1143 false)); 1144 } 1145 return result; 1146 } 1147 1148 public void addParameterAnnotation(final int parameterIndex, 1149 final AnnotationEntryGen annotation) 1150 { 1151 ensureExistingParameterAnnotationsUnpacked(); 1152 if (!hasParameterAnnotations) 1153 { 1154 @SuppressWarnings("unchecked") // OK 1155 final List<AnnotationEntryGen>[] parmList = new List[arg_types.length]; 1156 param_annotations = parmList; 1157 hasParameterAnnotations = true; 1158 } 1159 final List<AnnotationEntryGen> existingAnnotations = param_annotations[parameterIndex]; 1160 if (existingAnnotations != null) 1161 { 1162 existingAnnotations.add(annotation); 1163 } 1164 else 1165 { 1166 final List<AnnotationEntryGen> l = new ArrayList<>(); 1167 l.add(annotation); 1168 param_annotations[parameterIndex] = l; 1169 } 1170 } 1171 1172 1173 1174 1175 /** 1176 * @return Comparison strategy object 1177 */ 1178 public static BCELComparator getComparator() { 1179 return bcelComparator; 1180 } 1181 1182 1183 /** 1184 * @param comparator Comparison strategy object 1185 */ 1186 public static void setComparator( final BCELComparator comparator ) { 1187 bcelComparator = comparator; 1188 } 1189 1190 1191 /** 1192 * Return value as defined by given BCELComparator strategy. 1193 * By default two MethodGen objects are said to be equal when 1194 * their names and signatures are equal. 1195 * 1196 * @see java.lang.Object#equals(java.lang.Object) 1197 */ 1198 @Override 1199 public boolean equals( final Object obj ) { 1200 return bcelComparator.equals(this, obj); 1201 } 1202 1203 1204 /** 1205 * Return value as defined by given BCELComparator strategy. 1206 * By default return the hashcode of the method's name XOR signature. 1207 * 1208 * @see java.lang.Object#hashCode() 1209 */ 1210 @Override 1211 public int hashCode() { 1212 return bcelComparator.hashCode(this); 1213 } 1214}