1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 package com.sun.org.apache.bcel.internal.generic;
   6 
   7 /* ====================================================================
   8  * The Apache Software License, Version 1.1
   9  *
  10  * Copyright (c) 2001 The Apache Software Foundation.  All rights
  11  * reserved.
  12  *
  13  * Redistribution and use in source and binary forms, with or without
  14  * modification, are permitted provided that the following conditions
  15  * are met:
  16  *
  17  * 1. Redistributions of source code must retain the above copyright
  18  *    notice, this list of conditions and the following disclaimer.
  19  *
  20  * 2. Redistributions in binary form must reproduce the above copyright
  21  *    notice, this list of conditions and the following disclaimer in
  22  *    the documentation and/or other materials provided with the
  23  *    distribution.
  24  *
  25  * 3. The end-user documentation included with the redistribution,
  26  *    if any, must include the following acknowledgment:
  27  *       "This product includes software developed by the
  28  *        Apache Software Foundation (http://www.apache.org/)."
  29  *    Alternately, this acknowledgment may appear in the software itself,
  30  *    if and wherever such third-party acknowledgments normally appear.
  31  *
  32  * 4. The names "Apache" and "Apache Software Foundation" and
  33  *    "Apache BCEL" must not be used to endorse or promote products
  34  *    derived from this software without prior written permission. For
  35  *    written permission, please contact apache@apache.org.
  36  *
  37  * 5. Products derived from this software may not be called "Apache",
  38  *    "Apache BCEL", nor may "Apache" appear in their name, without
  39  *    prior written permission of the Apache Software Foundation.
  40  *
  41  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  42  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  43  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  44  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  45  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  46  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  47  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  48  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  49  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  50  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  51  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  52  * SUCH DAMAGE.
  53  * ====================================================================
  54  *
  55  * This software consists of voluntary contributions made by many
  56  * individuals on behalf of the Apache Software Foundation.  For more
  57  * information on the Apache Software Foundation, please see
  58  * <http://www.apache.org/>.
  59  */
  60 
  61 import com.sun.org.apache.bcel.internal.Constants;
  62 import com.sun.org.apache.bcel.internal.classfile.*;
  63 import java.util.*;
  64 
  65 /**
  66  * Template class for building up a method. This is done by defining exception
  67  * handlers, adding thrown exceptions, local variables and attributes, whereas
  68  * the `LocalVariableTable' and `LineNumberTable' attributes will be set
  69  * automatically for the code. Use stripAttributes() if you don't like this.
  70  *
  71  * While generating code it may be necessary to insert NOP operations. You can
  72  * use the `removeNOPs' method to get rid off them.
  73  * The resulting method object can be obtained via the `getMethod()' method.
  74  *
  75  * @author  <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
  76  * @author  <A HREF="http://www.vmeng.com/beard">Patrick C. Beard</A> [setMaxStack()]
  77  * @see     InstructionList
  78  * @see     Method
  79  */
  80 public class MethodGen extends FieldGenOrMethodGen {
  81   private String          class_name;
  82   private Type[]          arg_types;
  83   private String[]        arg_names;
  84   private int             max_locals;
  85   private int             max_stack;
  86   private InstructionList il;
  87   private boolean         strip_attributes;
  88 
  89   private ArrayList       variable_vec    = new ArrayList();
  90   private ArrayList       line_number_vec = new ArrayList();
  91   private ArrayList       exception_vec   = new ArrayList();
  92   private ArrayList       throws_vec      = new ArrayList();
  93   private ArrayList       code_attrs_vec  = new ArrayList();
  94 
  95   /**
  96    * Declare method. If the method is non-static the constructor
  97    * automatically declares a local variable `$this' in slot 0. The
  98    * actual code is contained in the `il' parameter, which may further
  99    * manipulated by the user. But he must take care not to remove any
 100    * instruction (handles) that are still referenced from this object.
 101    *
 102    * For example one may not add a local variable and later remove the
 103    * instructions it refers to without causing havoc. It is safe
 104    * however if you remove that local variable, too.
 105    *
 106    * @param access_flags access qualifiers
 107    * @param return_type  method type
 108    * @param arg_types argument types
 109    * @param arg_names argument names (if this is null, default names will be provided
 110    * for them)
 111    * @param method_name name of method
 112    * @param class_name class name containing this method (may be null, if you don't care)
 113    * @param il instruction list associated with this method, may be null only for
 114    * abstract or native methods
 115    * @param cp constant pool
 116    */
 117   public MethodGen(int access_flags, Type return_type, Type[] arg_types,
 118                    String[] arg_names, String method_name, String class_name,
 119                    InstructionList il, ConstantPoolGen cp) {
 120     setAccessFlags(access_flags);
 121     setType(return_type);
 122     setArgumentTypes(arg_types);
 123     setArgumentNames(arg_names);
 124     setName(method_name);
 125     setClassName(class_name);
 126     setInstructionList(il);
 127     setConstantPool(cp);
 128 
 129     boolean abstract_ = isAbstract() || isNative();
 130     InstructionHandle start = null;
 131     InstructionHandle end   = null;
 132 
 133     if(!abstract_) {
 134       start = il.getStart();
 135       end   = il.getEnd();
 136 
 137       /* Add local variables, namely the implicit `this' and the arguments
 138        */
 139       if(!isStatic() && (class_name != null)) { // Instance method -> `this' is local var 0
 140         addLocalVariable("this", new ObjectType(class_name), start, end);
 141       }
 142     }
 143 
 144     if(arg_types != null) {
 145       int size = arg_types.length;
 146 
 147       for(int i=0; i < size; i++) {
 148         if(Type.VOID == arg_types[i]) {
 149           throw new ClassGenException("'void' is an illegal argument type for a method");
 150         }
 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: " +
 156                                       size + " vs. " + arg_names.length);
 157       } else { // Give them dummy names
 158         arg_names = new String[size];
 159 
 160         for(int i=0; i < size; i++)
 161           arg_names[i] = "arg" + i;
 162 
 163         setArgumentNames(arg_names);
 164       }
 165 
 166       if(!abstract_) {
 167         for(int i=0; i < size; i++) {
 168           addLocalVariable(arg_names[i], arg_types[i], start, end);
 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(Method m, String class_name, ConstantPoolGen cp) {
 182     this(m.getAccessFlags(), Type.getReturnType(m.getSignature()),
 183          Type.getArgumentTypes(m.getSignature()), null /* may be overridden anyway */,
 184          m.getName(), class_name,
 185          ((m.getAccessFlags() & (Constants.ACC_ABSTRACT | Constants.ACC_NATIVE)) == 0)?
 186          new InstructionList(m.getCode().getCode()) : null,
 187          cp);
 188 
 189     Attribute[] attributes = m.getAttributes();
 190     for(int i=0; i < attributes.length; i++) {
 191       Attribute a = attributes[i];
 192 
 193       if(a instanceof Code) {
 194         Code c = (Code)a;
 195         setMaxStack(c.getMaxStack());
 196         setMaxLocals(c.getMaxLocals());
 197 
 198         CodeException[] ces = c.getExceptionTable();
 199 
 200         if(ces != null) {
 201           for(int j=0; j < ces.length; j++) {
 202             CodeException ce     = ces[j];
 203             int           type   = ce.getCatchType();
 204             ObjectType    c_type = null;
 205 
 206             if(type > 0) {
 207               String cen = m.getConstantPool().getConstantString(type, Constants.CONSTANT_Class);
 208               c_type = new ObjectType(cen);
 209             }
 210 
 211             int end_pc = ce.getEndPC();
 212             int length = m.getCode().getCode().length;
 213 
 214             InstructionHandle end;
 215 
 216             if(length == end_pc) { // May happen, because end_pc is exclusive
 217               end = il.getEnd();
 218             } else {
 219               end = il.findHandle(end_pc);
 220               end = end.getPrev(); // Make it inclusive
 221             }
 222 
 223             addExceptionHandler(il.findHandle(ce.getStartPC()), end,
 224                                 il.findHandle(ce.getHandlerPC()), c_type);
 225           }
 226         }
 227 
 228         Attribute[] c_attributes = c.getAttributes();
 229         for(int j=0; j < c_attributes.length; j++) {
 230           a = c_attributes[j];
 231 
 232           if(a instanceof LineNumberTable) {
 233             LineNumber[] ln = ((LineNumberTable)a).getLineNumberTable();
 234 
 235             for(int k=0; k < ln.length; k++) {
 236               LineNumber l = ln[k];
 237               addLineNumber(il.findHandle(l.getStartPC()), l.getLineNumber());
 238             }
 239           } else if(a instanceof LocalVariableTable) {
 240             LocalVariable[] lv = ((LocalVariableTable)a).getLocalVariableTable();
 241 
 242             removeLocalVariables();
 243 
 244             for(int k=0; k < lv.length; k++) {
 245               LocalVariable     l     = lv[k];
 246               InstructionHandle start = il.findHandle(l.getStartPC());
 247               InstructionHandle end   = il.findHandle(l.getStartPC() + l.getLength());
 248 
 249               // Repair malformed handles
 250               if(null == start) {
 251                 start = il.getStart();
 252               }
 253 
 254               if(null == end) {
 255                 end = il.getEnd();
 256               }
 257 
 258               addLocalVariable(l.getName(), Type.getType(l.getSignature()),
 259                                l.getIndex(), start, end);
 260             }
 261           } else if (a instanceof LocalVariableTypeTable) {
 262              LocalVariable[] lv = ((LocalVariableTypeTable) a).getLocalVariableTypeTable();
 263              removeLocalVariables();
 264              for (int k = 0; k < lv.length; k++) {
 265                  LocalVariable l = lv[k];
 266                  InstructionHandle start = il.findHandle(l.getStartPC());
 267                  InstructionHandle end = il.findHandle(l.getStartPC() + l.getLength());
 268                  // Repair malformed handles
 269                  if (null == start) {
 270                      start = il.getStart();
 271                  }
 272                  if (null == end) {
 273                      end = il.getEnd();
 274                  }
 275                  addLocalVariable(l.getName(), Type.getType(l.getSignature()), l
 276                          .getIndex(), start, end);
 277               }
 278           } else
 279             addCodeAttribute(a);
 280         }
 281       } else if(a instanceof ExceptionTable) {
 282         String[] names = ((ExceptionTable)a).getExceptionNames();
 283         for(int j=0; j < names.length; j++)
 284           addException(names[j]);
 285       } else
 286         addAttribute(a);
 287     }
 288   }
 289 
 290   /**
 291    * Adds a local variable to this method.
 292    *
 293    * @param name variable name
 294    * @param type variable type
 295    * @param slot the index of the local variable, if type is long or double, the next available
 296    * index is slot+2
 297    * @param start from where the variable is valid
 298    * @param end until where the variable is valid
 299    * @return new local variable object
 300    * @see LocalVariable
 301    */
 302   public LocalVariableGen addLocalVariable(String name, Type type, int slot,
 303                                            InstructionHandle start,
 304                                            InstructionHandle end) {
 305     byte t = type.getType();
 306 
 307     if(t != Constants.T_ADDRESS) {
 308       int  add = type.getSize();
 309 
 310       if(slot + add > max_locals)
 311         max_locals = slot + add;
 312 
 313       LocalVariableGen l = new LocalVariableGen(slot, name, type, start, end);
 314       int i;
 315 
 316       if((i = variable_vec.indexOf(l)) >= 0) // Overwrite if necessary
 317         variable_vec.set(i, l);
 318       else
 319         variable_vec.add(l);
 320 
 321       return l;
 322     } else {
 323       throw new IllegalArgumentException("Can not use " + type +
 324                                          " as type for local variable");
 325 
 326     }
 327   }
 328 
 329   /**
 330    * Adds a local variable to this method and assigns an index automatically.
 331    *
 332    * @param name variable name
 333    * @param type variable type
 334    * @param start from where the variable is valid, if this is null,
 335    * it is valid from the start
 336    * @param end until where the variable is valid, if this is null,
 337    * it is valid to the end
 338    * @return new local variable object
 339    * @see LocalVariable
 340    */
 341   public LocalVariableGen addLocalVariable(String name, Type type,
 342                                            InstructionHandle start,
 343                                            InstructionHandle end) {
 344     return addLocalVariable(name, type, max_locals, start, end);
 345   }
 346 
 347   /**
 348    * Remove a local variable, its slot will not be reused, if you do not use addLocalVariable
 349    * with an explicit index argument.
 350    */
 351   public void removeLocalVariable(LocalVariableGen l) {
 352     variable_vec.remove(l);
 353   }
 354 
 355   /**
 356    * Remove all local variables.
 357    */
 358   public void removeLocalVariables() {
 359     variable_vec.clear();
 360   }
 361 
 362   /**
 363    * Sort local variables by index
 364    */
 365   private static final void sort(LocalVariableGen[] vars, int l, int r) {
 366     int i = l, j = r;
 367     int m = vars[(l + r) / 2].getIndex();
 368     LocalVariableGen h;
 369 
 370     do {
 371       while(vars[i].getIndex() < m) i++;
 372       while(m < vars[j].getIndex()) j--;
 373 
 374       if(i <= j) {
 375         h=vars[i]; vars[i]=vars[j]; vars[j]=h; // Swap elements
 376         i++; j--;
 377       }
 378     } while(i <= j);
 379 
 380     if(l < j) sort(vars, l, j);
 381     if(i < r) sort(vars, i, r);
 382   }
 383 
 384   /*
 385    * If the range of the variable has not been set yet, it will be set to be valid from
 386    * the start to the end of the instruction list.
 387    *
 388    * @return array of declared local variables sorted by index
 389    */
 390   public LocalVariableGen[] getLocalVariables() {
 391     int                size = variable_vec.size();
 392     LocalVariableGen[] lg   = new LocalVariableGen[size];
 393     variable_vec.toArray(lg);
 394 
 395     for(int i=0; i < size; i++) {
 396       if(lg[i].getStart() == null)
 397         lg[i].setStart(il.getStart());
 398 
 399       if(lg[i].getEnd() == null)
 400         lg[i].setEnd(il.getEnd());
 401     }
 402 
 403     if(size > 1)
 404       sort(lg, 0, size - 1);
 405 
 406     return lg;
 407   }
 408 
 409   /**
 410    * @return `LocalVariableTable' attribute of all the local variables of this method.
 411    */
 412   public LocalVariableTable getLocalVariableTable(ConstantPoolGen cp) {
 413     LocalVariableGen[] lg   = getLocalVariables();
 414     int                size = lg.length;
 415     LocalVariable[]    lv   = new LocalVariable[size];
 416 
 417     for(int i=0; i < size; i++)
 418       lv[i] = lg[i].getLocalVariable(cp);
 419 
 420     return new LocalVariableTable(cp.addUtf8("LocalVariableTable"),
 421                                   2 + lv.length * 10, lv, cp.getConstantPool());
 422   }
 423 
 424   /**
 425    * Give an instruction a line number corresponding to the source code line.
 426    *
 427    * @param ih instruction to tag
 428    * @return new line number object
 429    * @see LineNumber
 430    */
 431   public LineNumberGen addLineNumber(InstructionHandle ih, int src_line) {
 432     LineNumberGen l = new LineNumberGen(ih, src_line);
 433     line_number_vec.add(l);
 434     return l;
 435   }
 436 
 437   /**
 438    * Remove a line number.
 439    */
 440   public void removeLineNumber(LineNumberGen l) {
 441     line_number_vec.remove(l);
 442   }
 443 
 444   /**
 445    * Remove all line numbers.
 446    */
 447   public void removeLineNumbers() {
 448     line_number_vec.clear();
 449   }
 450 
 451   /*
 452    * @return array of line numbers
 453    */
 454   public LineNumberGen[] getLineNumbers() {
 455     LineNumberGen[] lg = new LineNumberGen[line_number_vec.size()];
 456     line_number_vec.toArray(lg);
 457     return lg;
 458   }
 459 
 460   /**
 461    * @return `LineNumberTable' attribute of all the local variables of this method.
 462    */
 463   public LineNumberTable getLineNumberTable(ConstantPoolGen cp) {
 464     int          size = line_number_vec.size();
 465     LineNumber[] ln   = new LineNumber[size];
 466 
 467     try {
 468       for(int i=0; i < size; i++)
 469         ln[i] = ((LineNumberGen)line_number_vec.get(i)).getLineNumber();
 470     } catch(ArrayIndexOutOfBoundsException e) {} // Never occurs
 471 
 472     return new LineNumberTable(cp.addUtf8("LineNumberTable"),
 473                                2 + ln.length * 4, ln, cp.getConstantPool());
 474   }
 475 
 476   /**
 477    * Add an exception handler, i.e., specify region where a handler is active and an
 478    * instruction where the actual handling is done.
 479    *
 480    * @param start_pc Start of region (inclusive)
 481    * @param end_pc End of region (inclusive)
 482    * @param handler_pc Where handling is done
 483    * @param catch_type class type of handled exception or null if any
 484    * exception is handled
 485    * @return new exception handler object
 486    */
 487   public CodeExceptionGen addExceptionHandler(InstructionHandle start_pc,
 488                                               InstructionHandle end_pc,
 489                                               InstructionHandle handler_pc,
 490                                               ObjectType catch_type) {
 491     if((start_pc == null) || (end_pc == null) || (handler_pc == null))
 492       throw new ClassGenException("Exception handler target is null instruction");
 493 
 494     CodeExceptionGen c = new CodeExceptionGen(start_pc, end_pc,
 495                                               handler_pc, catch_type);
 496     exception_vec.add(c);
 497     return c;
 498   }
 499 
 500   /**
 501    * Remove an exception handler.
 502    */
 503   public void removeExceptionHandler(CodeExceptionGen c) {
 504     exception_vec.remove(c);
 505   }
 506 
 507   /**
 508    * Remove all line numbers.
 509    */
 510   public void removeExceptionHandlers() {
 511     exception_vec.clear();
 512   }
 513 
 514   /*
 515    * @return array of declared exception handlers
 516    */
 517   public CodeExceptionGen[] getExceptionHandlers() {
 518     CodeExceptionGen[] cg   = new CodeExceptionGen[exception_vec.size()];
 519     exception_vec.toArray(cg);
 520     return cg;
 521   }
 522 
 523   /**
 524    * @return code exceptions for `Code' attribute
 525    */
 526   private CodeException[] getCodeExceptions() {
 527     int             size  = exception_vec.size();
 528     CodeException[] c_exc = new CodeException[size];
 529 
 530     try {
 531       for(int i=0; i < size; i++) {
 532         CodeExceptionGen c = (CodeExceptionGen)exception_vec.get(i);
 533         c_exc[i] = c.getCodeException(cp);
 534       }
 535     } catch(ArrayIndexOutOfBoundsException e) {}
 536 
 537     return c_exc;
 538   }
 539 
 540   /**
 541    * Add an exception possibly thrown by this method.
 542    *
 543    * @param class_name (fully qualified) name of exception
 544    */
 545   public void addException(String class_name) {
 546     throws_vec.add(class_name);
 547   }
 548 
 549   /**
 550    * Remove an exception.
 551    */
 552   public void removeException(String c) {
 553     throws_vec.remove(c);
 554   }
 555 
 556   /**
 557    * Remove all exceptions.
 558    */
 559   public void removeExceptions() {
 560     throws_vec.clear();
 561   }
 562 
 563   /*
 564    * @return array of thrown exceptions
 565    */
 566   public String[] getExceptions() {
 567     String[] e = new String[throws_vec.size()];
 568     throws_vec.toArray(e);
 569     return e;
 570   }
 571 
 572   /**
 573    * @return `Exceptions' attribute of all the exceptions thrown by this method.
 574    */
 575   private ExceptionTable getExceptionTable(ConstantPoolGen cp) {
 576     int   size = throws_vec.size();
 577     int[] ex   = new int[size];
 578 
 579     try {
 580       for(int i=0; i < size; i++)
 581         ex[i] = cp.addClass((String)throws_vec.get(i));
 582     } catch(ArrayIndexOutOfBoundsException e) {}
 583 
 584     return new ExceptionTable(cp.addUtf8("Exceptions"),
 585                               2 + 2 * size, ex, cp.getConstantPool());
 586   }
 587 
 588   /**
 589    * Add an attribute to the code. Currently, the JVM knows about the
 590    * LineNumberTable, LocalVariableTable and StackMap attributes,
 591    * where the former two will be generated automatically and the
 592    * latter is used for the MIDP only. Other attributes will be
 593    * ignored by the JVM but do no harm.
 594    *
 595    * @param a attribute to be added
 596    */
 597   public void addCodeAttribute(Attribute a) { code_attrs_vec.add(a); }
 598 
 599   /**
 600    * Remove a code attribute.
 601    */
 602   public void removeCodeAttribute(Attribute a) { code_attrs_vec.remove(a); }
 603 
 604   /**
 605    * Remove all code attributes.
 606    */
 607   public void removeCodeAttributes() {
 608     code_attrs_vec.clear();
 609   }
 610 
 611   /**
 612    * @return all attributes of this method.
 613    */
 614   public Attribute[] getCodeAttributes() {
 615     Attribute[] attributes = new Attribute[code_attrs_vec.size()];
 616     code_attrs_vec.toArray(attributes);
 617     return attributes;
 618   }
 619 
 620   /**
 621    * Get method object. Never forget to call setMaxStack() or setMaxStack(max), respectively,
 622    * before calling this method (the same applies for max locals).
 623    *
 624    * @return method object
 625    */
 626   public Method getMethod() {
 627     String signature       = getSignature();
 628     int    name_index      = cp.addUtf8(name);
 629     int    signature_index = cp.addUtf8(signature);
 630 
 631     /* Also updates positions of instructions, i.e., their indices
 632      */
 633     byte[] byte_code = null;
 634 
 635     if(il != null)
 636       byte_code = il.getByteCode();
 637 
 638     LineNumberTable    lnt = null;
 639     LocalVariableTable lvt = null;
 640 
 641     /* Create LocalVariableTable and LineNumberTable attributes (for debuggers, e.g.)
 642      */
 643     if((variable_vec.size() > 0) && !strip_attributes)
 644       addCodeAttribute(lvt = getLocalVariableTable(cp));
 645 
 646     if((line_number_vec.size() > 0) && !strip_attributes)
 647       addCodeAttribute(lnt = getLineNumberTable(cp));
 648 
 649     Attribute[] code_attrs = getCodeAttributes();
 650 
 651     /* Each attribute causes 6 additional header bytes
 652      */
 653     int                attrs_len  = 0;
 654     for(int i=0; i < code_attrs.length; i++)
 655       attrs_len += (code_attrs[i].getLength() + 6);
 656 
 657     CodeException[] c_exc   = getCodeExceptions();
 658     int             exc_len = c_exc.length * 8; // Every entry takes 8 bytes
 659 
 660     Code code = null;
 661 
 662     if((il != null) && !isAbstract()) {
 663       // Remove any stale code attribute
 664       Attribute[] attributes = getAttributes();
 665       for(int i=0; i < attributes.length; i++) {
 666         Attribute a = attributes[i];
 667 
 668         if(a instanceof Code)
 669           removeAttribute(a);
 670       }
 671 
 672       code = new Code(cp.addUtf8("Code"),
 673                       8 + byte_code.length + // prologue byte code
 674                       2 + exc_len +          // exceptions
 675                       2 + attrs_len,         // attributes
 676                       max_stack, max_locals,
 677                       byte_code, c_exc,
 678                       code_attrs,
 679                       cp.getConstantPool());
 680 
 681       addAttribute(code);
 682     }
 683 
 684     ExceptionTable et = null;
 685 
 686     if(throws_vec.size() > 0)
 687       addAttribute(et = getExceptionTable(cp)); // Add `Exceptions' if there are "throws" clauses
 688 
 689     Method m = new Method(access_flags, name_index, signature_index,
 690                           getAttributes(), cp.getConstantPool());
 691 
 692     // Undo effects of adding attributes
 693     if(lvt != null)  removeCodeAttribute(lvt);
 694     if(lnt != null)  removeCodeAttribute(lnt);
 695     if(code != null) removeAttribute(code);
 696     if(et != null)   removeAttribute(et);
 697 
 698     return m;
 699   }
 700 
 701   /**
 702    * Remove all NOPs from the instruction list (if possible) and update every
 703    * object refering to them, i.e., branch instructions, local variables and
 704    * exception handlers.
 705    */
 706   public void removeNOPs() {
 707     if(il != null) {
 708       InstructionHandle next;
 709       /* Check branch instructions.
 710        */
 711       for(InstructionHandle ih = il.getStart(); ih != null; ih = next) {
 712         next = ih.next;
 713 
 714         if((next != null) && (ih.getInstruction() instanceof NOP)) {
 715           try {
 716             il.delete(ih);
 717           } catch(TargetLostException e) {
 718             InstructionHandle[] targets = e.getTargets();
 719 
 720             for(int i=0; i < targets.length; i++) {
 721               InstructionTargeter[] targeters = targets[i].getTargeters();
 722 
 723               for(int j=0; j < targeters.length; j++)
 724                 targeters[j].updateTarget(targets[i], next);
 725             }
 726           }
 727         }
 728       }
 729     }
 730   }
 731 
 732   /**
 733    * Set maximum number of local variables.
 734    */
 735   public void   setMaxLocals(int m)  { max_locals = m; }
 736   public int    getMaxLocals()       { return max_locals; }
 737 
 738   /**
 739    * Set maximum stack size for this method.
 740    */
 741   public void   setMaxStack(int m)  { max_stack = m; }
 742   public int    getMaxStack()       { return max_stack; }
 743 
 744   /** @return class that contains this method
 745    */
 746   public String getClassName()                     { return class_name; }
 747   public void   setClassName(String class_name)    { this.class_name = class_name; }
 748 
 749   public void   setReturnType(Type return_type)    { setType(return_type); }
 750   public Type   getReturnType()                    { return getType(); }
 751 
 752   public void   setArgumentTypes(Type[] arg_types)  { this.arg_types = arg_types; }
 753   public Type[] getArgumentTypes()                  { return (Type[])arg_types.clone(); }
 754   public void   setArgumentType(int i, Type type)       { arg_types[i] = type; }
 755   public Type   getArgumentType(int i)                  { return arg_types[i]; }
 756 
 757   public void     setArgumentNames(String[] arg_names) { this.arg_names = arg_names; }
 758   public String[] getArgumentNames()                   { return (String[])arg_names.clone(); }
 759   public void     setArgumentName(int i, String name)     { arg_names[i] = name; }
 760   public String   getArgumentName(int i)                  { return arg_names[i]; }
 761 
 762   public InstructionList getInstructionList()                    { return il; }
 763   public void            setInstructionList(InstructionList il)  { this.il = il; }
 764 
 765   public String getSignature() {
 766     return Type.getMethodSignature(type, arg_types);
 767   }
 768 
 769   /**
 770    * Computes max. stack size by performing control flow analysis.
 771    */
 772   public void setMaxStack() {
 773     if(il != null)
 774       max_stack = getMaxStack(cp, il, getExceptionHandlers());
 775     else
 776       max_stack = 0;
 777   }
 778 
 779   /**
 780    * Compute maximum number of local variables.
 781    */
 782   public void setMaxLocals() {
 783     if(il != null) {
 784       int max = isStatic()? 0 : 1;
 785 
 786       if(arg_types != null)
 787         for(int i=0; i < arg_types.length; i++)
 788           max += arg_types[i].getSize();
 789 
 790       for(InstructionHandle ih = il.getStart(); ih != null; ih = ih.getNext()) {
 791         Instruction ins = ih.getInstruction();
 792 
 793         if((ins instanceof LocalVariableInstruction) ||
 794            (ins instanceof RET) || (ins instanceof IINC))
 795         {
 796           int index = ((IndexedInstruction)ins).getIndex() +
 797             ((TypedInstruction)ins).getType(cp).getSize();
 798 
 799           if(index > max)
 800             max = index;
 801         }
 802       }
 803 
 804       max_locals = max;
 805     } else
 806       max_locals = 0;
 807   }
 808 
 809   /** Do not/Do produce attributes code attributesLineNumberTable and
 810    * LocalVariableTable, like javac -O
 811    */
 812   public void stripAttributes(boolean flag) { strip_attributes = flag; }
 813 
 814   static final class BranchTarget {
 815     InstructionHandle target;
 816     int               stackDepth;
 817 
 818     BranchTarget(InstructionHandle target, int stackDepth) {
 819       this.target = target;
 820       this.stackDepth = stackDepth;
 821     }
 822   }
 823 
 824   static final class BranchStack {
 825     Stack     branchTargets  = new Stack();
 826     Hashtable visitedTargets = new Hashtable();
 827 
 828     public void push(InstructionHandle target, int stackDepth) {
 829       if(visited(target))
 830         return;
 831 
 832       branchTargets.push(visit(target, stackDepth));
 833     }
 834 
 835     public BranchTarget pop() {
 836       if(!branchTargets.empty()) {
 837         BranchTarget bt = (BranchTarget) branchTargets.pop();
 838         return bt;
 839       }
 840 
 841       return null;
 842     }
 843 
 844     private final BranchTarget visit(InstructionHandle target, int stackDepth) {
 845       BranchTarget bt = new BranchTarget(target, stackDepth);
 846       visitedTargets.put(target, bt);
 847 
 848       return bt;
 849     }
 850 
 851     private final boolean visited(InstructionHandle target) {
 852       return (visitedTargets.get(target) != null);
 853     }
 854   }
 855 
 856   /**
 857    * Computes stack usage of an instruction list by performing control flow analysis.
 858    *
 859    * @return maximum stack depth used by method
 860    */
 861   public static int getMaxStack(ConstantPoolGen cp, InstructionList il, CodeExceptionGen[] et) {
 862     BranchStack branchTargets = new BranchStack();
 863 
 864     /* Initially, populate the branch stack with the exception
 865      * handlers, because these aren't (necessarily) branched to
 866      * explicitly. in each case, the stack will have depth 1,
 867      * containing the exception object.
 868      */
 869     for (int i = 0; i < et.length; i++) {
 870       InstructionHandle handler_pc = et[i].getHandlerPC();
 871       if (handler_pc != null)
 872         branchTargets.push(handler_pc, 1);
 873     }
 874 
 875     int               stackDepth = 0, maxStackDepth = 0;
 876     InstructionHandle ih         = il.getStart();
 877 
 878     while(ih != null) {
 879       Instruction instruction = ih.getInstruction();
 880       short opcode = instruction.getOpcode();
 881       int delta = instruction.produceStack(cp) - instruction.consumeStack(cp);
 882 
 883       stackDepth += delta;
 884       if(stackDepth > maxStackDepth)
 885         maxStackDepth = stackDepth;
 886 
 887       // choose the next instruction based on whether current is a branch.
 888       if(instruction instanceof BranchInstruction) {
 889         BranchInstruction branch = (BranchInstruction) instruction;
 890         if(instruction instanceof Select) {
 891           // explore all of the select's targets. the default target is handled below.
 892           Select select = (Select) branch;
 893           InstructionHandle[] targets = select.getTargets();
 894           for (int i = 0; i < targets.length; i++)
 895             branchTargets.push(targets[i], stackDepth);
 896           // nothing to fall through to.
 897           ih = null;
 898         } else if(!(branch instanceof IfInstruction)) {
 899           // if an instruction that comes back to following PC,
 900           // push next instruction, with stack depth reduced by 1.
 901           if(opcode == Constants.JSR || opcode == Constants.JSR_W)
 902             branchTargets.push(ih.getNext(), stackDepth - 1);
 903           ih = null;
 904         }
 905         // for all branches, the target of the branch is pushed on the branch stack.
 906         // conditional branches have a fall through case, selects don't, and
 907         // jsr/jsr_w return to the next instruction.
 908         branchTargets.push(branch.getTarget(), stackDepth);
 909       } else {
 910         // check for instructions that terminate the method.
 911         if(opcode == Constants.ATHROW || opcode == Constants.RET ||
 912            (opcode >= Constants.IRETURN && opcode <= Constants.RETURN))
 913           ih = null;
 914       }
 915       // normal case, go to the next instruction.
 916       if(ih != null)
 917         ih = ih.getNext();
 918       // if we have no more instructions, see if there are any deferred branches to explore.
 919       if(ih == null) {
 920         BranchTarget bt = branchTargets.pop();
 921         if (bt != null) {
 922           ih = bt.target;
 923           stackDepth = bt.stackDepth;
 924         }
 925       }
 926     }
 927 
 928     return maxStackDepth;
 929   }
 930 
 931   private ArrayList observers;
 932 
 933   /** Add observer for this object.
 934    */
 935   public void addObserver(MethodObserver o) {
 936     if(observers == null)
 937       observers = new ArrayList();
 938 
 939     observers.add(o);
 940   }
 941 
 942   /** Remove observer for this object.
 943    */
 944   public void removeObserver(MethodObserver o) {
 945     if(observers != null)
 946       observers.remove(o);
 947   }
 948 
 949   /** Call notify() method on all observers. This method is not called
 950    * automatically whenever the state has changed, but has to be
 951    * called by the user after he has finished editing the object.
 952    */
 953   public void update() {
 954     if(observers != null)
 955       for(Iterator e = observers.iterator(); e.hasNext(); )
 956         ((MethodObserver)e.next()).notify(this);
 957   }
 958 
 959   /**
 960    * Return string representation close to declaration format,
 961    * `public static void _main(String[]) throws IOException', e.g.
 962    *
 963    * @return String representation of the method.
 964    */
 965   public final String toString() {
 966     String access    = Utility.accessToString(access_flags);
 967     String signature = Type.getMethodSignature(type, arg_types);
 968 
 969     signature = Utility.methodSignatureToString(signature, name, access,
 970                                                 true, getLocalVariableTable(cp));
 971 
 972     StringBuffer buf = new StringBuffer(signature);
 973 
 974     if(throws_vec.size() > 0) {
 975       for(Iterator e = throws_vec.iterator(); e.hasNext(); )
 976         buf.append("\n\t\tthrows " + e.next());
 977     }
 978 
 979     return buf.toString();
 980   }
 981 
 982   /** @return deep copy of this method
 983    */
 984   public MethodGen copy(String class_name, ConstantPoolGen cp) {
 985     Method    m  = ((MethodGen)clone()).getMethod();
 986     MethodGen mg = new MethodGen(m, class_name, this.cp);
 987 
 988     if(this.cp != cp) {
 989       mg.setConstantPool(cp);
 990       mg.getInstructionList().replaceConstantPool(this.cp, cp);
 991     }
 992 
 993     return mg;
 994   }
 995 }