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