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