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