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