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