InstructionList xref

View Javadoc

    1   /*
    2    * Copyright  2000-2004 The Apache Software Foundation
    3    *
    4    *  Licensed under the Apache License, Version 2.0 (the "License");
    5    *  you may not use this file except in compliance with the License.
    6    *  You may obtain a copy of the License at
    7    *
    8    *      http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   *  Unless required by applicable law or agreed to in writing, software
    11   *  distributed under the License is distributed on an "AS IS" BASIS,
    12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   *  See the License for the specific language governing permissions and
    14   *  limitations under the License.
    15   *
    16   */
    17  package org.apache.bcel.generic;
    18
    19  import java.io.ByteArrayOutputStream;
    20  import java.io.DataOutputStream;
    21  import java.io.IOException;
    22  import java.io.Serializable;
    23  import java.util.ArrayList;
    24  import java.util.HashMap;
    25  import java.util.Iterator;
    26  import java.util.List;
    27  import java.util.Map;
    28  import org.apache.bcel.Constants;
    29  import org.apache.bcel.classfile.Constant;
    30  import org.apache.bcel.util.ByteSequence;
    31
    32  /***
    33   * This class is a container for a list of <a
    34   * href="Instruction.html">Instruction</a> objects. Instructions can
    35   * be appended, inserted, moved, deleted, etc.. Instructions are being
    36   * wrapped into <a
    37   * href="InstructionHandle.html">InstructionHandles</a> objects that
    38   * are returned upon append/insert operations. They give the user
    39   * (read only) access to the list structure, such that it can be traversed and
    40   * manipulated in a controlled way.
    41   *
    42   * A list is finally dumped to a byte code array with <a
    43   * href="#getByteCode()">getByteCode</a>.
    44   *
    45   * @version $Id: InstructionList.java 386056 2006-03-15 11:31:56Z tcurdt $
    46   * @author  <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
    47   * @see     Instruction
    48   * @see     InstructionHandle
    49   * @see BranchHandle
    50   */
    51  public class InstructionList implements Serializable {
    52
    53      private InstructionHandle start = null, end = null;
    54      private int length = 0; // number of elements in list
    55      private int[] byte_positions; // byte code offsets corresponding to instructions
    56
    57
    58      /***
    59       * Create (empty) instruction list.
    60       */
    61      public InstructionList() {
    62      }
    63
    64
    65      /***
    66       * Create instruction list containing one instruction.
    67       * @param i initial instruction
    68       */
    69      public InstructionList(Instruction i) {
    70          append(i);
    71      }
    72
    73
    74      /***
    75       * Create instruction list containing one instruction.
    76       * @param i initial instruction
    77       */
    78      public InstructionList(BranchInstruction i) {
    79          append(i);
    80      }
    81
    82
    83      /***
    84       * Initialize list with (nonnull) compound instruction. Consumes argument
    85       * list, i.e., it becomes empty.
    86       *
    87       * @param c compound instruction (list)
    88       */
    89      public InstructionList(CompoundInstruction c) {
    90          append(c.getInstructionList());
    91      }
    92
    93
    94      /***
    95       * Test for empty list.
    96       */
    97      public boolean isEmpty() {
    98          return start == null;
    99      } // && end == null
    100
    101
    102     /***
    103      * Find the target instruction (handle) that corresponds to the given target
    104      * position (byte code offset).
    105      *
    106      * @param ihs array of instruction handles, i.e. il.getInstructionHandles()
    107      * @param pos array of positions corresponding to ihs, i.e. il.getInstructionPositions()
    108      * @param count length of arrays
    109      * @param target target position to search for
    110      * @return target position's instruction handle if available
    111      */
    112     public static InstructionHandle findHandle( InstructionHandle[] ihs, int[] pos, int count,
    113             int target ) {
    114         int l = 0, r = count - 1;
    115         /* Do a binary search since the pos array is orderd.
    116          */
    117         do {
    118             int i = (l + r) / 2;
    119             int j = pos[i];
    120             if (j == target) {
    121                 return ihs[i];
    122             } else if (target < j) {
    123                 r = i - 1;
    124             } else {
    125                 l = i + 1;
    126             }
    127         } while (l <= r);
    128         return null;
    129     }
    130
    131
    132     /***
    133      * Get instruction handle for instruction at byte code position pos.
    134      * This only works properly, if the list is freshly initialized from a byte array or
    135      * setPositions() has been called before this method.
    136      *
    137      * @param pos byte code position to search for
    138      * @return target position's instruction handle if available
    139      */
    140     public InstructionHandle findHandle( int pos ) {
    141         InstructionHandle[] ihs = getInstructionHandles();
    142         return findHandle(ihs, byte_positions, length, pos);
    143     }
    144
    145
    146     /***
    147      * Initialize instruction list from byte array.
    148      *
    149      * @param code byte array containing the instructions
    150      */
    151     public InstructionList(byte[] code) {
    152         ByteSequence bytes = new ByteSequence(code);
    153         InstructionHandle[] ihs = new InstructionHandle[code.length];
    154         int[] pos = new int[code.length]; // Can't be more than that
    155         int count = 0; // Contains actual length
    156         /* Pass 1: Create an object for each byte code and append them
    157          * to the list.
    158          */
    159         try {
    160             while (bytes.available() > 0) {
    161                 // Remember byte offset and associate it with the instruction
    162                 int off = bytes.getIndex();
    163                 pos[count] = off;
    164                 /* Read one instruction from the byte stream, the byte position is set
    165                  * accordingly.
    166                  */
    167                 Instruction i = Instruction.readInstruction(bytes);
    168                 InstructionHandle ih;
    169                 if (i instanceof BranchInstruction) {
    170                     ih = append((BranchInstruction) i);
    171                 } else {
    172                     ih = append(i);
    173                 }
    174                 ih.setPosition(off);
    175                 ihs[count] = ih;
    176                 count++;
    177             }
    178         } catch (IOException e) {
    179             throw new ClassGenException(e.toString());
    180         }
    181         byte_positions = new int[count]; // Trim to proper size
    182         System.arraycopy(pos, 0, byte_positions, 0, count);
    183         /* Pass 2: Look for BranchInstruction and update their targets, i.e.,
    184          * convert offsets to instruction handles.
    185          */
    186         for (int i = 0; i < count; i++) {
    187             if (ihs[i] instanceof BranchHandle) {
    188                 BranchInstruction bi = (BranchInstruction) ihs[i].instruction;
    189                 int target = bi.position + bi.getIndex(); /* Byte code position:
    190                  * relative -> absolute. */
    191                 // Search for target position
    192                 InstructionHandle ih = findHandle(ihs, pos, count, target);
    193                 if (ih == null) {
    194                     throw new ClassGenException("Couldn't find target for branch: " + bi);
    195                 }
    196                 bi.setTarget(ih); // Update target
    197                 // If it is a Select instruction, update all branch targets
    198                 if (bi instanceof Select) { // Either LOOKUPSWITCH or TABLESWITCH
    199                     Select s = (Select) bi;
    200                     int[] indices = s.getIndices();
    201                     for (int j = 0; j < indices.length; j++) {
    202                         target = bi.position + indices[j];
    203                         ih = findHandle(ihs, pos, count, target);
    204                         if (ih == null) {
    205                             throw new ClassGenException("Couldn't find target for switch: " + bi);
    206                         }
    207                         s.setTarget(j, ih); // Update target
    208                     }
    209                 }
    210             }
    211         }
    212     }
    213
    214
    215     /***
    216      * Append another list after instruction (handle) ih contained in this list.
    217      * Consumes argument list, i.e., it becomes empty.
    218      *
    219      * @param ih where to append the instruction list
    220      * @param il Instruction list to append to this one
    221      * @return instruction handle pointing to the <B>first</B> appended instruction
    222      */
    223     public InstructionHandle append( InstructionHandle ih, InstructionList il ) {
    224         if (il == null) {
    225             throw new ClassGenException("Appending null InstructionList");
    226         }
    227         if (il.isEmpty()) {
    228             return ih;
    229         }
    230         InstructionHandle next = ih.next, ret = il.start;
    231         ih.next = il.start;
    232         il.start.prev = ih;
    233         il.end.next = next;
    234         if (next != null) {
    235             next.prev = il.end;
    236         } else {
    237             end = il.end; // Update end ...
    238         }
    239         length += il.length; // Update length
    240         il.clear();
    241         return ret;
    242     }
    243
    244
    245     /***
    246      * Append another list after instruction i contained in this list.
    247      * Consumes argument list, i.e., it becomes empty.
    248      *
    249      * @param i  where to append the instruction list
    250      * @param il Instruction list to append to this one
    251      * @return instruction handle pointing to the <B>first</B> appended instruction
    252      */
    253     public InstructionHandle append( Instruction i, InstructionList il ) {
    254         InstructionHandle ih;
    255         if ((ih = findInstruction2(i)) == null) {
    256             throw new ClassGenException("Instruction " + i + " is not contained in this list.");
    257         }
    258         return append(ih, il);
    259     }
    260
    261
    262     /***
    263      * Append another list to this one.
    264      * Consumes argument list, i.e., it becomes empty.
    265      *
    266      * @param il list to append to end of this list
    267      * @return instruction handle of the <B>first</B> appended instruction
    268      */
    269     public InstructionHandle append( InstructionList il ) {
    270         if (il == null) {
    271             throw new ClassGenException("Appending null InstructionList");
    272         }
    273         if (il.isEmpty()) {
    274             return null;
    275         }
    276         if (isEmpty()) {
    277             start = il.start;
    278             end = il.end;
    279             length = il.length;
    280             il.clear();
    281             return start;
    282         } else {
    283             return append(end, il); // was end.instruction
    284         }
    285     }
    286
    287
    288     /***
    289      * Append an instruction to the end of this list.
    290      *
    291      * @param ih instruction to append
    292      */
    293     private void append( InstructionHandle ih ) {
    294         if (isEmpty()) {
    295             start = end = ih;
    296             ih.next = ih.prev = null;
    297         } else {
    298             end.next = ih;
    299             ih.prev = end;
    300             ih.next = null;
    301             end = ih;
    302         }
    303         length++; // Update length
    304     }
    305
    306
    307     /***
    308      * Append an instruction to the end of this list.
    309      *
    310      * @param i instruction to append
    311      * @return instruction handle of the appended instruction
    312      */
    313     public InstructionHandle append( Instruction i ) {
    314         InstructionHandle ih = InstructionHandle.getInstructionHandle(i);
    315         append(ih);
    316         return ih;
    317     }
    318
    319
    320     /***
    321      * Append a branch instruction to the end of this list.
    322      *
    323      * @param i branch instruction to append
    324      * @return branch instruction handle of the appended instruction
    325      */
    326     public BranchHandle append( BranchInstruction i ) {
    327         BranchHandle ih = BranchHandle.getBranchHandle(i);
    328         append(ih);
    329         return ih;
    330     }
    331
    332
    333     /***
    334      * Append a single instruction j after another instruction i, which
    335      * must be in this list of course!
    336      *
    337      * @param i Instruction in list
    338      * @param j Instruction to append after i in list
    339      * @return instruction handle of the first appended instruction
    340      */
    341     public InstructionHandle append( Instruction i, Instruction j ) {
    342         return append(i, new InstructionList(j));
    343     }
    344
    345
    346     /***
    347      * Append a compound instruction, after instruction i.
    348      *
    349      * @param i Instruction in list
    350      * @param c The composite instruction (containing an InstructionList)
    351      * @return instruction handle of the first appended instruction
    352      */
    353     public InstructionHandle append( Instruction i, CompoundInstruction c ) {
    354         return append(i, c.getInstructionList());
    355     }
    356
    357
    358     /***
    359      * Append a compound instruction.
    360      *
    361      * @param c The composite instruction (containing an InstructionList)
    362      * @return instruction handle of the first appended instruction
    363      */
    364     public InstructionHandle append( CompoundInstruction c ) {
    365         return append(c.getInstructionList());
    366     }
    367
    368
    369     /***
    370      * Append a compound instruction.
    371      *
    372      * @param ih where to append the instruction list
    373      * @param c The composite instruction (containing an InstructionList)
    374      * @return instruction handle of the first appended instruction
    375      */
    376     public InstructionHandle append( InstructionHandle ih, CompoundInstruction c ) {
    377         return append(ih, c.getInstructionList());
    378     }
    379
    380
    381     /***
    382      * Append an instruction after instruction (handle) ih contained in this list.
    383      *
    384      * @param ih where to append the instruction list
    385      * @param i Instruction to append
    386      * @return instruction handle pointing to the <B>first</B> appended instruction
    387      */
    388     public InstructionHandle append( InstructionHandle ih, Instruction i ) {
    389         return append(ih, new InstructionList(i));
    390     }
    391
    392
    393     /***
    394      * Append an instruction after instruction (handle) ih contained in this list.
    395      *
    396      * @param ih where to append the instruction list
    397      * @param i Instruction to append
    398      * @return instruction handle pointing to the <B>first</B> appended instruction
    399      */
    400     public BranchHandle append( InstructionHandle ih, BranchInstruction i ) {
    401         BranchHandle bh = BranchHandle.getBranchHandle(i);
    402         InstructionList il = new InstructionList();
    403         il.append(bh);
    404         append(ih, il);
    405         return bh;
    406     }
    407
    408
    409     /***
    410      * Insert another list before Instruction handle ih contained in this list.
    411      * Consumes argument list, i.e., it becomes empty.
    412      *
    413      * @param ih  where to append the instruction list
    414      * @param il Instruction list to insert
    415      * @return instruction handle of the first inserted instruction
    416      */
    417     public InstructionHandle insert( InstructionHandle ih, InstructionList il ) {
    418         if (il == null) {
    419             throw new ClassGenException("Inserting null InstructionList");
    420         }
    421         if (il.isEmpty()) {
    422             return ih;
    423         }
    424         InstructionHandle prev = ih.prev, ret = il.start;
    425         ih.prev = il.end;
    426         il.end.next = ih;
    427         il.start.prev = prev;
    428         if (prev != null) {
    429             prev.next = il.start;
    430         } else {
    431             start = il.start; // Update start ...
    432         }
    433         length += il.length; // Update length
    434         il.clear();
    435         return ret;
    436     }
    437
    438
    439     /***
    440      * Insert another list.
    441      *
    442      * @param il list to insert before start of this list
    443      * @return instruction handle of the first inserted instruction
    444      */
    445     public InstructionHandle insert( InstructionList il ) {
    446         if (isEmpty()) {
    447             append(il); // Code is identical for this case
    448             return start;
    449         } else {
    450             return insert(start, il);
    451         }
    452     }
    453
    454
    455     /***
    456      * Insert an instruction at start of this list.
    457      *
    458      * @param ih instruction to insert
    459      */
    460     private void insert( InstructionHandle ih ) {
    461         if (isEmpty()) {
    462             start = end = ih;
    463             ih.next = ih.prev = null;
    464         } else {
    465             start.prev = ih;
    466             ih.next = start;
    467             ih.prev = null;
    468             start = ih;
    469         }
    470         length++;
    471     }
    472
    473
    474     /***
    475      * Insert another list before Instruction i contained in this list.
    476      * Consumes argument list, i.e., it becomes empty.
    477      *
    478      * @param i  where to append the instruction list
    479      * @param il Instruction list to insert
    480      * @return instruction handle pointing to the first inserted instruction,
    481      * i.e., il.getStart()
    482      */
    483     public InstructionHandle insert( Instruction i, InstructionList il ) {
    484         InstructionHandle ih;
    485         if ((ih = findInstruction1(i)) == null) {
    486             throw new ClassGenException("Instruction " + i + " is not contained in this list.");
    487         }
    488         return insert(ih, il);
    489     }
    490
    491
    492     /***
    493      * Insert an instruction at start of this list.
    494      *
    495      * @param i instruction to insert
    496      * @return instruction handle of the inserted instruction
    497      */
    498     public InstructionHandle insert( Instruction i ) {
    499         InstructionHandle ih = InstructionHandle.getInstructionHandle(i);
    500         insert(ih);
    501         return ih;
    502     }
    503
    504
    505     /***
    506      * Insert a branch instruction at start of this list.
    507      *
    508      * @param i branch instruction to insert
    509      * @return branch instruction handle of the appended instruction
    510      */
    511     public BranchHandle insert( BranchInstruction i ) {
    512         BranchHandle ih = BranchHandle.getBranchHandle(i);
    513         insert(ih);
    514         return ih;
    515     }
    516
    517
    518     /***
    519      * Insert a single instruction j before another instruction i, which
    520      * must be in this list of course!
    521      *
    522      * @param i Instruction in list
    523      * @param j Instruction to insert before i in list
    524      * @return instruction handle of the first inserted instruction
    525      */
    526     public InstructionHandle insert( Instruction i, Instruction j ) {
    527         return insert(i, new InstructionList(j));
    528     }
    529
    530
    531     /***
    532      * Insert a compound instruction before instruction i.
    533      *
    534      * @param i Instruction in list
    535      * @param c The composite instruction (containing an InstructionList)
    536      * @return instruction handle of the first inserted instruction
    537      */
    538     public InstructionHandle insert( Instruction i, CompoundInstruction c ) {
    539         return insert(i, c.getInstructionList());
    540     }
    541
    542
    543     /***
    544      * Insert a compound instruction.
    545      *
    546      * @param c The composite instruction (containing an InstructionList)
    547      * @return instruction handle of the first inserted instruction
    548      */
    549     public InstructionHandle insert( CompoundInstruction c ) {
    550         return insert(c.getInstructionList());
    551     }
    552
    553
    554     /***
    555      * Insert an instruction before instruction (handle) ih contained in this list.
    556      *
    557      * @param ih where to insert to the instruction list
    558      * @param i Instruction to insert
    559      * @return instruction handle of the first inserted instruction
    560      */
    561     public InstructionHandle insert( InstructionHandle ih, Instruction i ) {
    562         return insert(ih, new InstructionList(i));
    563     }
    564
    565
    566     /***
    567      * Insert a compound instruction.
    568      *
    569      * @param ih where to insert the instruction list
    570      * @param c The composite instruction (containing an InstructionList)
    571      * @return instruction handle of the first inserted instruction
    572      */
    573     public InstructionHandle insert( InstructionHandle ih, CompoundInstruction c ) {
    574         return insert(ih, c.getInstructionList());
    575     }
    576
    577
    578     /***
    579      * Insert an instruction before instruction (handle) ih contained in this list.
    580      *
    581      * @param ih where to insert to the instruction list
    582      * @param i Instruction to insert
    583      * @return instruction handle of the first inserted instruction
    584      */
    585     public BranchHandle insert( InstructionHandle ih, BranchInstruction i ) {
    586         BranchHandle bh = BranchHandle.getBranchHandle(i);
    587         InstructionList il = new InstructionList();
    588         il.append(bh);
    589         insert(ih, il);
    590         return bh;
    591     }
    592
    593
    594     /***
    595      * Take all instructions (handles) from "start" to "end" and append them after the
    596      * new location "target". Of course, "end" must be after "start" and target must
    597      * not be located withing this range. If you want to move something to the start of
    598      * the list use null as value for target.<br>
    599      * Any instruction targeters pointing to handles within the block, keep their targets.
    600      *
    601      * @param start  of moved block
    602      * @param end    of moved block
    603      * @param target of moved block
    604      */
    605     public void move( InstructionHandle start, InstructionHandle end, InstructionHandle target ) {
    606         // Step 1: Check constraints
    607         if ((start == null) || (end == null)) {
    608             throw new ClassGenException("Invalid null handle: From " + start + " to " + end);
    609         }
    610         if ((target == start) || (target == end)) {
    611             throw new ClassGenException("Invalid range: From " + start + " to " + end
    612                     + " contains target " + target);
    613         }
    614         for (InstructionHandle ih = start; ih != end.next; ih = ih.next) {
    615             if (ih == null) {
    616                 throw new ClassGenException("Invalid range: From " + start + " to " + end);
    617             } else if (ih == target) {
    618                 throw new ClassGenException("Invalid range: From " + start + " to " + end
    619                         + " contains target " + target);
    620             }
    621         }
    622         // Step 2: Temporarily remove the given instructions from the list
    623         InstructionHandle prev = start.prev, next = end.next;
    624         if (prev != null) {
    625             prev.next = next;
    626         } else {
    627             this.start = next;
    628         }
    629         if (next != null) {
    630             next.prev = prev;
    631         } else {
    632             this.end = prev;
    633         }
    634         start.prev = end.next = null;
    635         // Step 3: append after target
    636         if (target == null) { // append to start of list
    637             if (this.start != null) {
    638                 this.start.prev = end;
    639             }
    640             end.next = this.start;
    641             this.start = start;
    642         } else {
    643             next = target.next;
    644             target.next = start;
    645             start.prev = target;
    646             end.next = next;
    647             if (next != null) {
    648                 next.prev = end;
    649             } else {
    650                 this.end = end;
    651             }
    652         }
    653     }
    654
    655
    656     /***
    657      * Move a single instruction (handle) to a new location.
    658      *
    659      * @param ih     moved instruction
    660      * @param target new location of moved instruction
    661      */
    662     public void move( InstructionHandle ih, InstructionHandle target ) {
    663         move(ih, ih, target);
    664     }
    665
    666
    667     /***
    668      * Remove from instruction `prev' to instruction `next' both contained
    669      * in this list. Throws TargetLostException when one of the removed instruction handles
    670      * is still being targeted.
    671      *
    672      * @param prev where to start deleting (predecessor, exclusive)
    673      * @param next where to end deleting (successor, exclusive)
    674      */
    675     private void remove( InstructionHandle prev, InstructionHandle next )
    676             throws TargetLostException {
    677         InstructionHandle first, last; // First and last deleted instruction
    678         if ((prev == null) && (next == null)) { // singleton list
    679             first = last = start;
    680             start = end = null;
    681         } else {
    682             if (prev == null) { // At start of list
    683                 first = start;
    684                 start = next;
    685             } else {
    686                 first = prev.next;
    687                 prev.next = next;
    688             }
    689             if (next == null) { // At end of list
    690                 last = end;
    691                 end = prev;
    692             } else {
    693                 last = next.prev;
    694                 next.prev = prev;
    695             }
    696         }
    697         first.prev = null; // Completely separated from rest of list
    698         last.next = null;
    699         List target_vec = new ArrayList();
    700         for (InstructionHandle ih = first; ih != null; ih = ih.next) {
    701             ih.getInstruction().dispose(); // e.g. BranchInstructions release their targets
    702         }
    703         StringBuffer buf = new StringBuffer("{ ");
    704         for (InstructionHandle ih = first; ih != null; ih = next) {
    705             next = ih.next;
    706             length--;
    707             if (ih.hasTargeters()) { // Still got targeters?
    708                 target_vec.add(ih);
    709                 buf.append(ih.toString(true) + " ");
    710                 ih.next = ih.prev = null;
    711             } else {
    712                 ih.dispose();
    713             }
    714         }
    715         buf.append("}");
    716         if (!target_vec.isEmpty()) {
    717             InstructionHandle[] targeted = new InstructionHandle[target_vec.size()];
    718             target_vec.toArray(targeted);
    719             throw new TargetLostException(targeted, buf.toString());
    720         }
    721     }
    722
    723
    724     /***
    725      * Remove instruction from this list. The corresponding Instruction
    726      * handles must not be reused!
    727      *
    728      * @param ih instruction (handle) to remove
    729      */
    730     public void delete( InstructionHandle ih ) throws TargetLostException {
    731         remove(ih.prev, ih.next);
    732     }
    733
    734
    735     /***
    736      * Remove instruction from this list. The corresponding Instruction
    737      * handles must not be reused!
    738      *
    739      * @param i instruction to remove
    740      */
    741     public void delete( Instruction i ) throws TargetLostException {
    742         InstructionHandle ih;
    743         if ((ih = findInstruction1(i)) == null) {
    744             throw new ClassGenException("Instruction " + i + " is not contained in this list.");
    745         }
    746         delete(ih);
    747     }
    748
    749
    750     /***
    751      * Remove instructions from instruction `from' to instruction `to' contained
    752      * in this list. The user must ensure that `from' is an instruction before
    753      * `to', or risk havoc. The corresponding Instruction handles must not be reused!
    754      *
    755      * @param from where to start deleting (inclusive)
    756      * @param to   where to end deleting (inclusive)
    757      */
    758     public void delete( InstructionHandle from, InstructionHandle to ) throws TargetLostException {
    759         remove(from.prev, to.next);
    760     }
    761
    762
    763     /***
    764      * Remove instructions from instruction `from' to instruction `to' contained
    765      * in this list. The user must ensure that `from' is an instruction before
    766      * `to', or risk havoc. The corresponding Instruction handles must not be reused!
    767      *
    768      * @param from where to start deleting (inclusive)
    769      * @param to   where to end deleting (inclusive)
    770      */
    771     public void delete( Instruction from, Instruction to ) throws TargetLostException {
    772         InstructionHandle from_ih, to_ih;
    773         if ((from_ih = findInstruction1(from)) == null) {
    774             throw new ClassGenException("Instruction " + from + " is not contained in this list.");
    775         }
    776         if ((to_ih = findInstruction2(to)) == null) {
    777             throw new ClassGenException("Instruction " + to + " is not contained in this list.");
    778         }
    779         delete(from_ih, to_ih);
    780     }
    781
    782
    783     /***
    784      * Search for given Instruction reference, start at beginning of list.
    785      *
    786      * @param i instruction to search for
    787      * @return instruction found on success, null otherwise
    788      */
    789     private InstructionHandle findInstruction1( Instruction i ) {
    790         for (InstructionHandle ih = start; ih != null; ih = ih.next) {
    791             if (ih.instruction == i) {
    792                 return ih;
    793             }
    794         }
    795         return null;
    796     }
    797
    798
    799     /***
    800      * Search for given Instruction reference, start at end of list
    801      *
    802      * @param i instruction to search for
    803      * @return instruction found on success, null otherwise
    804      */
    805     private InstructionHandle findInstruction2( Instruction i ) {
    806         for (InstructionHandle ih = end; ih != null; ih = ih.prev) {
    807             if (ih.instruction == i) {
    808                 return ih;
    809             }
    810         }
    811         return null;
    812     }
    813
    814
    815     public boolean contains( InstructionHandle i ) {
    816         if (i == null) {
    817             return false;
    818         }
    819         for (InstructionHandle ih = start; ih != null; ih = ih.next) {
    820             if (ih == i) {
    821                 return true;
    822             }
    823         }
    824         return false;
    825     }
    826
    827
    828     public boolean contains( Instruction i ) {
    829         return findInstruction1(i) != null;
    830     }
    831
    832
    833     public void setPositions() {
    834         setPositions(false);
    835     }
    836
    837
    838     /***
    839      * Give all instructions their position number (offset in byte stream), i.e.,
    840      * make the list ready to be dumped.
    841      *
    842      * @param check Perform sanity checks, e.g. if all targeted instructions really belong
    843      * to this list
    844      */
    845     public void setPositions( boolean check ) {
    846         int max_additional_bytes = 0, additional_bytes = 0;
    847         int index = 0, count = 0;
    848         int[] pos = new int[length];
    849         /* Pass 0: Sanity checks
    850          */
    851         if (check) {
    852             for (InstructionHandle ih = start; ih != null; ih = ih.next) {
    853                 Instruction i = ih.instruction;
    854                 if (i instanceof BranchInstruction) { // target instruction within list?
    855                     Instruction inst = ((BranchInstruction) i).getTarget().instruction;
    856                     if (!contains(inst)) {
    857                         throw new ClassGenException("Branch target of "
    858                                 + Constants.OPCODE_NAMES[i.opcode] + ":" + inst
    859                                 + " not in instruction list");
    860                     }
    861                     if (i instanceof Select) {
    862                         InstructionHandle[] targets = ((Select) i).getTargets();
    863                         for (int j = 0; j < targets.length; j++) {
    864                             inst = targets[j].instruction;
    865                             if (!contains(inst)) {
    866                                 throw new ClassGenException("Branch target of "
    867                                         + Constants.OPCODE_NAMES[i.opcode] + ":" + inst
    868                                         + " not in instruction list");
    869                             }
    870                         }
    871                     }
    872                     if (!(ih instanceof BranchHandle)) {
    873                         throw new ClassGenException("Branch instruction "
    874                                 + Constants.OPCODE_NAMES[i.opcode] + ":" + inst
    875                                 + " not contained in BranchHandle.");
    876                     }
    877                 }
    878             }
    879         }
    880         /* Pass 1: Set position numbers and sum up the maximum number of bytes an
    881          * instruction may be shifted.
    882          */
    883         for (InstructionHandle ih = start; ih != null; ih = ih.next) {
    884             Instruction i = ih.instruction;
    885             ih.setPosition(index);
    886             pos[count++] = index;
    887             /* Get an estimate about how many additional bytes may be added, because
    888              * BranchInstructions may have variable length depending on the target
    889              * offset (short vs. int) or alignment issues (TABLESWITCH and
    890              * LOOKUPSWITCH).
    891              */
    892             switch (i.getOpcode()) {
    893                 case Constants.JSR:
    894                 case Constants.GOTO:
    895                     max_additional_bytes += 2;
    896                     break;
    897                 case Constants.TABLESWITCH:
    898                 case Constants.LOOKUPSWITCH:
    899                     max_additional_bytes += 3;
    900                     break;
    901             }
    902             index += i.getLength();
    903         }
    904         /* Pass 2: Expand the variable-length (Branch)Instructions depending on
    905          * the target offset (short or int) and ensure that branch targets are
    906          * within this list.
    907          */
    908         for (InstructionHandle ih = start; ih != null; ih = ih.next) {
    909             additional_bytes += ih.updatePosition(additional_bytes, max_additional_bytes);
    910         }
    911         /* Pass 3: Update position numbers (which may have changed due to the
    912          * preceding expansions), like pass 1.
    913          */
    914         index = count = 0;
    915         for (InstructionHandle ih = start; ih != null; ih = ih.next) {
    916             Instruction i = ih.instruction;
    917             ih.setPosition(index);
    918             pos[count++] = index;
    919             index += i.getLength();
    920         }
    921         byte_positions = new int[count]; // Trim to proper size
    922         System.arraycopy(pos, 0, byte_positions, 0, count);
    923     }
    924
    925
    926     /***
    927      * When everything is finished, use this method to convert the instruction
    928      * list into an array of bytes.
    929      *
    930      * @return the byte code ready to be dumped
    931      */
    932     public byte[] getByteCode() {
    933         // Update position indices of instructions
    934         setPositions();
    935         ByteArrayOutputStream b = new ByteArrayOutputStream();
    936         DataOutputStream out = new DataOutputStream(b);
    937         try {
    938             for (InstructionHandle ih = start; ih != null; ih = ih.next) {
    939                 Instruction i = ih.instruction;
    940                 i.dump(out); // Traverse list
    941             }
    942         } catch (IOException e) {
    943             System.err.println(e);
    944             return null;
    945         }
    946         return b.toByteArray();
    947     }
    948
    949
    950     /***
    951      * @return an array of instructions without target information for branch instructions.
    952      */
    953     public Instruction[] getInstructions() {
    954         ByteSequence bytes = new ByteSequence(getByteCode());
    955         List instructions = new ArrayList();
    956         try {
    957             while (bytes.available() > 0) {
    958                 instructions.add(Instruction.readInstruction(bytes));
    959             }
    960         } catch (IOException e) {
    961             throw new ClassGenException(e.toString());
    962         }
    963         return (Instruction[]) instructions.toArray(new Instruction[instructions.size()]);
    964     }
    965
    966
    967     public String toString() {
    968         return toString(true);
    969     }
    970
    971
    972     /***
    973      * @param verbose toggle output format
    974      * @return String containing all instructions in this list.
    975      */
    976     public String toString( boolean verbose ) {
    977         StringBuffer buf = new StringBuffer();
    978         for (InstructionHandle ih = start; ih != null; ih = ih.next) {
    979             buf.append(ih.toString(verbose)).append("\n");
    980         }
    981         return buf.toString();
    982     }
    983
    984
    985     /***
    986      * @return Enumeration that lists all instructions (handles)
    987      */
    988     public Iterator iterator() {
    989         return new Iterator() {
    990
    991             private InstructionHandle ih = start;
    992
    993
    994             public Object next() {
    995                 InstructionHandle i = ih;
    996                 ih = ih.next;
    997                 return i;
    998             }
    999
    1000
    1001             public void remove() {
    1002                 throw new UnsupportedOperationException();
    1003             }
    1004
    1005
    1006             public boolean hasNext() {
    1007                 return ih != null;
    1008             }
    1009         };
    1010     }
    1011
    1012
    1013     /***
    1014      * @return array containing all instructions (handles)
    1015      */
    1016     public InstructionHandle[] getInstructionHandles() {
    1017         InstructionHandle[] ihs = new InstructionHandle[length];
    1018         InstructionHandle ih = start;
    1019         for (int i = 0; i < length; i++) {
    1020             ihs[i] = ih;
    1021             ih = ih.next;
    1022         }
    1023         return ihs;
    1024     }
    1025
    1026
    1027     /***
    1028      * Get positions (offsets) of all instructions in the list. This relies on that
    1029      * the list has been freshly created from an byte code array, or that setPositions()
    1030      * has been called. Otherwise this may be inaccurate.
    1031      *
    1032      * @return array containing all instruction's offset in byte code
    1033      */
    1034     public int[] getInstructionPositions() {
    1035         return byte_positions;
    1036     }
    1037
    1038
    1039     /***
    1040      * @return complete, i.e., deep copy of this list
    1041      */
    1042     public InstructionList copy() {
    1043         Map map = new HashMap();
    1044         InstructionList il = new InstructionList();
    1045         /* Pass 1: Make copies of all instructions, append them to the new list
    1046          * and associate old instruction references with the new ones, i.e.,
    1047          * a 1:1 mapping.
    1048          */
    1049         for (InstructionHandle ih = start; ih != null; ih = ih.next) {
    1050             Instruction i = ih.instruction;
    1051             Instruction c = i.copy(); // Use clone for shallow copy
    1052             if (c instanceof BranchInstruction) {
    1053                 map.put(ih, il.append((BranchInstruction) c));
    1054             } else {
    1055                 map.put(ih, il.append(c));
    1056             }
    1057         }
    1058         /* Pass 2: Update branch targets.
    1059          */
    1060         InstructionHandle ih = start;
    1061         InstructionHandle ch = il.start;
    1062         while (ih != null) {
    1063             Instruction i = ih.instruction;
    1064             Instruction c = ch.instruction;
    1065             if (i instanceof BranchInstruction) {
    1066                 BranchInstruction bi = (BranchInstruction) i;
    1067                 BranchInstruction bc = (BranchInstruction) c;
    1068                 InstructionHandle itarget = bi.getTarget(); // old target
    1069                 // New target is in hash map
    1070                 bc.setTarget((InstructionHandle) map.get(itarget));
    1071                 if (bi instanceof Select) { // Either LOOKUPSWITCH or TABLESWITCH
    1072                     InstructionHandle[] itargets = ((Select) bi).getTargets();
    1073                     InstructionHandle[] ctargets = ((Select) bc).getTargets();
    1074                     for (int j = 0; j < itargets.length; j++) { // Update all targets
    1075                         ctargets[j] = (InstructionHandle) map.get(itargets[j]);
    1076                     }
    1077                 }
    1078             }
    1079             ih = ih.next;
    1080             ch = ch.next;
    1081         }
    1082         return il;
    1083     }
    1084
    1085
    1086     /*** Replace all references to the old constant pool with references to the new
    1087      *  constant pool
    1088      */
    1089     public void replaceConstantPool( ConstantPoolGen old_cp, ConstantPoolGen new_cp ) {
    1090         for (InstructionHandle ih = start; ih != null; ih = ih.next) {
    1091             Instruction i = ih.instruction;
    1092             if (i instanceof CPInstruction) {
    1093                 CPInstruction ci = (CPInstruction) i;
    1094                 Constant c = old_cp.getConstant(ci.getIndex());
    1095                 ci.setIndex(new_cp.addConstant(c, old_cp));
    1096             }
    1097         }
    1098     }
    1099
    1100
    1101     private void clear() {
    1102         start = end = null;
    1103         length = 0;
    1104     }
    1105
    1106
    1107     /***
    1108      * Delete contents of list. Provides besser memory utilization,
    1109      * because the system then may reuse the instruction handles. This
    1110      * method is typically called right after
    1111      * <href="MethodGen.html#getMethod()">MethodGen.getMethod()</a>.
    1112      */
    1113     public void dispose() {
    1114         // Traverse in reverse order, because ih.next is overwritten
    1115         for (InstructionHandle ih = end; ih != null; ih = ih.prev) {
    1116             /* Causes BranchInstructions to release target and targeters, because it
    1117              * calls dispose() on the contained instruction.
    1118              */
    1119             ih.dispose();
    1120         }
    1121         clear();
    1122     }
    1123
    1124
    1125     /***
    1126      * @return start of list
    1127      */
    1128     public InstructionHandle getStart() {
    1129         return start;
    1130     }
    1131
    1132
    1133     /***
    1134      * @return end of list
    1135      */
    1136     public InstructionHandle getEnd() {
    1137         return end;
    1138     }
    1139
    1140
    1141     /***
    1142      * @return length of list (Number of instructions, not bytes)
    1143      */
    1144     public int getLength() {
    1145         return length;
    1146     }
    1147
    1148
    1149     /***
    1150      * @return length of list (Number of instructions, not bytes)
    1151      */
    1152     public int size() {
    1153         return length;
    1154     }
    1155
    1156
    1157     /***
    1158      * Redirect all references from old_target to new_target, i.e., update targets
    1159      * of branch instructions.
    1160      *
    1161      * @param old_target the old target instruction handle
    1162      * @param new_target the new target instruction handle
    1163      */
    1164     public void redirectBranches( InstructionHandle old_target, InstructionHandle new_target ) {
    1165         for (InstructionHandle ih = start; ih != null; ih = ih.next) {
    1166             Instruction i = ih.getInstruction();
    1167             if (i instanceof BranchInstruction) {
    1168                 BranchInstruction b = (BranchInstruction) i;
    1169                 InstructionHandle target = b.getTarget();
    1170                 if (target == old_target) {
    1171                     b.setTarget(new_target);
    1172                 }
    1173                 if (b instanceof Select) { // Either LOOKUPSWITCH or TABLESWITCH
    1174                     InstructionHandle[] targets = ((Select) b).getTargets();
    1175                     for (int j = 0; j < targets.length; j++) {
    1176                         if (targets[j] == old_target) {
    1177                             ((Select) b).setTarget(j, new_target);
    1178                         }
    1179                     }
    1180                 }
    1181             }
    1182         }
    1183     }
    1184
    1185
    1186     /***
    1187      * Redirect all references of local variables from old_target to new_target.
    1188      *
    1189      * @param lg array of local variables
    1190      * @param old_target the old target instruction handle
    1191      * @param new_target the new target instruction handle
    1192      * @see MethodGen
    1193      */
    1194     public void redirectLocalVariables( LocalVariableGen[] lg, InstructionHandle old_target,
    1195             InstructionHandle new_target ) {
    1196         for (int i = 0; i < lg.length; i++) {
    1197             InstructionHandle start = lg[i].getStart();
    1198             InstructionHandle end = lg[i].getEnd();
    1199             if (start == old_target) {
    1200                 lg[i].setStart(new_target);
    1201             }
    1202             if (end == old_target) {
    1203                 lg[i].setEnd(new_target);
    1204             }
    1205         }
    1206     }
    1207
    1208
    1209     /***
    1210      * Redirect all references of exception handlers from old_target to new_target.
    1211      *
    1212      * @param exceptions array of exception handlers
    1213      * @param old_target the old target instruction handle
    1214      * @param new_target the new target instruction handle
    1215      * @see MethodGen
    1216      */
    1217     public void redirectExceptionHandlers( CodeExceptionGen[] exceptions,
    1218             InstructionHandle old_target, InstructionHandle new_target ) {
    1219         for (int i = 0; i < exceptions.length; i++) {
    1220             if (exceptions[i].getStartPC() == old_target) {
    1221                 exceptions[i].setStartPC(new_target);
    1222             }
    1223             if (exceptions[i].getEndPC() == old_target) {
    1224                 exceptions[i].setEndPC(new_target);
    1225             }
    1226             if (exceptions[i].getHandlerPC() == old_target) {
    1227                 exceptions[i].setHandlerPC(new_target);
    1228             }
    1229         }
    1230     }
    1231
    1232     private List observers;
    1233
    1234
    1235     /*** Add observer for this object.
    1236      */
    1237     public void addObserver( InstructionListObserver o ) {
    1238         if (observers == null) {
    1239             observers = new ArrayList();
    1240         }
    1241         observers.add(o);
    1242     }
    1243
    1244
    1245     /*** Remove observer for this object.
    1246      */
    1247     public void removeObserver( InstructionListObserver o ) {
    1248         if (observers != null) {
    1249             observers.remove(o);
    1250         }
    1251     }
    1252
    1253
    1254     /*** Call notify() method on all observers. This method is not called
    1255      * automatically whenever the state has changed, but has to be
    1256      * called by the user after he has finished editing the object.
    1257      */
    1258     public void update() {
    1259         if (observers != null) {
    1260             for (Iterator e = observers.iterator(); e.hasNext();) {
    1261                 ((InstructionListObserver) e.next()).notify(this);
    1262             }
    1263         }
    1264     }
    1265 }