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