1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 package org.apache.bcel.generic; 19 20 import java.util.ArrayList; 21 import java.util.Arrays; 22 import java.util.Comparator; 23 import java.util.Hashtable; 24 import java.util.List; 25 import java.util.Stack; 26 27 import org.apache.bcel.Const; 28 import org.apache.bcel.classfile.AnnotationEntry; 29 import org.apache.bcel.classfile.Annotations; 30 import org.apache.bcel.classfile.Attribute; 31 import org.apache.bcel.classfile.Code; 32 import org.apache.bcel.classfile.CodeException; 33 import org.apache.bcel.classfile.ExceptionTable; 34 import org.apache.bcel.classfile.LineNumber; 35 import org.apache.bcel.classfile.LineNumberTable; 36 import org.apache.bcel.classfile.LocalVariable; 37 import org.apache.bcel.classfile.LocalVariableTable; 38 import org.apache.bcel.classfile.LocalVariableTypeTable; 39 import org.apache.bcel.classfile.Method; 40 import org.apache.bcel.classfile.ParameterAnnotationEntry; 41 import org.apache.bcel.classfile.ParameterAnnotations; 42 import org.apache.bcel.classfile.RuntimeVisibleParameterAnnotations; 43 import org.apache.bcel.classfile.Utility; 44 import org.apache.bcel.util.BCELComparator; 45 46 /** 47 * Template class for building up a method. This is done by defining exception 48 * handlers, adding thrown exceptions, local variables and attributes, whereas 49 * the `LocalVariableTable' and `LineNumberTable' attributes will be set 50 * automatically for the code. Use stripAttributes() if you don't like this. 51 * 52 * While generating code it may be necessary to insert NOP operations. You can 53 * use the `removeNOPs' method to get rid off them. 54 * The resulting method object can be obtained via the `getMethod()' method. 55 * 56 * @version $Id$ 57 * @see InstructionList 58 * @see Method 59 */ 60 public class MethodGen extends FieldGenOrMethodGen { 61 62 private String class_name; 63 private Type[] arg_types; 64 private String[] arg_names; 65 private int max_locals; 66 private int max_stack; 67 private InstructionList il; 68 private boolean strip_attributes; 69 private LocalVariableTypeTable local_variable_type_table = null; 70 private final List<LocalVariableGen> variable_vec = new ArrayList<>(); 71 private final List<LineNumberGen> line_number_vec = new ArrayList<>(); 72 private final List<CodeExceptionGen> exception_vec = new ArrayList<>(); 73 private final List<String> throws_vec = new ArrayList<>(); 74 private final List<Attribute> code_attrs_vec = new ArrayList<>(); 75 76 private List<AnnotationEntryGen>[] param_annotations; // Array of lists containing AnnotationGen objects 77 private boolean hasParameterAnnotations = false; 78 private boolean haveUnpackedParameterAnnotations = false; 79 80 private static BCELComparator bcelComparator = new BCELComparator() { 81 82 @Override 83 public boolean equals( final Object o1, final Object o2 ) { 84 final MethodGen THIS = (MethodGen) o1; 85 final MethodGen THAT = (MethodGen) o2; 86 return THIS.getName().equals(THAT.getName()) 87 && THIS.getSignature().equals(THAT.getSignature()); 88 } 89 90 91 @Override 92 public int hashCode( final Object o ) { 93 final MethodGen THIS = (MethodGen) o; 94 return THIS.getSignature().hashCode() ^ THIS.getName().hashCode(); 95 } 96 }; 97 98 99 /** 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 */ MethodGen(final int access_flags, final Type return_type, final Type[] arg_types, String[] arg_names, final String method_name, final String class_name, final InstructionList il, final ConstantPoolGen cp)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 */ MethodGen(final Method m, final String class_name, final ConstantPoolGen cp)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 */ addLocalVariable( final String name, final Type type, final int slot, final InstructionHandle start, final InstructionHandle end, final int orig_index )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 */ addLocalVariable( final String name, final Type type, final int slot, final InstructionHandle start, final InstructionHandle end )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 */ addLocalVariable( final String name, final Type type, final InstructionHandle start, final InstructionHandle end )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 */ removeLocalVariable( final LocalVariableGen l )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 */ removeLocalVariables()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 */ getLocalVariables()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 */ getLocalVariableTable( final ConstantPoolGen cp )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 */ getLocalVariableTypeTable()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 */ addLineNumber( final InstructionHandle ih, final int src_line )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 */ removeLineNumber( final LineNumberGen l )410 public void removeLineNumber( final LineNumberGen l ) { 411 line_number_vec.remove(l); 412 } 413 414 415 /** 416 * Remove all line numbers. 417 */ removeLineNumbers()418 public void removeLineNumbers() { 419 line_number_vec.clear(); 420 } 421 422 423 /* 424 * @return array of line numbers 425 */ getLineNumbers()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 */ getLineNumberTable( final ConstantPoolGen cp )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 */ addExceptionHandler( final InstructionHandle start_pc, final InstructionHandle end_pc, final InstructionHandle handler_pc, final ObjectType catch_type )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 */ removeExceptionHandler( final CodeExceptionGen c )472 public void removeExceptionHandler( final CodeExceptionGen c ) { 473 exception_vec.remove(c); 474 } 475 476 477 /** 478 * Remove all line numbers. 479 */ removeExceptionHandlers()480 public void removeExceptionHandlers() { 481 exception_vec.clear(); 482 } 483 484 485 /* 486 * @return array of declared exception handlers 487 */ getExceptionHandlers()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 */ getCodeExceptions()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 */ addException( final String class_name )514 public void addException( final String class_name ) { 515 throws_vec.add(class_name); 516 } 517 518 519 /** 520 * Remove an exception. 521 */ removeException( final String c )522 public void removeException( final String c ) { 523 throws_vec.remove(c); 524 } 525 526 527 /** 528 * Remove all exceptions. 529 */ removeExceptions()530 public void removeExceptions() { 531 throws_vec.clear(); 532 } 533 534 535 /* 536 * @return array of thrown exceptions 537 */ getExceptions()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 */ getExceptionTable( final ConstantPoolGen cp )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 */ addCodeAttribute( final Attribute a )567 public void addCodeAttribute( final Attribute a ) { 568 code_attrs_vec.add(a); 569 } 570 571 572 /** 573 * Remove the LocalVariableTypeTable 574 */ removeLocalVariableTypeTable( )575 public void removeLocalVariableTypeTable( ) { 576 local_variable_type_table = null; 577 } 578 579 /** 580 * Remove a code attribute. 581 */ removeCodeAttribute( final Attribute a )582 public void removeCodeAttribute( final Attribute a ) { 583 code_attrs_vec.remove(a); 584 } 585 586 587 /** 588 * Remove all code attributes. 589 */ removeCodeAttributes()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 */ getCodeAttributes()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 */ addAnnotationsAsAttribute(final ConstantPoolGen cp)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 */ addParameterAnnotationsAsAttribute(final ConstantPoolGen cp)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 */ getMethod()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 updateLocalVariableTable(final LocalVariableTable a)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 adjustLocalVariableTypeTable(final LocalVariableTable lvt)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 */ removeNOPs()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 */ setMaxLocals( final int m )784 public void setMaxLocals( final int m ) { 785 max_locals = m; 786 } 787 788 getMaxLocals()789 public int getMaxLocals() { 790 return max_locals; 791 } 792 793 794 /** 795 * Set maximum stack size for this method. 796 */ setMaxStack( final int m )797 public void setMaxStack( final int m ) { // TODO could be package-protected? 798 max_stack = m; 799 } 800 801 getMaxStack()802 public int getMaxStack() { 803 return max_stack; 804 } 805 806 807 /** @return class that contains this method 808 */ getClassName()809 public String getClassName() { 810 return class_name; 811 } 812 813 setClassName( final String class_name )814 public void setClassName( final String class_name ) { // TODO could be package-protected? 815 this.class_name = class_name; 816 } 817 818 setReturnType( final Type return_type )819 public void setReturnType( final Type return_type ) { 820 setType(return_type); 821 } 822 823 getReturnType()824 public Type getReturnType() { 825 return getType(); 826 } 827 828 setArgumentTypes( final Type[] arg_types )829 public void setArgumentTypes( final Type[] arg_types ) { 830 this.arg_types = arg_types; 831 } 832 833 getArgumentTypes()834 public Type[] getArgumentTypes() { 835 return arg_types.clone(); 836 } 837 838 setArgumentType( final int i, final Type type )839 public void setArgumentType( final int i, final Type type ) { 840 arg_types[i] = type; 841 } 842 843 getArgumentType( final int i )844 public Type getArgumentType( final int i ) { 845 return arg_types[i]; 846 } 847 848 setArgumentNames( final String[] arg_names )849 public void setArgumentNames( final String[] arg_names ) { 850 this.arg_names = arg_names; 851 } 852 853 getArgumentNames()854 public String[] getArgumentNames() { 855 return arg_names.clone(); 856 } 857 858 setArgumentName( final int i, final String name )859 public void setArgumentName( final int i, final String name ) { 860 arg_names[i] = name; 861 } 862 863 getArgumentName( final int i )864 public String getArgumentName( final int i ) { 865 return arg_names[i]; 866 } 867 868 getInstructionList()869 public InstructionList getInstructionList() { 870 return il; 871 } 872 873 setInstructionList( final InstructionList il )874 public void setInstructionList( final InstructionList il ) { // TODO could be package-protected? 875 this.il = il; 876 } 877 878 879 @Override getSignature()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 */ setMaxStack()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 */ setMaxLocals()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 */ stripAttributes( final boolean flag )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 BranchTarget(final InstructionHandle target, final int stackDepth)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 push( final InstructionHandle target, final int stackDepth )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 pop()959 public BranchTarget pop() { 960 if (!branchTargets.empty()) { 961 final BranchTarget bt = branchTargets.pop(); 962 return bt; 963 } 964 return null; 965 } 966 967 visit( final InstructionHandle target, final int stackDepth )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 visited( final InstructionHandle target )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 */ getMaxStack( final ConstantPoolGen cp, final InstructionList il, final CodeExceptionGen[] et )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 */ addObserver( final MethodObserver o )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 */ removeObserver( final MethodObserver o )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 */ update()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 toString()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 */ copy( final String class_name, final ConstantPoolGen cp )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 */ getAnnotationsOnParameter(final int i)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 */ ensureExistingParameterAnnotationsUnpacked()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 makeMutableVersion(final AnnotationEntry[] mutableArray)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 addParameterAnnotation(final int parameterIndex, final AnnotationEntryGen annotation)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 */ getComparator()1244 public static BCELComparator getComparator() { 1245 return bcelComparator; 1246 } 1247 1248 1249 /** 1250 * @param comparator Comparison strategy object 1251 */ setComparator( final BCELComparator comparator )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 equals( final Object obj )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 hashCode()1277 public int hashCode() { 1278 return bcelComparator.hashCode(this); 1279 } 1280 } 1281