package org.apache.bcel.generic; /* ==================================================================== * The Apache Software License, Version 1.1 * * Copyright (c) 2001 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Apache" and "Apache Software Foundation" and * "Apache BCEL" must not be used to endorse or promote products * derived from this software without prior written permission. For * written permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache", * "Apache BCEL", nor may "Apache" appear in their name, without * prior written permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . */ import org.apache.bcel.Constants; import org.apache.bcel.classfile.Constant; import org.apache.bcel.util.ByteSequence; import java.io.*; import java.util.Iterator; import java.util.HashMap; import java.util.ArrayList; /** * This class is a container for a list of Instruction objects. Instructions can * be appended, inserted, moved, deleted, etc.. Instructions are being * wrapped into InstructionHandles objects that * are returned upon append/insert operations. They give the user * (read only) access to the list structure, such that it can be traversed and * manipulated in a controlled way. * * A list is finally dumped to a byte code array with getByteCode. * * @version $Id: InstructionList.java,v 1.2 2002/03/11 16:16:38 mdahm Exp $ * @author M. Dahm * @see Instruction * @see InstructionHandle * @see BranchHandle */ public class InstructionList implements Serializable { /** * */ private static final long serialVersionUID = -7679099301896128498L; private InstructionHandle start = null, end = null; private int length = 0; // number of elements in list private int[] byte_positions; // byte code offsets corresponding to instructions /** * Create (empty) instruction list. */ public InstructionList() {} /** * Create instruction list containing one instruction. * @param i initial instruction */ public InstructionList(Instruction i) { append(i); } /** * Create instruction list containing one instruction. * @param i initial instruction */ public InstructionList(BranchInstruction i) { append(i); } /** * Initialize list with (nonnull) compound instruction. Consumes argument * list, i.e., it becomes empty. * * @param c compound instruction (list) */ public InstructionList(CompoundInstruction c) { append(c.getInstructionList()); } /** * Test for empty list. */ public boolean isEmpty() { return start == null; } // && end == null /** * Find the target instruction (handle) that corresponds to the given target * position (byte code offset). * * @param ihs array of instruction handles, i.e. il.getInstructionHandles() * @param pos array of positions corresponding to ihs, i.e. il.getInstructionPositions() * @param count length of arrays * @param target target position to search for * @return target position's instruction handle if available */ public static InstructionHandle findHandle(InstructionHandle[] ihs, int[] pos, int count, int target) { int l=0, r = count - 1; /* Do a binary search since the pos array is orderd. */ do { int i = (l + r) / 2; int j = pos[i]; if(j == target) // target found return ihs[i]; else if(target < j) // else constrain search area r = i - 1; else // target > j l = i + 1; } while(l <= r); return null; } /** * Get instruction handle for instruction at byte code position pos. * This only works properly, if the list is freshly initialized from a byte array or * setPositions() has been called before this method. * * @param pos byte code position to search for * @return target position's instruction handle if available */ public InstructionHandle findHandle(int pos) { InstructionHandle[] ihs = getInstructionHandles(); return findHandle(ihs, byte_positions, length, pos); } /** * Initialize instruction list from byte array. * * @param code byte array containing the instructions */ public InstructionList(byte[] code) { ByteSequence bytes = new ByteSequence(code); InstructionHandle[] ihs = new InstructionHandle[code.length]; int[] pos = new int[code.length]; // Can't be more than that int count = 0; // Contains actual length /* Pass 1: Create an object for each byte code and append them * to the list. */ try { while(bytes.available() > 0) { // Remember byte offset and associate it with the instruction int off = bytes.getIndex(); pos[count] = off; /* Read one instruction from the byte stream, the byte position is set * accordingly. */ Instruction i = Instruction.readInstruction(bytes); InstructionHandle ih; if(i instanceof BranchInstruction) // Use proper append() method ih = append((BranchInstruction)i); else ih = append(i); ih.setPosition(off); ihs[count] = ih; count++; } } catch(IOException e) { throw new ClassGenException(e.toString()); } byte_positions = new int[count]; // Trim to proper size System.arraycopy(pos, 0, byte_positions, 0, count); /* Pass 2: Look for BranchInstruction and update their targets, i.e., * convert offsets to instruction handles. */ for(int i=0; i < count; i++) { if(ihs[i] instanceof BranchHandle) { BranchInstruction bi = (BranchInstruction)ihs[i].instruction; int target = bi.position + bi.getIndex(); /* Byte code position: * relative -> absolute. */ // Search for target position InstructionHandle ih = findHandle(ihs, pos, count, target); if(ih == null) // Search failed throw new ClassGenException("Couldn't find target for branch: " + bi); bi.setTarget(ih); // Update target // If it is a Select instruction, update all branch targets if(bi instanceof Select) { // Either LOOKUPSWITCH or TABLESWITCH Select s = (Select)bi; int[] indices = s.getIndices(); for(int j=0; j < indices.length; j++) { target = bi.position + indices[j]; ih = findHandle(ihs, pos, count, target); if(ih == null) // Search failed throw new ClassGenException("Couldn't find target for switch: " + bi); s.setTarget(j, ih); // Update target } } } } } /** * Append another list after instruction (handle) ih contained in this list. * Consumes argument list, i.e., it becomes empty. * * @param ih where to append the instruction list * @param il Instruction list to append to this one * @return instruction handle pointing to the first appended instruction */ public InstructionHandle append(InstructionHandle ih, InstructionList il) { if(il == null) throw new ClassGenException("Appending null InstructionList"); if(il.isEmpty()) // Nothing to do return ih; InstructionHandle next = ih.next, ret = il.start; ih.next = il.start; il.start.prev = ih; il.end.next = next; if(next != null) // i == end ? next.prev = il.end; else end = il.end; // Update end ... length += il.length; // Update length il.clear(); return ret; } /** * Append another list after instruction i contained in this list. * Consumes argument list, i.e., it becomes empty. * * @param i where to append the instruction list * @param il Instruction list to append to this one * @return instruction handle pointing to the first appended instruction */ public InstructionHandle append(Instruction i, InstructionList il) { InstructionHandle ih; if((ih = findInstruction2(i)) == null) // Also applies for empty list throw new ClassGenException("Instruction " + i + " is not contained in this list."); return append(ih, il); } /** * Append another list to this one. * Consumes argument list, i.e., it becomes empty. * * @param il list to append to end of this list * @return instruction handle of the first appended instruction */ public InstructionHandle append(InstructionList il) { if(il == null) throw new ClassGenException("Appending null InstructionList"); if(il.isEmpty()) // Nothing to do return null; if(isEmpty()) { start = il.start; end = il.end; length = il.length; il.clear(); return start; } else return append(end, il); // was end.instruction } /** * Append an instruction to the end of this list. * * @param ih instruction to append */ private void append(InstructionHandle ih) { if(isEmpty()) { start = end = ih; ih.next = ih.prev = null; } else { end.next = ih; ih.prev = end; ih.next = null; end = ih; } length++; // Update length } /** * Append an instruction to the end of this list. * * @param i instruction to append * @return instruction handle of the appended instruction */ public InstructionHandle append(Instruction i) { InstructionHandle ih = InstructionHandle.getInstructionHandle(i); append(ih); return ih; } /** * Append a branch instruction to the end of this list. * * @param i branch instruction to append * @return branch instruction handle of the appended instruction */ public BranchHandle append(BranchInstruction i) { BranchHandle ih = BranchHandle.getBranchHandle(i); append(ih); return ih; } /** * Append a single instruction j after another instruction i, which * must be in this list of course! * * @param i Instruction in list * @param j Instruction to append after i in list * @return instruction handle of the first appended instruction */ public InstructionHandle append(Instruction i, Instruction j) { return append(i, new InstructionList(j)); } /** * Append a compound instruction, after instruction i. * * @param i Instruction in list * @param c The composite instruction (containing an InstructionList) * @return instruction handle of the first appended instruction */ public InstructionHandle append(Instruction i, CompoundInstruction c) { return append(i, c.getInstructionList()); } /** * Append a compound instruction. * * @param c The composite instruction (containing an InstructionList) * @return instruction handle of the first appended instruction */ public InstructionHandle append(CompoundInstruction c) { return append(c.getInstructionList()); } /** * Append a compound instruction. * * @param ih where to append the instruction list * @param c The composite instruction (containing an InstructionList) * @return instruction handle of the first appended instruction */ public InstructionHandle append(InstructionHandle ih, CompoundInstruction c) { return append(ih, c.getInstructionList()); } /** * Append an instruction after instruction (handle) ih contained in this list. * * @param ih where to append the instruction list * @param i Instruction to append * @return instruction handle pointing to the first appended instruction */ public InstructionHandle append(InstructionHandle ih, Instruction i) { return append(ih, new InstructionList(i)); } /** * Append an instruction after instruction (handle) ih contained in this list. * * @param ih where to append the instruction list * @param i Instruction to append * @return instruction handle pointing to the first appended instruction */ public BranchHandle append(InstructionHandle ih, BranchInstruction i) { BranchHandle bh = BranchHandle.getBranchHandle(i); InstructionList il = new InstructionList(); il.append(bh); append(ih, il); return bh; } /** * Insert another list before Instruction handle ih contained in this list. * Consumes argument list, i.e., it becomes empty. * * @param i where to append the instruction list * @param il Instruction list to insert * @return instruction handle of the first inserted instruction */ public InstructionHandle insert(InstructionHandle ih, InstructionList il) { if(il == null) throw new ClassGenException("Inserting null InstructionList"); if(il.isEmpty()) // Nothing to do return ih; InstructionHandle prev = ih.prev, ret = il.start; ih.prev = il.end; il.end.next = ih; il.start.prev = prev; if(prev != null) // ih == start ? prev.next = il.start; else start = il.start; // Update start ... length += il.length; // Update length il.clear(); return ret; } /** * Insert another list. * * @param il list to insert before start of this list * @return instruction handle of the first inserted instruction */ public InstructionHandle insert(InstructionList il) { if(isEmpty()) { append(il); // Code is identical for this case return start; } else return insert(start, il); } /** * Insert an instruction at start of this list. * * @param ih instruction to insert */ private void insert(InstructionHandle ih) { if(isEmpty()) { start = end = ih; ih.next = ih.prev = null; } else { start.prev = ih; ih.next = start; ih.prev = null; start = ih; } length++; } /** * Insert another list before Instruction i contained in this list. * Consumes argument list, i.e., it becomes empty. * * @param i where to append the instruction list * @param il Instruction list to insert * @return instruction handle pointing to the first inserted instruction, * i.e., il.getStart() */ public InstructionHandle insert(Instruction i, InstructionList il) { InstructionHandle ih; if((ih = findInstruction1(i)) == null) throw new ClassGenException("Instruction " + i + " is not contained in this list."); return insert(ih, il); } /** * Insert an instruction at start of this list. * * @param i instruction to insert * @return instruction handle of the inserted instruction */ public InstructionHandle insert(Instruction i) { InstructionHandle ih = InstructionHandle.getInstructionHandle(i); insert(ih); return ih; } /** * Insert a branch instruction at start of this list. * * @param i branch instruction to insert * @return branch instruction handle of the appended instruction */ public BranchHandle insert(BranchInstruction i) { BranchHandle ih = BranchHandle.getBranchHandle(i); insert(ih); return ih; } /** * Insert a single instruction j before another instruction i, which * must be in this list of course! * * @param i Instruction in list * @param j Instruction to insert before i in list * @return instruction handle of the first inserted instruction */ public InstructionHandle insert(Instruction i, Instruction j) { return insert(i, new InstructionList(j)); } /** * Insert a compound instruction before instruction i. * * @param i Instruction in list * @param c The composite instruction (containing an InstructionList) * @return instruction handle of the first inserted instruction */ public InstructionHandle insert(Instruction i, CompoundInstruction c) { return insert(i, c.getInstructionList()); } /** * Insert a compound instruction. * * @param c The composite instruction (containing an InstructionList) * @return instruction handle of the first inserted instruction */ public InstructionHandle insert(CompoundInstruction c) { return insert(c.getInstructionList()); } /** * Insert an instruction before instruction (handle) ih contained in this list. * * @param ih where to insert to the instruction list * @param i Instruction to insert * @return instruction handle of the first inserted instruction */ public InstructionHandle insert(InstructionHandle ih, Instruction i) { return insert(ih, new InstructionList(i)); } /** * Insert a compound instruction. * * @param ih where to insert the instruction list * @param c The composite instruction (containing an InstructionList) * @return instruction handle of the first inserted instruction */ public InstructionHandle insert(InstructionHandle ih, CompoundInstruction c) { return insert(ih, c.getInstructionList()); } /** * Insert an instruction before instruction (handle) ih contained in this list. * * @param ih where to insert to the instruction list * @param i Instruction to insert * @return instruction handle of the first inserted instruction */ public BranchHandle insert(InstructionHandle ih, BranchInstruction i) { BranchHandle bh = BranchHandle.getBranchHandle(i); InstructionList il = new InstructionList(); il.append(bh); insert(ih, il); return bh; } /** * Take all instructions (handles) from "start" to "end" and append them after the * new location "target". Of course, "end" must be after "start" and target must * not be located withing this range. If you want to move something to the start of * the list use null as value for target.
* Any instruction targeters pointing to handles within the block, keep their targets. * * @param start of moved block * @param end of moved block * @param target of moved block */ public void move(InstructionHandle start, InstructionHandle end, InstructionHandle target) { // Step 1: Check constraints if((start == null) || (end == null)) throw new ClassGenException("Invalid null handle: From " + start + " to " + end); if((target == start) || (target == end)) throw new ClassGenException("Invalid range: From " + start + " to " + end + " contains target " + target); for(InstructionHandle ih = start; ih != end.next; ih = ih.next) { if(ih == null) // At end of list, end not found yet throw new ClassGenException("Invalid range: From " + start + " to " + end); else if(ih == target) // target may be null throw new ClassGenException("Invalid range: From " + start + " to " + end + " contains target " + target); } // Step 2: Temporarily remove the given instructions from the list InstructionHandle prev = start.prev, next = end.next; if(prev != null) prev.next = next; else // start == this.start! this.start = next; if(next != null) next.prev = prev; else // end == this.end! this.end = prev; start.prev = end.next = null; // Step 3: append after target if(target == null) { // append to start of list end.next = this.start; this.start = start; } else { next = target.next; target.next = start; start.prev = target; end.next = next; if(next != null) next.prev = end; } } /** * Move a single instruction (handle) to a new location. * * @param ih moved instruction * @param target new location of moved instruction */ public void move(InstructionHandle ih, InstructionHandle target) { move(ih, ih, target); } /** * Remove from instruction `prev' to instruction `next' both contained * in this list. Throws TargetLostException when one of the removed instruction handles * is still being targeted. * * @param prev where to start deleting (predecessor, exclusive) * @param next where to end deleting (successor, exclusive) */ private void remove(InstructionHandle prev, InstructionHandle next) throws TargetLostException { InstructionHandle first, last; // First and last deleted instruction if((prev == null) && (next == null)) { // singleton list first = last = start; start = end = null; } else { if(prev == null) { // At start of list first = start; start = next; } else { first = prev.next; prev.next = next; } if(next == null) { // At end of list last = end; end = prev; } else { last = next.prev; next.prev = prev; } } first.prev = null; // Completely separated from rest of list last.next = null; ArrayList target_vec = new ArrayList(); for(InstructionHandle ih=first; ih != null; ih = ih.next) ih.getInstruction().dispose(); // e.g. BranchInstructions release their targets StringBuffer buf = new StringBuffer("{ "); for(InstructionHandle ih=first; ih != null; ih = next) { next = ih.next; length--; if(ih.hasTargeters()) { // Still got targeters? target_vec.add(ih); buf.append(ih.toString(true) + " "); ih.next = ih.prev = null; } else ih.dispose(); } buf.append("}"); if(!target_vec.isEmpty()) { InstructionHandle[] targeted = new InstructionHandle[target_vec.size()]; target_vec.toArray(targeted); throw new TargetLostException(targeted, buf.toString()); } } /** * Remove instruction from this list. The corresponding Instruction * handles must not be reused! * * @param ih instruction (handle) to remove */ public void delete(InstructionHandle ih) throws TargetLostException { remove(ih.prev, ih.next); } /** * Remove instruction from this list. The corresponding Instruction * handles must not be reused! * * @param i instruction to remove */ public void delete(Instruction i) throws TargetLostException { InstructionHandle ih; if((ih = findInstruction1(i)) == null) throw new ClassGenException("Instruction " + i + " is not contained in this list."); delete(ih); } /** * Remove instructions from instruction `from' to instruction `to' contained * in this list. The user must ensure that `from' is an instruction before * `to', or risk havoc. The corresponding Instruction handles must not be reused! * * @param from where to start deleting (inclusive) * @param to where to end deleting (inclusive) */ public void delete(InstructionHandle from, InstructionHandle to) throws TargetLostException { remove(from.prev, to.next); } /** * Remove instructions from instruction `from' to instruction `to' contained * in this list. The user must ensure that `from' is an instruction before * `to', or risk havoc. The corresponding Instruction handles must not be reused! * * @param from where to start deleting (inclusive) * @param to where to end deleting (inclusive) */ public void delete(Instruction from, Instruction to) throws TargetLostException { InstructionHandle from_ih, to_ih; if((from_ih = findInstruction1(from)) == null) throw new ClassGenException("Instruction " + from + " is not contained in this list."); if((to_ih = findInstruction2(to)) == null) throw new ClassGenException("Instruction " + to + " is not contained in this list."); delete(from_ih, to_ih); } /** * Search for given Instruction reference, start at beginning of list. * * @param i instruction to search for * @return instruction found on success, null otherwise */ private InstructionHandle findInstruction1(Instruction i) { for(InstructionHandle ih=start; ih != null; ih = ih.next) if(ih.instruction == i) return ih; return null; } /** * Search for given Instruction reference, start at end of list * * @param i instruction to search for * @return instruction found on success, null otherwise */ private InstructionHandle findInstruction2(Instruction i) { for(InstructionHandle ih=end; ih != null; ih = ih.prev) if(ih.instruction == i) return ih; return null; } public boolean contains(InstructionHandle i) { if(i == null) return false; for(InstructionHandle ih=start; ih != null; ih = ih.next) if(ih == i) return true; return false; } public boolean contains(Instruction i) { return findInstruction1(i) != null; } public void setPositions() { setPositions(false); } /** * Give all instructions their position number (offset in byte stream), i.e., * make the list ready to be dumped. * * @param check Perform sanity checks, e.g. if all targeted instructions really belong * to this list */ public void setPositions(boolean check) { int max_additional_bytes = 0, additional_bytes = 0; int index = 0, count = 0; int[] pos = new int[length]; /* Pass 0: Sanity checks */ if(check) { for(InstructionHandle ih=start; ih != null; ih = ih.next) { Instruction i = ih.instruction; if(i instanceof BranchInstruction) { // target instruction within list? Instruction inst = ((BranchInstruction)i).getTarget().instruction; if(!contains(inst)) throw new ClassGenException("Branch target of " + Constants.OPCODE_NAMES[i.opcode] + ":" + inst + " not in instruction list"); if(i instanceof Select) { InstructionHandle[] targets = ((Select)i).getTargets(); for(int j=0; j < targets.length; j++) { inst = targets[j].instruction; if(!contains(inst)) throw new ClassGenException("Branch target of " + Constants.OPCODE_NAMES[i.opcode] + ":" + inst + " not in instruction list"); } } if(!(ih instanceof BranchHandle)) throw new ClassGenException("Branch instruction " + Constants.OPCODE_NAMES[i.opcode] + ":" + inst + " not contained in BranchHandle."); } } } /* Pass 1: Set position numbers and sum up the maximum number of bytes an * instruction may be shifted. */ for(InstructionHandle ih=start; ih != null; ih = ih.next) { Instruction i = ih.instruction; ih.setPosition(index); pos[count++] = index; /* Get an estimate about how many additional bytes may be added, because * BranchInstructions may have variable length depending on the target * offset (short vs. int) or alignment issues (TABLESWITCH and * LOOKUPSWITCH). */ switch(i.getOpcode()) { case Constants.JSR: case Constants.GOTO: max_additional_bytes += 2; break; case Constants.TABLESWITCH: case Constants.LOOKUPSWITCH: max_additional_bytes += 3; break; } index += i.getLength(); } /* Pass 2: Expand the variable-length (Branch)Instructions depending on * the target offset (short or int) and ensure that branch targets are * within this list. */ for(InstructionHandle ih=start; ih != null; ih = ih.next) additional_bytes += ih.updatePosition(additional_bytes, max_additional_bytes); /* Pass 3: Update position numbers (which may have changed due to the * preceding expansions), like pass 1. */ index=count=0; for(InstructionHandle ih=start; ih != null; ih = ih.next) { Instruction i = ih.instruction; ih.setPosition(index); pos[count++] = index; index += i.getLength(); } byte_positions = new int[count]; // Trim to proper size System.arraycopy(pos, 0, byte_positions, 0, count); } /** * When everything is finished, use this method to convert the instruction * list into an array of bytes. * * @return the byte code ready to be dumped */ public byte[] getByteCode() { // Update position indices of instructions setPositions(); ByteArrayOutputStream b = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(b); try { for(InstructionHandle ih=start; ih != null; ih = ih.next) { Instruction i = ih.instruction; i.dump(out); // Traverse list } } catch(IOException e) { System.err.println(e); return null; } return b.toByteArray(); } /** * @return an array of instructions without target information for branch instructions. */ public Instruction[] getInstructions() { ByteSequence bytes = new ByteSequence(getByteCode()); ArrayList instructions = new ArrayList(); try { while(bytes.available() > 0) { instructions.add(Instruction.readInstruction(bytes)); } } catch(IOException e) { throw new ClassGenException(e.toString()); } Instruction[] result = new Instruction[instructions.size()]; instructions.toArray(result); return result; } public String toString() { return toString(true); } /** * @param verbose toggle output format * @return String containing all instructions in this list. */ public String toString(boolean verbose) { StringBuffer buf = new StringBuffer(); for(InstructionHandle ih=start; ih != null; ih = ih.next) { buf.append(ih.toString(verbose) + "\n"); } return buf.toString(); } /** * @return Enumeration that lists all instructions (handles) */ public Iterator iterator() { return new Iterator() { private InstructionHandle ih = start; public InstructionHandle next() { InstructionHandle i = ih; ih = ih.next; return i; } public void remove() { throw new UnsupportedOperationException(); } public boolean hasNext() { return ih != null; } }; } /** * @return array containing all instructions (handles) */ public InstructionHandle[] getInstructionHandles() { InstructionHandle[] ihs = new InstructionHandle[length]; InstructionHandle ih = start; for(int i=0; i < length; i++) { ihs[i] = ih; ih = ih.next; } return ihs; } /** * Get positions (offsets) of all instructions in the list. This relies on that * the list has been freshly created from an byte code array, or that setPositions() * has been called. Otherwise this may be inaccurate. * * @return array containing all instruction's offset in byte code */ public int[] getInstructionPositions() { return byte_positions; } /** * @return complete, i.e., deep copy of this list */ public InstructionList copy() { HashMap map = new HashMap(); InstructionList il = new InstructionList(); /* Pass 1: Make copies of all instructions, append them to the new list * and associate old instruction references with the new ones, i.e., * a 1:1 mapping. */ for(InstructionHandle ih=start; ih != null; ih = ih.next) { Instruction i = ih.instruction; Instruction c = i.copy(); // Use clone for shallow copy if(c instanceof BranchInstruction) map.put(ih, il.append((BranchInstruction)c)); else map.put(ih, il.append(c)); } /* Pass 2: Update branch targets. */ InstructionHandle ih=start; InstructionHandle ch=il.start; while(ih != null) { Instruction i = ih.instruction; Instruction c = ch.instruction; if(i instanceof BranchInstruction) { BranchInstruction bi = (BranchInstruction)i; BranchInstruction bc = (BranchInstruction)c; InstructionHandle itarget = bi.getTarget(); // old target // New target is in hash map bc.setTarget(map.get(itarget)); if(bi instanceof Select) { // Either LOOKUPSWITCH or TABLESWITCH InstructionHandle[] itargets = ((Select)bi).getTargets(); InstructionHandle[] ctargets = ((Select)bc).getTargets(); for(int j=0; j < itargets.length; j++) { // Update all targets ctargets[j] = map.get(itargets[j]); } } } ih = ih.next; ch = ch.next; } return il; } /** Replace all references to the old constant pool with references to the new * constant pool */ public void replaceConstantPool(ConstantPoolGen old_cp, ConstantPoolGen new_cp) { for(InstructionHandle ih=start; ih != null; ih = ih.next) { Instruction i = ih.instruction; if(i instanceof CPInstruction) { CPInstruction ci = (CPInstruction)i; Constant c = old_cp.getConstant(ci.getIndex()); ci.setIndex(new_cp.addConstant(c, old_cp)); } } } private void clear() { start = end = null; length = 0; } /** * Delete contents of list. Provides besser memory utilization, * because the system then may reuse the instruction handles. This * method is typically called right after * MethodGen.getMethod(). */ public void dispose() { // Traverse in reverse order, because ih.next is overwritten for(InstructionHandle ih=end; ih != null; ih = ih.prev) /* Causes BranchInstructions to release target and targeters, because it * calls dispose() on the contained instruction. */ ih.dispose(); clear(); } /** * @return start of list */ public InstructionHandle getStart() { return start; } /** * @return end of list */ public InstructionHandle getEnd() { return end; } /** * @return length of list (Number of instructions, not bytes) */ public int getLength() { return length; } /** * @return length of list (Number of instructions, not bytes) */ public int size() { return length; } /** * Redirect all references from old_target to new_target, i.e., update targets * of branch instructions. * * @param old_target the old target instruction handle * @param new_target the new target instruction handle */ public void redirectBranches(InstructionHandle old_target, InstructionHandle new_target) { for(InstructionHandle ih = start; ih != null; ih = ih.next) { Instruction i = ih.getInstruction(); if(i instanceof BranchInstruction) { BranchInstruction b = (BranchInstruction)i; InstructionHandle target = b.getTarget(); if(target == old_target) b.setTarget(new_target); if(b instanceof Select) { // Either LOOKUPSWITCH or TABLESWITCH InstructionHandle[] targets = ((Select)b).getTargets(); for(int j=0; j < targets.length; j++) // Update targets if(targets[j] == old_target) ((Select)b).setTarget(j, new_target); } } } } /** * Redirect all references of local variables from old_target to new_target. * * @param lg array of local variables * @param old_target the old target instruction handle * @param new_target the new target instruction handle * @see MethodGen */ public void redirectLocalVariables(LocalVariableGen[] lg, InstructionHandle old_target, InstructionHandle new_target) { for(int i=0; i < lg.length; i++) { InstructionHandle start = lg[i].getStart(); InstructionHandle end = lg[i].getEnd(); if(start == old_target) lg[i].setStart(new_target); if(end == old_target) lg[i].setEnd(new_target); } } /** * Redirect all references of exception handlers from old_target to new_target. * * @param exceptions array of exception handlers * @param old_target the old target instruction handle * @param new_target the new target instruction handle * @see MethodGen */ public void redirectExceptionHandlers(CodeExceptionGen[] exceptions, InstructionHandle old_target, InstructionHandle new_target) { for(int i=0; i < exceptions.length; i++) { if(exceptions[i].getStartPC() == old_target) exceptions[i].setStartPC(new_target); if(exceptions[i].getEndPC() == old_target) exceptions[i].setEndPC(new_target); if(exceptions[i].getHandlerPC() == old_target) exceptions[i].setHandlerPC(new_target); } } private ArrayList observers; /** Add observer for this object. */ public void addObserver(InstructionListObserver o) { if(observers == null) observers = new ArrayList(); observers.add(o); } /** Remove observer for this object. */ public void removeObserver(InstructionListObserver o) { if(observers != null) observers.remove(o); } /** Call notify() method on all observers. This method is not called * automatically whenever the state has changed, but has to be * called by the user after he has finished editing the object. */ public void update() { if(observers != null) for(Iterator e = observers.iterator(); e.hasNext(); ) e.next().notify(this); } }