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[] oldLV = ((LocalVariableTypeTable) a).getLocalVariableTypeTable();
 263              int lvLength = oldLV.length;
 264              LocalVariable[] newLV = new LocalVariable[lvLength];
 265              for (int k = 0; k < lvLength; k++) {
 266                  LocalVariable l = oldLV[k];
 267                  InstructionHandle start = il.findHandle(l.getStartPC());
 268                  InstructionHandle end = il.findHandle(l.getStartPC() + l.getLength());
 269                  // Repair malformed handles
 270                  if (null == start) {
 271                      start = il.getStart();
 272                  }
 273                  if (null == end) {
 274                      end = il.getEnd();
 275                  }
 276                  LocalVariable newVar = new LocalVariable(l);
 277                  int startPosition = start.getPosition();
 278                  newVar.setStartPC(startPosition);
 279                  newVar.setLength(end.getPosition() - startPosition);
 280                  newLV[k] = newVar;
 281               }
 282               LocalVariableTypeTable newLVTT = new LocalVariableTypeTable(
 283                       (LocalVariableTypeTable)a);
 284               newLVTT.setLocalVariableTable(newLV);
 285               addCodeAttribute(newLVTT);
 286           } else
 287             addCodeAttribute(a);
 288         }
 289       } else if(a instanceof ExceptionTable) {
 290         String[] names = ((ExceptionTable)a).getExceptionNames();
 291         for(int j=0; j < names.length; j++)
 292           addException(names[j]);
 293       } else
 294         addAttribute(a);
 295     }
 296   }
 297 
 298   /**
 299    * Adds a local variable to this method.
 300    *
 301    * @param name variable name
 302    * @param type variable type
 303    * @param slot the index of the local variable, if type is long or double, the next available
 304    * index is slot+2
 305    * @param start from where the variable is valid
 306    * @param end until where the variable is valid
 307    * @return new local variable object
 308    * @see LocalVariable
 309    */
 310   public LocalVariableGen addLocalVariable(String name, Type type, int slot,
 311                                            InstructionHandle start,
 312                                            InstructionHandle end) {
 313     byte t = type.getType();
 314 
 315     if(t != Constants.T_ADDRESS) {
 316       int  add = type.getSize();
 317 
 318       if(slot + add > max_locals)
 319         max_locals = slot + add;
 320 
 321       LocalVariableGen l = new LocalVariableGen(slot, name, type, start, end);
 322       int i;
 323 
 324       if((i = variable_vec.indexOf(l)) >= 0) // Overwrite if necessary
 325         variable_vec.set(i, l);
 326       else
 327         variable_vec.add(l);
 328 
 329       return l;
 330     } else {
 331       throw new IllegalArgumentException("Can not use " + type +
 332                                          " as type for local variable");
 333 
 334     }
 335   }
 336 
 337   /**
 338    * Adds a local variable to this method and assigns an index automatically.
 339    *
 340    * @param name variable name
 341    * @param type variable type
 342    * @param start from where the variable is valid, if this is null,
 343    * it is valid from the start
 344    * @param end until where the variable is valid, if this is null,
 345    * it is valid to the end
 346    * @return new local variable object
 347    * @see LocalVariable
 348    */
 349   public LocalVariableGen addLocalVariable(String name, Type type,
 350                                            InstructionHandle start,
 351                                            InstructionHandle end) {
 352     return addLocalVariable(name, type, max_locals, start, end);
 353   }
 354 
 355   /**
 356    * Remove a local variable, its slot will not be reused, if you do not use addLocalVariable
 357    * with an explicit index argument.
 358    */
 359   public void removeLocalVariable(LocalVariableGen l) {
 360     variable_vec.remove(l);
 361   }
 362 
 363   /**
 364    * Remove all local variables.
 365    */
 366   public void removeLocalVariables() {
 367     variable_vec.clear();
 368   }
 369 
 370   /**
 371    * Sort local variables by index
 372    */
 373   private static final void sort(LocalVariableGen[] vars, int l, int r) {
 374     int i = l, j = r;
 375     int m = vars[(l + r) / 2].getIndex();
 376     LocalVariableGen h;
 377 
 378     do {
 379       while(vars[i].getIndex() < m) i++;
 380       while(m < vars[j].getIndex()) j--;
 381 
 382       if(i <= j) {
 383         h=vars[i]; vars[i]=vars[j]; vars[j]=h; // Swap elements
 384         i++; j--;
 385       }
 386     } while(i <= j);
 387 
 388     if(l < j) sort(vars, l, j);
 389     if(i < r) sort(vars, i, r);
 390   }
 391 
 392   /*
 393    * If the range of the variable has not been set yet, it will be set to be valid from
 394    * the start to the end of the instruction list.
 395    *
 396    * @return array of declared local variables sorted by index
 397    */
 398   public LocalVariableGen[] getLocalVariables() {
 399     int                size = variable_vec.size();
 400     LocalVariableGen[] lg   = new LocalVariableGen[size];
 401     variable_vec.toArray(lg);
 402 
 403     for(int i=0; i < size; i++) {
 404       if(lg[i].getStart() == null)
 405         lg[i].setStart(il.getStart());
 406 
 407       if(lg[i].getEnd() == null)
 408         lg[i].setEnd(il.getEnd());
 409     }
 410 
 411     if(size > 1)
 412       sort(lg, 0, size - 1);
 413 
 414     return lg;
 415   }
 416 
 417   /**
 418    * @return `LocalVariableTable' attribute of all the local variables of this method.
 419    */
 420   public LocalVariableTable getLocalVariableTable(ConstantPoolGen cp) {
 421     LocalVariableGen[] lg   = getLocalVariables();
 422     int                size = lg.length;
 423     LocalVariable[]    lv   = new LocalVariable[size];
 424 
 425     for(int i=0; i < size; i++)
 426       lv[i] = lg[i].getLocalVariable(cp);
 427 
 428     return new LocalVariableTable(cp.addUtf8("LocalVariableTable"),
 429                                   2 + lv.length * 10, lv, cp.getConstantPool());
 430   }
 431 
 432   /**
 433    * Give an instruction a line number corresponding to the source code line.
 434    *
 435    * @param ih instruction to tag
 436    * @return new line number object
 437    * @see LineNumber
 438    */
 439   public LineNumberGen addLineNumber(InstructionHandle ih, int src_line) {
 440     LineNumberGen l = new LineNumberGen(ih, src_line);
 441     line_number_vec.add(l);
 442     return l;
 443   }
 444 
 445   /**
 446    * Remove a line number.
 447    */
 448   public void removeLineNumber(LineNumberGen l) {
 449     line_number_vec.remove(l);
 450   }
 451 
 452   /**
 453    * Remove all line numbers.
 454    */
 455   public void removeLineNumbers() {
 456     line_number_vec.clear();
 457   }
 458 
 459   /*
 460    * @return array of line numbers
 461    */
 462   public LineNumberGen[] getLineNumbers() {
 463     LineNumberGen[] lg = new LineNumberGen[line_number_vec.size()];
 464     line_number_vec.toArray(lg);
 465     return lg;
 466   }
 467 
 468   /**
 469    * @return `LineNumberTable' attribute of all the local variables of this method.
 470    */
 471   public LineNumberTable getLineNumberTable(ConstantPoolGen cp) {
 472     int          size = line_number_vec.size();
 473     LineNumber[] ln   = new LineNumber[size];
 474 
 475     try {
 476       for(int i=0; i < size; i++)
 477         ln[i] = ((LineNumberGen)line_number_vec.get(i)).getLineNumber();
 478     } catch(ArrayIndexOutOfBoundsException e) {} // Never occurs
 479 
 480     return new LineNumberTable(cp.addUtf8("LineNumberTable"),
 481                                2 + ln.length * 4, ln, cp.getConstantPool());
 482   }
 483 
 484   /**
 485    * Add an exception handler, i.e., specify region where a handler is active and an
 486    * instruction where the actual handling is done.
 487    *
 488    * @param start_pc Start of region (inclusive)
 489    * @param end_pc End of region (inclusive)
 490    * @param handler_pc Where handling is done
 491    * @param catch_type class type of handled exception or null if any
 492    * exception is handled
 493    * @return new exception handler object
 494    */
 495   public CodeExceptionGen addExceptionHandler(InstructionHandle start_pc,
 496                                               InstructionHandle end_pc,
 497                                               InstructionHandle handler_pc,
 498                                               ObjectType catch_type) {
 499     if((start_pc == null) || (end_pc == null) || (handler_pc == null))
 500       throw new ClassGenException("Exception handler target is null instruction");
 501 
 502     CodeExceptionGen c = new CodeExceptionGen(start_pc, end_pc,
 503                                               handler_pc, catch_type);
 504     exception_vec.add(c);
 505     return c;
 506   }
 507 
 508   /**
 509    * Remove an exception handler.
 510    */
 511   public void removeExceptionHandler(CodeExceptionGen c) {
 512     exception_vec.remove(c);
 513   }
 514 
 515   /**
 516    * Remove all line numbers.
 517    */
 518   public void removeExceptionHandlers() {
 519     exception_vec.clear();
 520   }
 521 
 522   /*
 523    * @return array of declared exception handlers
 524    */
 525   public CodeExceptionGen[] getExceptionHandlers() {
 526     CodeExceptionGen[] cg   = new CodeExceptionGen[exception_vec.size()];
 527     exception_vec.toArray(cg);
 528     return cg;
 529   }
 530 
 531   /**
 532    * @return code exceptions for `Code' attribute
 533    */
 534   private CodeException[] getCodeExceptions() {
 535     int             size  = exception_vec.size();
 536     CodeException[] c_exc = new CodeException[size];
 537 
 538     try {
 539       for(int i=0; i < size; i++) {
 540         CodeExceptionGen c = (CodeExceptionGen)exception_vec.get(i);
 541         c_exc[i] = c.getCodeException(cp);
 542       }
 543     } catch(ArrayIndexOutOfBoundsException e) {}
 544 
 545     return c_exc;
 546   }
 547 
 548   /**
 549    * Add an exception possibly thrown by this method.
 550    *
 551    * @param class_name (fully qualified) name of exception
 552    */
 553   public void addException(String class_name) {
 554     throws_vec.add(class_name);
 555   }
 556 
 557   /**
 558    * Remove an exception.
 559    */
 560   public void removeException(String c) {
 561     throws_vec.remove(c);
 562   }
 563 
 564   /**
 565    * Remove all exceptions.
 566    */
 567   public void removeExceptions() {
 568     throws_vec.clear();
 569   }
 570 
 571   /*
 572    * @return array of thrown exceptions
 573    */
 574   public String[] getExceptions() {
 575     String[] e = new String[throws_vec.size()];
 576     throws_vec.toArray(e);
 577     return e;
 578   }
 579 
 580   /**
 581    * @return `Exceptions' attribute of all the exceptions thrown by this method.
 582    */
 583   private ExceptionTable getExceptionTable(ConstantPoolGen cp) {
 584     int   size = throws_vec.size();
 585     int[] ex   = new int[size];
 586 
 587     try {
 588       for(int i=0; i < size; i++)
 589         ex[i] = cp.addClass((String)throws_vec.get(i));
 590     } catch(ArrayIndexOutOfBoundsException e) {}
 591 
 592     return new ExceptionTable(cp.addUtf8("Exceptions"),
 593                               2 + 2 * size, ex, cp.getConstantPool());
 594   }
 595 
 596   /**
 597    * Add an attribute to the code. Currently, the JVM knows about the
 598    * LineNumberTable, LocalVariableTable and StackMap attributes,
 599    * where the former two will be generated automatically and the
 600    * latter is used for the MIDP only. Other attributes will be
 601    * ignored by the JVM but do no harm.
 602    *
 603    * @param a attribute to be added
 604    */
 605   public void addCodeAttribute(Attribute a) { code_attrs_vec.add(a); }
 606 
 607   /**
 608    * Remove a code attribute.
 609    */
 610   public void removeCodeAttribute(Attribute a) { code_attrs_vec.remove(a); }
 611 
 612   /**
 613    * Remove all code attributes.
 614    */
 615   public void removeCodeAttributes() {
 616     code_attrs_vec.clear();
 617   }
 618 
 619   /**
 620    * @return all attributes of this method.
 621    */
 622   public Attribute[] getCodeAttributes() {
 623     Attribute[] attributes = new Attribute[code_attrs_vec.size()];
 624     code_attrs_vec.toArray(attributes);
 625     return attributes;
 626   }
 627 
 628   /**
 629    * Get method object. Never forget to call setMaxStack() or setMaxStack(max), respectively,
 630    * before calling this method (the same applies for max locals).
 631    *
 632    * @return method object
 633    */
 634   public Method getMethod() {
 635     String signature       = getSignature();
 636     int    name_index      = cp.addUtf8(name);
 637     int    signature_index = cp.addUtf8(signature);
 638 
 639     /* Also updates positions of instructions, i.e., their indices
 640      */
 641     byte[] byte_code = null;
 642 
 643     if(il != null)
 644       byte_code = il.getByteCode();
 645 
 646     LineNumberTable    lnt = null;
 647     LocalVariableTable lvt = null;
 648 
 649     /* Create LocalVariableTable and LineNumberTable attributes (for debuggers, e.g.)
 650      */
 651     if((variable_vec.size() > 0) && !strip_attributes)
 652       addCodeAttribute(lvt = getLocalVariableTable(cp));
 653 
 654     if((line_number_vec.size() > 0) && !strip_attributes)
 655       addCodeAttribute(lnt = getLineNumberTable(cp));
 656 
 657     Attribute[] code_attrs = getCodeAttributes();
 658 
 659     /* Each attribute causes 6 additional header bytes
 660      */
 661     int                attrs_len  = 0;
 662     for(int i=0; i < code_attrs.length; i++)
 663       attrs_len += (code_attrs[i].getLength() + 6);
 664 
 665     CodeException[] c_exc   = getCodeExceptions();
 666     int             exc_len = c_exc.length * 8; // Every entry takes 8 bytes
 667 
 668     Code code = null;
 669 
 670     if((il != null) && !isAbstract()) {
 671       // Remove any stale code attribute
 672       Attribute[] attributes = getAttributes();
 673       for(int i=0; i < attributes.length; i++) {
 674         Attribute a = attributes[i];
 675 
 676         if(a instanceof Code)
 677           removeAttribute(a);
 678       }
 679 
 680       code = new Code(cp.addUtf8("Code"),
 681                       8 + byte_code.length + // prologue byte code
 682                       2 + exc_len +          // exceptions
 683                       2 + attrs_len,         // attributes
 684                       max_stack, max_locals,
 685                       byte_code, c_exc,
 686                       code_attrs,
 687                       cp.getConstantPool());
 688 
 689       addAttribute(code);
 690     }
 691 
 692     ExceptionTable et = null;
 693 
 694     if(throws_vec.size() > 0)
 695       addAttribute(et = getExceptionTable(cp)); // Add `Exceptions' if there are "throws" clauses
 696 
 697     Method m = new Method(access_flags, name_index, signature_index,
 698                           getAttributes(), cp.getConstantPool());
 699 
 700     // Undo effects of adding attributes
 701     if(lvt != null)  removeCodeAttribute(lvt);
 702     if(lnt != null)  removeCodeAttribute(lnt);
 703     if(code != null) removeAttribute(code);
 704     if(et != null)   removeAttribute(et);
 705 
 706     return m;
 707   }
 708 
 709   /**
 710    * Remove all NOPs from the instruction list (if possible) and update every
 711    * object refering to them, i.e., branch instructions, local variables and
 712    * exception handlers.
 713    */
 714   public void removeNOPs() {
 715     if(il != null) {
 716       InstructionHandle next;
 717       /* Check branch instructions.
 718        */
 719       for(InstructionHandle ih = il.getStart(); ih != null; ih = next) {
 720         next = ih.next;
 721 
 722         if((next != null) && (ih.getInstruction() instanceof NOP)) {
 723           try {
 724             il.delete(ih);
 725           } catch(TargetLostException e) {
 726             InstructionHandle[] targets = e.getTargets();
 727 
 728             for(int i=0; i < targets.length; i++) {
 729               InstructionTargeter[] targeters = targets[i].getTargeters();
 730 
 731               for(int j=0; j < targeters.length; j++)
 732                 targeters[j].updateTarget(targets[i], next);
 733             }
 734           }
 735         }
 736       }
 737     }
 738   }
 739 
 740   /**
 741    * Set maximum number of local variables.
 742    */
 743   public void   setMaxLocals(int m)  { max_locals = m; }
 744   public int    getMaxLocals()       { return max_locals; }
 745 
 746   /**
 747    * Set maximum stack size for this method.
 748    */
 749   public void   setMaxStack(int m)  { max_stack = m; }
 750   public int    getMaxStack()       { return max_stack; }
 751 
 752   /** @return class that contains this method
 753    */
 754   public String getClassName()                     { return class_name; }
 755   public void   setClassName(String class_name)    { this.class_name = class_name; }
 756 
 757   public void   setReturnType(Type return_type)    { setType(return_type); }
 758   public Type   getReturnType()                    { return getType(); }
 759 
 760   public void   setArgumentTypes(Type[] arg_types)  { this.arg_types = arg_types; }
 761   public Type[] getArgumentTypes()                  { return (Type[])arg_types.clone(); }
 762   public void   setArgumentType(int i, Type type)       { arg_types[i] = type; }
 763   public Type   getArgumentType(int i)                  { return arg_types[i]; }
 764 
 765   public void     setArgumentNames(String[] arg_names) { this.arg_names = arg_names; }
 766   public String[] getArgumentNames()                   { return (String[])arg_names.clone(); }
 767   public void     setArgumentName(int i, String name)     { arg_names[i] = name; }
 768   public String   getArgumentName(int i)                  { return arg_names[i]; }
 769 
 770   public InstructionList getInstructionList()                    { return il; }
 771   public void            setInstructionList(InstructionList il)  { this.il = il; }
 772 
 773   public String getSignature() {
 774     return Type.getMethodSignature(type, arg_types);
 775   }
 776 
 777   /**
 778    * Computes max. stack size by performing control flow analysis.
 779    */
 780   public void setMaxStack() {
 781     if(il != null)
 782       max_stack = getMaxStack(cp, il, getExceptionHandlers());
 783     else
 784       max_stack = 0;
 785   }
 786 
 787   /**
 788    * Compute maximum number of local variables.
 789    */
 790   public void setMaxLocals() {
 791     if(il != null) {
 792       int max = isStatic()? 0 : 1;
 793 
 794       if(arg_types != null)
 795         for(int i=0; i < arg_types.length; i++)
 796           max += arg_types[i].getSize();
 797 
 798       for(InstructionHandle ih = il.getStart(); ih != null; ih = ih.getNext()) {
 799         Instruction ins = ih.getInstruction();
 800 
 801         if((ins instanceof LocalVariableInstruction) ||
 802            (ins instanceof RET) || (ins instanceof IINC))
 803         {
 804           int index = ((IndexedInstruction)ins).getIndex() +
 805             ((TypedInstruction)ins).getType(cp).getSize();
 806 
 807           if(index > max)
 808             max = index;
 809         }
 810       }
 811 
 812       max_locals = max;
 813     } else
 814       max_locals = 0;
 815   }
 816 
 817   /** Do not/Do produce attributes code attributesLineNumberTable and
 818    * LocalVariableTable, like javac -O
 819    */
 820   public void stripAttributes(boolean flag) { strip_attributes = flag; }
 821 
 822   static final class BranchTarget {
 823     InstructionHandle target;
 824     int               stackDepth;
 825 
 826     BranchTarget(InstructionHandle target, int stackDepth) {
 827       this.target = target;
 828       this.stackDepth = stackDepth;
 829     }
 830   }
 831 
 832   static final class BranchStack {
 833     Stack     branchTargets  = new Stack();
 834     Hashtable visitedTargets = new Hashtable();
 835 
 836     public void push(InstructionHandle target, int stackDepth) {
 837       if(visited(target))
 838         return;
 839 
 840       branchTargets.push(visit(target, stackDepth));
 841     }
 842 
 843     public BranchTarget pop() {
 844       if(!branchTargets.empty()) {
 845         BranchTarget bt = (BranchTarget) branchTargets.pop();
 846         return bt;
 847       }
 848 
 849       return null;
 850     }
 851 
 852     private final BranchTarget visit(InstructionHandle target, int stackDepth) {
 853       BranchTarget bt = new BranchTarget(target, stackDepth);
 854       visitedTargets.put(target, bt);
 855 
 856       return bt;
 857     }
 858 
 859     private final boolean visited(InstructionHandle target) {
 860       return (visitedTargets.get(target) != null);
 861     }
 862   }
 863 
 864   /**
 865    * Computes stack usage of an instruction list by performing control flow analysis.
 866    *
 867    * @return maximum stack depth used by method
 868    */
 869   public static int getMaxStack(ConstantPoolGen cp, InstructionList il, CodeExceptionGen[] et) {
 870     BranchStack branchTargets = new BranchStack();
 871 
 872     /* Initially, populate the branch stack with the exception
 873      * handlers, because these aren't (necessarily) branched to
 874      * explicitly. in each case, the stack will have depth 1,
 875      * containing the exception object.
 876      */
 877     for (int i = 0; i < et.length; i++) {
 878       InstructionHandle handler_pc = et[i].getHandlerPC();
 879       if (handler_pc != null)
 880         branchTargets.push(handler_pc, 1);
 881     }
 882 
 883     int               stackDepth = 0, maxStackDepth = 0;
 884     InstructionHandle ih         = il.getStart();
 885 
 886     while(ih != null) {
 887       Instruction instruction = ih.getInstruction();
 888       short opcode = instruction.getOpcode();
 889       int delta = instruction.produceStack(cp) - instruction.consumeStack(cp);
 890 
 891       stackDepth += delta;
 892       if(stackDepth > maxStackDepth)
 893         maxStackDepth = stackDepth;
 894 
 895       // choose the next instruction based on whether current is a branch.
 896       if(instruction instanceof BranchInstruction) {
 897         BranchInstruction branch = (BranchInstruction) instruction;
 898         if(instruction instanceof Select) {
 899           // explore all of the select's targets. the default target is handled below.
 900           Select select = (Select) branch;
 901           InstructionHandle[] targets = select.getTargets();
 902           for (int i = 0; i < targets.length; i++)
 903             branchTargets.push(targets[i], stackDepth);
 904           // nothing to fall through to.
 905           ih = null;
 906         } else if(!(branch instanceof IfInstruction)) {
 907           // if an instruction that comes back to following PC,
 908           // push next instruction, with stack depth reduced by 1.
 909           if(opcode == Constants.JSR || opcode == Constants.JSR_W)
 910             branchTargets.push(ih.getNext(), stackDepth - 1);
 911           ih = null;
 912         }
 913         // for all branches, the target of the branch is pushed on the branch stack.
 914         // conditional branches have a fall through case, selects don't, and
 915         // jsr/jsr_w return to the next instruction.
 916         branchTargets.push(branch.getTarget(), stackDepth);
 917       } else {
 918         // check for instructions that terminate the method.
 919         if(opcode == Constants.ATHROW || opcode == Constants.RET ||
 920            (opcode >= Constants.IRETURN && opcode <= Constants.RETURN))
 921           ih = null;
 922       }
 923       // normal case, go to the next instruction.
 924       if(ih != null)
 925         ih = ih.getNext();
 926       // if we have no more instructions, see if there are any deferred branches to explore.
 927       if(ih == null) {
 928         BranchTarget bt = branchTargets.pop();
 929         if (bt != null) {
 930           ih = bt.target;
 931           stackDepth = bt.stackDepth;
 932         }
 933       }
 934     }
 935 
 936     return maxStackDepth;
 937   }
 938 
 939   private ArrayList observers;
 940 
 941   /** Add observer for this object.
 942    */
 943   public void addObserver(MethodObserver o) {
 944     if(observers == null)
 945       observers = new ArrayList();
 946 
 947     observers.add(o);
 948   }
 949 
 950   /** Remove observer for this object.
 951    */
 952   public void removeObserver(MethodObserver o) {
 953     if(observers != null)
 954       observers.remove(o);
 955   }
 956 
 957   /** Call notify() method on all observers. This method is not called
 958    * automatically whenever the state has changed, but has to be
 959    * called by the user after he has finished editing the object.
 960    */
 961   public void update() {
 962     if(observers != null)
 963       for(Iterator e = observers.iterator(); e.hasNext(); )
 964         ((MethodObserver)e.next()).notify(this);
 965   }
 966 
 967   /**
 968    * Return string representation close to declaration format,
 969    * `public static void _main(String[]) throws IOException', e.g.
 970    *
 971    * @return String representation of the method.
 972    */
 973   public final String toString() {
 974     String access    = Utility.accessToString(access_flags);
 975     String signature = Type.getMethodSignature(type, arg_types);
 976 
 977     signature = Utility.methodSignatureToString(signature, name, access,
 978                                                 true, getLocalVariableTable(cp));
 979 
 980     StringBuffer buf = new StringBuffer(signature);
 981 
 982     if(throws_vec.size() > 0) {
 983       for(Iterator e = throws_vec.iterator(); e.hasNext(); )
 984         buf.append("\n\t\tthrows " + e.next());
 985     }
 986 
 987     return buf.toString();
 988   }
 989 
 990   /** @return deep copy of this method
 991    */
 992   public MethodGen copy(String class_name, ConstantPoolGen cp) {
 993     Method    m  = ((MethodGen)clone()).getMethod();
 994     MethodGen mg = new MethodGen(m, class_name, this.cp);
 995 
 996     if(this.cp != cp) {
 997       mg.setConstantPool(cp);
 998       mg.getInstructionList().replaceConstantPool(this.cp, cp);
 999     }
1000 
1001     return mg;
1002   }
1003 }