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