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