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