InstructionList.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  *  Unless required by applicable law or agreed to in writing, software
  12.  *  distributed under the License is distributed on an "AS IS" BASIS,
  13.  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  *  See the License for the specific language governing permissions and
  15.  *  limitations under the License.
  16.  */
  17. package org.apache.bcel.generic;

  18. import java.io.ByteArrayOutputStream;
  19. import java.io.DataOutputStream;
  20. import java.io.IOException;
  21. import java.util.ArrayList;
  22. import java.util.Arrays;
  23. import java.util.HashMap;
  24. import java.util.Iterator;
  25. import java.util.List;
  26. import java.util.Map;
  27. import java.util.NoSuchElementException;

  28. import org.apache.bcel.Const;
  29. import org.apache.bcel.classfile.Constant;
  30. import org.apache.bcel.util.ByteSequence;
  31. import org.apache.commons.lang3.ArrayUtils;
  32. import org.apache.commons.lang3.stream.Streams;

  33. /**
  34.  * This class is a container for a list of <a href="Instruction.html">Instruction</a> objects. Instructions can be
  35.  * appended, inserted, moved, deleted, etc.. Instructions are being wrapped into
  36.  * <a href="InstructionHandle.html">InstructionHandles</a> objects that are returned upon append/insert operations. They
  37.  * give the user (read only) access to the list structure, such that it can be traversed and manipulated in a controlled
  38.  * way.
  39.  *
  40.  * A list is finally dumped to a byte code array with <a href="#getByteCode()">getByteCode</a>.
  41.  *
  42.  * @see Instruction
  43.  * @see InstructionHandle
  44.  * @see BranchHandle
  45.  */
  46. public class InstructionList implements Iterable<InstructionHandle> {

  47.     /**
  48.      * Find the target instruction (handle) that corresponds to the given target position (byte code offset).
  49.      *
  50.      * @param ihs array of instruction handles, i.e. il.getInstructionHandles()
  51.      * @param pos array of positions corresponding to ihs, i.e. il.getInstructionPositions()
  52.      * @param count length of arrays
  53.      * @param target target position to search for
  54.      * @return target position's instruction handle if available
  55.      */
  56.     public static InstructionHandle findHandle(final InstructionHandle[] ihs, final int[] pos, final int count, final int target) {
  57.         if (ihs != null && pos != null) {
  58.             int l = 0;
  59.             int r = count - 1;
  60.             /*
  61.              * Do a binary search since the pos array is orderd.
  62.              */
  63.             do {
  64.                 final int i = l + r >>> 1;
  65.                 final int j = pos[i];
  66.                 if (j == target) {
  67.                     return ihs[i];
  68.                 }
  69.                 if (target < j) {
  70.                     r = i - 1;
  71.                 } else {
  72.                     l = i + 1;
  73.                 }
  74.             } while (l <= r);
  75.         }
  76.         return null;
  77.     }

  78.     private InstructionHandle start;
  79.     private InstructionHandle end;
  80.     private int length; // number of elements in list

  81.     private int[] bytePositions; // byte code offsets corresponding to instructions

  82.     private List<InstructionListObserver> observers;

  83.     /**
  84.      * Create (empty) instruction list.
  85.      */
  86.     public InstructionList() {
  87.     }

  88.     /**
  89.      * Create instruction list containing one instruction.
  90.      *
  91.      * @param i initial instruction
  92.      */
  93.     public InstructionList(final BranchInstruction i) {
  94.         append(i);
  95.     }

  96.     /**
  97.      * Initialize instruction list from byte array.
  98.      *
  99.      * @param code byte array containing the instructions
  100.      */
  101.     public InstructionList(final byte[] code) {
  102.         int count = 0; // Contains actual length
  103.         int[] pos;
  104.         InstructionHandle[] ihs;
  105.         try (ByteSequence bytes = new ByteSequence(code)) {
  106.             ihs = new InstructionHandle[code.length];
  107.             pos = new int[code.length]; // Can't be more than that
  108.             /*
  109.              * Pass 1: Create an object for each byte code and append them to the list.
  110.              */
  111.             while (bytes.available() > 0) {
  112.                 // Remember byte offset and associate it with the instruction
  113.                 final int off = bytes.getIndex();
  114.                 pos[count] = off;
  115.                 /*
  116.                  * Read one instruction from the byte stream, the byte position is set accordingly.
  117.                  */
  118.                 final Instruction i = Instruction.readInstruction(bytes);
  119.                 InstructionHandle ih;
  120.                 if (i instanceof BranchInstruction) {
  121.                     ih = append((BranchInstruction) i);
  122.                 } else {
  123.                     ih = append(i);
  124.                 }
  125.                 ih.setPosition(off);
  126.                 ihs[count] = ih;
  127.                 count++;
  128.             }
  129.         } catch (final IOException e) {
  130.             throw new ClassGenException(e.toString(), e);
  131.         }
  132.         bytePositions = Arrays.copyOf(pos, count); // Trim to proper size
  133.         /*
  134.          * Pass 2: Look for BranchInstruction and update their targets, i.e., convert offsets to instruction handles.
  135.          */
  136.         for (int i = 0; i < count; i++) {
  137.             if (ihs[i] instanceof BranchHandle) {
  138.                 final BranchInstruction bi = (BranchInstruction) ihs[i].getInstruction();
  139.                 int target = bi.getPosition() + bi.getIndex(); /*
  140.                                                                 * Byte code position: relative -> absolute.
  141.                                                                 */
  142.                 // Search for target position
  143.                 InstructionHandle ih = findHandle(ihs, pos, count, target);
  144.                 if (ih == null) {
  145.                     throw new ClassGenException("Couldn't find target for branch: " + bi);
  146.                 }
  147.                 bi.setTarget(ih); // Update target
  148.                 // If it is a Select instruction, update all branch targets
  149.                 if (bi instanceof Select) { // Either LOOKUPSWITCH or TABLESWITCH
  150.                     final Select s = (Select) bi;
  151.                     final int[] indices = s.getIndices();
  152.                     for (int j = 0; j < indices.length; j++) {
  153.                         target = bi.getPosition() + indices[j];
  154.                         ih = findHandle(ihs, pos, count, target);
  155.                         if (ih == null) {
  156.                             throw new ClassGenException("Couldn't find target for switch: " + bi);
  157.                         }
  158.                         s.setTarget(j, ih); // Update target
  159.                     }
  160.                 }
  161.             }
  162.         }
  163.     }

  164.     /**
  165.      * Initialize list with (nonnull) compound instruction. Consumes argument list, i.e., it becomes empty.
  166.      *
  167.      * @param c compound instruction (list)
  168.      */
  169.     public InstructionList(final CompoundInstruction c) {
  170.         append(c.getInstructionList());
  171.     }

  172.     /**
  173.      * Create instruction list containing one instruction.
  174.      *
  175.      * @param i initial instruction
  176.      */
  177.     public InstructionList(final Instruction i) {
  178.         append(i);
  179.     }

  180.     /**
  181.      * Add observer for this object.
  182.      */
  183.     public void addObserver(final InstructionListObserver o) {
  184.         if (observers == null) {
  185.             observers = new ArrayList<>();
  186.         }
  187.         observers.add(o);
  188.     }

  189.     /**
  190.      * Append a branch instruction to the end of this list.
  191.      *
  192.      * @param i branch instruction to append
  193.      * @return branch instruction handle of the appended instruction
  194.      */
  195.     public BranchHandle append(final BranchInstruction i) {
  196.         final BranchHandle ih = BranchHandle.getBranchHandle(i);
  197.         append(ih);
  198.         return ih;
  199.     }

  200.     /**
  201.      * Append a compound instruction.
  202.      *
  203.      * @param c The composite instruction (containing an InstructionList)
  204.      * @return instruction handle of the first appended instruction
  205.      */
  206.     public InstructionHandle append(final CompoundInstruction c) {
  207.         return append(c.getInstructionList());
  208.     }

  209.     /**
  210.      * Append an instruction to the end of this list.
  211.      *
  212.      * @param i instruction to append
  213.      * @return instruction handle of the appended instruction
  214.      */
  215.     public InstructionHandle append(final Instruction i) {
  216.         final InstructionHandle ih = InstructionHandle.getInstructionHandle(i);
  217.         append(ih);
  218.         return ih;
  219.     }

  220.     /**
  221.      * Append a compound instruction, after instruction i.
  222.      *
  223.      * @param i Instruction in list
  224.      * @param c The composite instruction (containing an InstructionList)
  225.      * @return instruction handle of the first appended instruction
  226.      */
  227.     public InstructionHandle append(final Instruction i, final CompoundInstruction c) {
  228.         return append(i, c.getInstructionList());
  229.     }

  230.     /**
  231.      * Append a single instruction j after another instruction i, which must be in this list of course!
  232.      *
  233.      * @param i Instruction in list
  234.      * @param j Instruction to append after i in list
  235.      * @return instruction handle of the first appended instruction
  236.      */
  237.     public InstructionHandle append(final Instruction i, final Instruction j) {
  238.         return append(i, new InstructionList(j));
  239.     }

  240.     /**
  241.      * Append another list after instruction i contained in this list. Consumes argument list, i.e., it becomes empty.
  242.      *
  243.      * @param i where to append the instruction list
  244.      * @param il Instruction list to append to this one
  245.      * @return instruction handle pointing to the <B>first</B> appended instruction
  246.      */
  247.     public InstructionHandle append(final Instruction i, final InstructionList il) {
  248.         InstructionHandle ih;
  249.         if ((ih = findInstruction2(i)) == null) {
  250.             throw new ClassGenException("Instruction " + i + " is not contained in this list.");
  251.         }
  252.         return append(ih, il);
  253.     }

  254.     /**
  255.      * Append an instruction to the end of this list.
  256.      *
  257.      * @param ih instruction to append
  258.      */
  259.     private void append(final InstructionHandle ih) {
  260.         if (isEmpty()) {
  261.             start = end = ih;
  262.             ih.setNext(ih.setPrev(null));
  263.         } else {
  264.             end.setNext(ih);
  265.             ih.setPrev(end);
  266.             ih.setNext(null);
  267.             end = ih;
  268.         }
  269.         length++; // Update length
  270.     }

  271.     /**
  272.      * Append an instruction after instruction (handle) ih contained in this list.
  273.      *
  274.      * @param ih where to append the instruction list
  275.      * @param i Instruction to append
  276.      * @return instruction handle pointing to the <B>first</B> appended instruction
  277.      */
  278.     public BranchHandle append(final InstructionHandle ih, final BranchInstruction i) {
  279.         final BranchHandle bh = BranchHandle.getBranchHandle(i);
  280.         final InstructionList il = new InstructionList();
  281.         il.append(bh);
  282.         append(ih, il);
  283.         return bh;
  284.     }

  285.     /**
  286.      * Append a compound instruction.
  287.      *
  288.      * @param ih where to append the instruction list
  289.      * @param c The composite instruction (containing an InstructionList)
  290.      * @return instruction handle of the first appended instruction
  291.      */
  292.     public InstructionHandle append(final InstructionHandle ih, final CompoundInstruction c) {
  293.         return append(ih, c.getInstructionList());
  294.     }

  295.     /**
  296.      * Append an instruction after instruction (handle) ih contained in this list.
  297.      *
  298.      * @param ih where to append the instruction list
  299.      * @param i Instruction to append
  300.      * @return instruction handle pointing to the <B>first</B> appended instruction
  301.      */
  302.     public InstructionHandle append(final InstructionHandle ih, final Instruction i) {
  303.         return append(ih, new InstructionList(i));
  304.     }

  305.     /**
  306.      * Append another list after instruction (handle) ih contained in this list. Consumes argument list, i.e., it becomes
  307.      * empty.
  308.      *
  309.      * @param ih where to append the instruction list
  310.      * @param il Instruction list to append to this one
  311.      * @return instruction handle pointing to the <B>first</B> appended instruction
  312.      */
  313.     public InstructionHandle append(final InstructionHandle ih, final InstructionList il) {
  314.         if (il == null) {
  315.             throw new ClassGenException("Appending null InstructionList");
  316.         }
  317.         if (il.isEmpty()) {
  318.             return ih;
  319.         }
  320.         final InstructionHandle next = ih.getNext();
  321.         final InstructionHandle ret = il.start;
  322.         ih.setNext(il.start);
  323.         il.start.setPrev(ih);
  324.         il.end.setNext(next);
  325.         if (next != null) {
  326.             next.setPrev(il.end);
  327.         } else {
  328.             end = il.end; // Update end ...
  329.         }
  330.         length += il.length; // Update length
  331.         il.clear();
  332.         return ret;
  333.     }

  334.     /**
  335.      * Append another list to this one. Consumes argument list, i.e., it becomes empty.
  336.      *
  337.      * @param il list to append to end of this list
  338.      * @return instruction handle of the <B>first</B> appended instruction
  339.      */
  340.     public InstructionHandle append(final InstructionList il) {
  341.         if (il == null) {
  342.             throw new ClassGenException("Appending null InstructionList");
  343.         }
  344.         if (il.isEmpty()) {
  345.             return null;
  346.         }
  347.         if (isEmpty()) {
  348.             start = il.start;
  349.             end = il.end;
  350.             length = il.length;
  351.             il.clear();
  352.             return start;
  353.         }
  354.         return append(end, il); // was end.instruction
  355.     }

  356.     private void clear() {
  357.         start = end = null;
  358.         length = 0;
  359.     }

  360.     public boolean contains(final Instruction i) {
  361.         return findInstruction1(i) != null;
  362.     }

  363.     public boolean contains(final InstructionHandle i) {
  364.         if (i == null) {
  365.             return false;
  366.         }
  367.         for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) {
  368.             if (ih == i) {
  369.                 return true;
  370.             }
  371.         }
  372.         return false;
  373.     }

  374.     /**
  375.      * @return complete, i.e., deep copy of this list
  376.      */
  377.     public InstructionList copy() {
  378.         final Map<InstructionHandle, InstructionHandle> map = new HashMap<>();
  379.         final InstructionList il = new InstructionList();
  380.         /*
  381.          * Pass 1: Make copies of all instructions, append them to the new list and associate old instruction references with
  382.          * the new ones, i.e., a 1:1 mapping.
  383.          */
  384.         for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) {
  385.             final Instruction i = ih.getInstruction();
  386.             final Instruction c = i.copy(); // Use clone for shallow copy
  387.             if (c instanceof BranchInstruction) {
  388.                 map.put(ih, il.append((BranchInstruction) c));
  389.             } else {
  390.                 map.put(ih, il.append(c));
  391.             }
  392.         }
  393.         /*
  394.          * Pass 2: Update branch targets.
  395.          */
  396.         InstructionHandle ih = start;
  397.         InstructionHandle ch = il.start;
  398.         while (ih != null) {
  399.             final Instruction i = ih.getInstruction();
  400.             final Instruction c = ch.getInstruction();
  401.             if (i instanceof BranchInstruction) {
  402.                 final BranchInstruction bi = (BranchInstruction) i;
  403.                 final BranchInstruction bc = (BranchInstruction) c;
  404.                 final InstructionHandle itarget = bi.getTarget(); // old target
  405.                 // New target is in hash map
  406.                 bc.setTarget(map.get(itarget));
  407.                 if (bi instanceof Select) { // Either LOOKUPSWITCH or TABLESWITCH
  408.                     final InstructionHandle[] itargets = ((Select) bi).getTargets();
  409.                     final InstructionHandle[] ctargets = ((Select) bc).getTargets();
  410.                     for (int j = 0; j < itargets.length; j++) { // Update all targets
  411.                         ctargets[j] = map.get(itargets[j]);
  412.                     }
  413.                 }
  414.             }
  415.             ih = ih.getNext();
  416.             ch = ch.getNext();
  417.         }
  418.         return il;
  419.     }

  420.     /**
  421.      * Remove instruction from this list. The corresponding Instruction handles must not be reused!
  422.      *
  423.      * @param i instruction to remove
  424.      */
  425.     public void delete(final Instruction i) throws TargetLostException {
  426.         InstructionHandle ih;
  427.         if ((ih = findInstruction1(i)) == null) {
  428.             throw new ClassGenException("Instruction " + i + " is not contained in this list.");
  429.         }
  430.         delete(ih);
  431.     }

  432.     /**
  433.      * Remove instructions from instruction 'from' to instruction 'to' contained in this list. The user must ensure that
  434.      * 'from' is an instruction before 'to', or risk havoc. The corresponding Instruction handles must not be reused!
  435.      *
  436.      * @param from where to start deleting (inclusive)
  437.      * @param to where to end deleting (inclusive)
  438.      */
  439.     public void delete(final Instruction from, final Instruction to) throws TargetLostException {
  440.         InstructionHandle fromIh;
  441.         InstructionHandle toIh;
  442.         if ((fromIh = findInstruction1(from)) == null) {
  443.             throw new ClassGenException("Instruction " + from + " is not contained in this list.");
  444.         }
  445.         if ((toIh = findInstruction2(to)) == null) {
  446.             throw new ClassGenException("Instruction " + to + " is not contained in this list.");
  447.         }
  448.         delete(fromIh, toIh);
  449.     }

  450.     /**
  451.      * Remove instruction from this list. The corresponding Instruction handles must not be reused!
  452.      *
  453.      * @param ih instruction (handle) to remove
  454.      */
  455.     public void delete(final InstructionHandle ih) throws TargetLostException {
  456.         remove(ih.getPrev(), ih.getNext());
  457.     }

  458.     /**
  459.      * Remove instructions from instruction 'from' to instruction 'to' contained in this list. The user must ensure that
  460.      * 'from' is an instruction before 'to', or risk havoc. The corresponding Instruction handles must not be reused!
  461.      *
  462.      * @param from where to start deleting (inclusive)
  463.      * @param to where to end deleting (inclusive)
  464.      */
  465.     public void delete(final InstructionHandle from, final InstructionHandle to) throws TargetLostException {
  466.         remove(from.getPrev(), to.getNext());
  467.     }

  468.     /**
  469.      * Delete contents of list. Provides better memory utilization, because the system then may reuse the instruction
  470.      * handles. This method is typically called right after {@link MethodGen#getMethod()}.
  471.      */
  472.     public void dispose() {
  473.         // Traverse in reverse order, because ih.next is overwritten
  474.         for (InstructionHandle ih = end; ih != null; ih = ih.getPrev()) {
  475.             // Causes BranchInstructions to release target and targeters, because it calls dispose() on the contained instruction.
  476.             ih.dispose();
  477.         }
  478.         clear();
  479.     }

  480.     /**
  481.      * Gets instruction handle for instruction at byte code position pos. This only works properly, if the list is freshly
  482.      * initialized from a byte array or setPositions() has been called before this method.
  483.      *
  484.      * @param pos byte code position to search for
  485.      * @return target position's instruction handle if available
  486.      */
  487.     public InstructionHandle findHandle(final int pos) {
  488.         final int[] positions = bytePositions;
  489.         InstructionHandle ih = start;
  490.         for (int i = 0; i < length; i++) {
  491.             if (positions[i] == pos) {
  492.                 return ih;
  493.             }
  494.             ih = ih.getNext();
  495.         }
  496.         return null;
  497.     }

  498.     /**
  499.      * Search for given Instruction reference, start at beginning of list.
  500.      *
  501.      * @param i instruction to search for
  502.      * @return instruction found on success, null otherwise
  503.      */
  504.     private InstructionHandle findInstruction1(final Instruction i) {
  505.         for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) {
  506.             if (ih.getInstruction() == i) {
  507.                 return ih;
  508.             }
  509.         }
  510.         return null;
  511.     }

  512.     /**
  513.      * Search for given Instruction reference, start at end of list
  514.      *
  515.      * @param i instruction to search for
  516.      * @return instruction found on success, null otherwise
  517.      */
  518.     private InstructionHandle findInstruction2(final Instruction i) {
  519.         for (InstructionHandle ih = end; ih != null; ih = ih.getPrev()) {
  520.             if (ih.getInstruction() == i) {
  521.                 return ih;
  522.             }
  523.         }
  524.         return null;
  525.     }

  526.     /**
  527.      * When everything is finished, use this method to convert the instruction list into an array of bytes.
  528.      *
  529.      * @return the byte code ready to be dumped
  530.      */
  531.     public byte[] getByteCode() {
  532.         // Update position indices of instructions
  533.         setPositions();
  534.         final ByteArrayOutputStream b = new ByteArrayOutputStream();
  535.         final DataOutputStream out = new DataOutputStream(b);
  536.         try {
  537.             for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) {
  538.                 final Instruction i = ih.getInstruction();
  539.                 i.dump(out); // Traverse list
  540.             }
  541.             out.flush();
  542.         } catch (final IOException e) {
  543.             System.err.println(e);
  544.             return ArrayUtils.EMPTY_BYTE_ARRAY;
  545.         }
  546.         return b.toByteArray();
  547.     }

  548.     /**
  549.      * @return end of list
  550.      */
  551.     public InstructionHandle getEnd() {
  552.         return end;
  553.     }

  554.     /**
  555.      * @return array containing all instructions (handles)
  556.      */
  557.     public InstructionHandle[] getInstructionHandles() {
  558.         final InstructionHandle[] ihs = new InstructionHandle[length];
  559.         InstructionHandle ih = start;
  560.         for (int i = 0; i < length; i++) {
  561.             ihs[i] = ih;
  562.             ih = ih.getNext();
  563.         }
  564.         return ihs;
  565.     }

  566.     /**
  567.      * Gets positions (offsets) of all instructions in the list. This relies on that the list has been freshly created from
  568.      * an byte code array, or that setPositions() has been called. Otherwise this may be inaccurate.
  569.      *
  570.      * @return array containing all instruction's offset in byte code
  571.      */
  572.     public int[] getInstructionPositions() {
  573.         return bytePositions;
  574.     }

  575.     /**
  576.      * @return an array of instructions without target information for branch instructions.
  577.      */
  578.     public Instruction[] getInstructions() {
  579.         final List<Instruction> instructions = new ArrayList<>();
  580.         try (ByteSequence bytes = new ByteSequence(getByteCode())) {
  581.             while (bytes.available() > 0) {
  582.                 instructions.add(Instruction.readInstruction(bytes));
  583.             }
  584.         } catch (final IOException e) {
  585.             throw new ClassGenException(e.toString(), e);
  586.         }
  587.         return instructions.toArray(Instruction.EMPTY_ARRAY);
  588.     }

  589.     /**
  590.      * @return length of list (Number of instructions, not bytes)
  591.      */
  592.     public int getLength() {
  593.         return length;
  594.     }

  595.     /**
  596.      * @return start of list
  597.      */
  598.     public InstructionHandle getStart() {
  599.         return start;
  600.     }

  601.     /**
  602.      * Insert a branch instruction at start of this list.
  603.      *
  604.      * @param i branch instruction to insert
  605.      * @return branch instruction handle of the appended instruction
  606.      */
  607.     public BranchHandle insert(final BranchInstruction i) {
  608.         final BranchHandle ih = BranchHandle.getBranchHandle(i);
  609.         insert(ih);
  610.         return ih;
  611.     }

  612.     /**
  613.      * Insert a compound instruction.
  614.      *
  615.      * @param c The composite instruction (containing an InstructionList)
  616.      * @return instruction handle of the first inserted instruction
  617.      */
  618.     public InstructionHandle insert(final CompoundInstruction c) {
  619.         return insert(c.getInstructionList());
  620.     }

  621.     /**
  622.      * Insert an instruction at start of this list.
  623.      *
  624.      * @param i instruction to insert
  625.      * @return instruction handle of the inserted instruction
  626.      */
  627.     public InstructionHandle insert(final Instruction i) {
  628.         final InstructionHandle ih = InstructionHandle.getInstructionHandle(i);
  629.         insert(ih);
  630.         return ih;
  631.     }

  632.     /**
  633.      * Insert a compound instruction before instruction i.
  634.      *
  635.      * @param i Instruction in list
  636.      * @param c The composite instruction (containing an InstructionList)
  637.      * @return instruction handle of the first inserted instruction
  638.      */
  639.     public InstructionHandle insert(final Instruction i, final CompoundInstruction c) {
  640.         return insert(i, c.getInstructionList());
  641.     }

  642.     /**
  643.      * Insert a single instruction j before another instruction i, which must be in this list of course!
  644.      *
  645.      * @param i Instruction in list
  646.      * @param j Instruction to insert before i in list
  647.      * @return instruction handle of the first inserted instruction
  648.      */
  649.     public InstructionHandle insert(final Instruction i, final Instruction j) {
  650.         return insert(i, new InstructionList(j));
  651.     }

  652.     /**
  653.      * Insert another list before Instruction i contained in this list. Consumes argument list, i.e., it becomes empty.
  654.      *
  655.      * @param i where to append the instruction list
  656.      * @param il Instruction list to insert
  657.      * @return instruction handle pointing to the first inserted instruction, i.e., il.getStart()
  658.      */
  659.     public InstructionHandle insert(final Instruction i, final InstructionList il) {
  660.         InstructionHandle ih;
  661.         if ((ih = findInstruction1(i)) == null) {
  662.             throw new ClassGenException("Instruction " + i + " is not contained in this list.");
  663.         }
  664.         return insert(ih, il);
  665.     }

  666.     /**
  667.      * Insert an instruction at start of this list.
  668.      *
  669.      * @param ih instruction to insert
  670.      */
  671.     private void insert(final InstructionHandle ih) {
  672.         if (isEmpty()) {
  673.             start = end = ih;
  674.             ih.setNext(ih.setPrev(null));
  675.         } else {
  676.             start.setPrev(ih);
  677.             ih.setNext(start);
  678.             ih.setPrev(null);
  679.             start = ih;
  680.         }
  681.         length++;
  682.     }

  683.     /**
  684.      * Insert an instruction before instruction (handle) ih contained in this list.
  685.      *
  686.      * @param ih where to insert to the instruction list
  687.      * @param i Instruction to insert
  688.      * @return instruction handle of the first inserted instruction
  689.      */
  690.     public BranchHandle insert(final InstructionHandle ih, final BranchInstruction i) {
  691.         final BranchHandle bh = BranchHandle.getBranchHandle(i);
  692.         final InstructionList il = new InstructionList();
  693.         il.append(bh);
  694.         insert(ih, il);
  695.         return bh;
  696.     }

  697.     /**
  698.      * Insert a compound instruction.
  699.      *
  700.      * @param ih where to insert the instruction list
  701.      * @param c The composite instruction (containing an InstructionList)
  702.      * @return instruction handle of the first inserted instruction
  703.      */
  704.     public InstructionHandle insert(final InstructionHandle ih, final CompoundInstruction c) {
  705.         return insert(ih, c.getInstructionList());
  706.     }

  707.     /**
  708.      * Insert an instruction before instruction (handle) ih contained in this list.
  709.      *
  710.      * @param ih where to insert to the instruction list
  711.      * @param i Instruction to insert
  712.      * @return instruction handle of the first inserted instruction
  713.      */
  714.     public InstructionHandle insert(final InstructionHandle ih, final Instruction i) {
  715.         return insert(ih, new InstructionList(i));
  716.     }

  717.     /**
  718.      * Insert another list before Instruction handle ih contained in this list. Consumes argument list, i.e., it becomes
  719.      * empty.
  720.      *
  721.      * @param ih where to append the instruction list
  722.      * @param il Instruction list to insert
  723.      * @return instruction handle of the first inserted instruction
  724.      */
  725.     public InstructionHandle insert(final InstructionHandle ih, final InstructionList il) {
  726.         if (il == null) {
  727.             throw new ClassGenException("Inserting null InstructionList");
  728.         }
  729.         if (il.isEmpty()) {
  730.             return ih;
  731.         }
  732.         final InstructionHandle prev = ih.getPrev();
  733.         final InstructionHandle ret = il.start;
  734.         ih.setPrev(il.end);
  735.         il.end.setNext(ih);
  736.         il.start.setPrev(prev);
  737.         if (prev != null) {
  738.             prev.setNext(il.start);
  739.         } else {
  740.             start = il.start; // Update start ...
  741.         }
  742.         length += il.length; // Update length
  743.         il.clear();
  744.         return ret;
  745.     }

  746.     /**
  747.      * Insert another list.
  748.      *
  749.      * @param il list to insert before start of this list
  750.      * @return instruction handle of the first inserted instruction
  751.      */
  752.     public InstructionHandle insert(final InstructionList il) {
  753.         if (isEmpty()) {
  754.             append(il); // Code is identical for this case
  755.             return start;
  756.         }
  757.         return insert(start, il);
  758.     }

  759.     /**
  760.      * Test for empty list.
  761.      */
  762.     public boolean isEmpty() {
  763.         return start == null;
  764.     } // && end == null

  765.     /**
  766.      * @return iterator that lists all instructions (handles)
  767.      */
  768.     @Override
  769.     public Iterator<InstructionHandle> iterator() {
  770.         return new Iterator<InstructionHandle>() {

  771.             private InstructionHandle ih = start;

  772.             @Override
  773.             public boolean hasNext() {
  774.                 return ih != null;
  775.             }

  776.             @Override
  777.             public InstructionHandle next() throws NoSuchElementException {
  778.                 if (ih == null) {
  779.                     throw new NoSuchElementException();
  780.                 }
  781.                 final InstructionHandle i = ih;
  782.                 ih = ih.getNext();
  783.                 return i;
  784.             }

  785.             @Override
  786.             public void remove() {
  787.                 throw new UnsupportedOperationException();
  788.             }
  789.         };
  790.     }

  791.     /**
  792.      * Move a single instruction (handle) to a new location.
  793.      *
  794.      * @param ih moved instruction
  795.      * @param target new location of moved instruction
  796.      */
  797.     public void move(final InstructionHandle ih, final InstructionHandle target) {
  798.         move(ih, ih, target);
  799.     }

  800.     /**
  801.      * Take all instructions (handles) from "start" to "end" and append them after the new location "target". Of course,
  802.      * "end" must be after "start" and target must not be located withing this range. If you want to move something to the
  803.      * start of the list use null as value for target.
  804.      * <p>
  805.      * Any instruction targeters pointing to handles within the block, keep their targets.
  806.      * </p>
  807.      *
  808.      * @param start of moved block
  809.      * @param end of moved block
  810.      * @param target of moved block
  811.      */
  812.     public void move(final InstructionHandle start, final InstructionHandle end, final InstructionHandle target) {
  813.         // Step 1: Check constraints
  814.         if (start == null || end == null) {
  815.             throw new ClassGenException("Invalid null handle: From " + start + " to " + end);
  816.         }
  817.         if (target == start || target == end) {
  818.             throw new ClassGenException("Invalid range: From " + start + " to " + end + " contains target " + target);
  819.         }
  820.         for (InstructionHandle ih = start; ih != end.getNext(); ih = ih.getNext()) {
  821.             if (ih == null) {
  822.                 throw new ClassGenException("Invalid range: From " + start + " to " + end);
  823.             }
  824.             if (ih == target) {
  825.                 throw new ClassGenException("Invalid range: From " + start + " to " + end + " contains target " + target);
  826.             }
  827.         }
  828.         // Step 2: Temporarily remove the given instructions from the list
  829.         final InstructionHandle prev = start.getPrev();
  830.         InstructionHandle next = end.getNext();
  831.         if (prev != null) {
  832.             prev.setNext(next);
  833.         } else {
  834.             this.start = next;
  835.         }
  836.         if (next != null) {
  837.             next.setPrev(prev);
  838.         } else {
  839.             this.end = prev;
  840.         }
  841.         start.setPrev(end.setNext(null));
  842.         // Step 3: append after target
  843.         if (target == null) { // append to start of list
  844.             if (this.start != null) {
  845.                 this.start.setPrev(end);
  846.             }
  847.             end.setNext(this.start);
  848.             this.start = start;
  849.         } else {
  850.             next = target.getNext();
  851.             target.setNext(start);
  852.             start.setPrev(target);
  853.             end.setNext(next);
  854.             if (next != null) {
  855.                 next.setPrev(end);
  856.             } else {
  857.                 this.end = end;
  858.             }
  859.         }
  860.     }

  861.     /**
  862.      * Redirect all references from oldTarget to newTarget, i.e., update targets of branch instructions.
  863.      *
  864.      * @param oldTarget the old target instruction handle
  865.      * @param newTarget the new target instruction handle
  866.      */
  867.     public void redirectBranches(final InstructionHandle oldTarget, final InstructionHandle newTarget) {
  868.         for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) {
  869.             final Instruction i = ih.getInstruction();
  870.             if (i instanceof BranchInstruction) {
  871.                 final BranchInstruction b = (BranchInstruction) i;
  872.                 final InstructionHandle target = b.getTarget();
  873.                 if (target == oldTarget) {
  874.                     b.setTarget(newTarget);
  875.                 }
  876.                 if (b instanceof Select) { // Either LOOKUPSWITCH or TABLESWITCH
  877.                     final InstructionHandle[] targets = ((Select) b).getTargets();
  878.                     for (int j = 0; j < targets.length; j++) {
  879.                         if (targets[j] == oldTarget) {
  880.                             ((Select) b).setTarget(j, newTarget);
  881.                         }
  882.                     }
  883.                 }
  884.             }
  885.         }
  886.     }

  887.     /**
  888.      * Redirect all references of exception handlers from oldTarget to newTarget.
  889.      *
  890.      * @param exceptions array of exception handlers
  891.      * @param oldTarget the old target instruction handle
  892.      * @param newTarget the new target instruction handle
  893.      * @see MethodGen
  894.      */
  895.     public void redirectExceptionHandlers(final CodeExceptionGen[] exceptions, final InstructionHandle oldTarget, final InstructionHandle newTarget) {
  896.         Streams.of(exceptions).forEach(exception -> {
  897.             if (exception.getStartPC() == oldTarget) {
  898.                 exception.setStartPC(newTarget);
  899.             }
  900.             if (exception.getEndPC() == oldTarget) {
  901.                 exception.setEndPC(newTarget);
  902.             }
  903.             if (exception.getHandlerPC() == oldTarget) {
  904.                 exception.setHandlerPC(newTarget);
  905.             }
  906.         });
  907.     }

  908.     /**
  909.      * Redirect all references of local variables from oldTarget to newTarget.
  910.      *
  911.      * @param lg array of local variables
  912.      * @param oldTarget the old target instruction handle
  913.      * @param newTarget the new target instruction handle
  914.      * @see MethodGen
  915.      */
  916.     public void redirectLocalVariables(final LocalVariableGen[] lg, final InstructionHandle oldTarget, final InstructionHandle newTarget) {
  917.         Streams.of(lg).forEach(element -> {
  918.             if (element.getStart() == oldTarget) {
  919.                 element.setStart(newTarget);
  920.             }
  921.             if (element.getEnd() == oldTarget) {
  922.                 element.setEnd(newTarget);
  923.             }
  924.         });
  925.     }

  926.     /**
  927.      * Remove from instruction 'prev' to instruction 'next' both contained in this list. Throws TargetLostException when one
  928.      * of the removed instruction handles is still being targeted.
  929.      *
  930.      * @param prev where to start deleting (predecessor, exclusive)
  931.      * @param next where to end deleting (successor, exclusive)
  932.      */
  933.     private void remove(final InstructionHandle prev, InstructionHandle next) throws TargetLostException {
  934.         InstructionHandle first;
  935.         InstructionHandle last; // First and last deleted instruction
  936.         if (prev == null && next == null) {
  937.             first = start;
  938.             last = end;
  939.             start = end = null;
  940.         } else {
  941.             if (prev == null) { // At start of list
  942.                 first = start;
  943.                 start = next;
  944.             } else {
  945.                 first = prev.getNext();
  946.                 prev.setNext(next);
  947.             }
  948.             if (next == null) { // At end of list
  949.                 last = end;
  950.                 end = prev;
  951.             } else {
  952.                 last = next.getPrev();
  953.                 next.setPrev(prev);
  954.             }
  955.         }
  956.         first.setPrev(null); // Completely separated from rest of list
  957.         last.setNext(null);
  958.         final List<InstructionHandle> targetList = new ArrayList<>();
  959.         for (InstructionHandle ih = first; ih != null; ih = ih.getNext()) {
  960.             ih.getInstruction().dispose(); // e.g. BranchInstructions release their targets
  961.         }
  962.         final StringBuilder buf = new StringBuilder("{ ");
  963.         for (InstructionHandle ih = first; ih != null; ih = next) {
  964.             next = ih.getNext();
  965.             length--;
  966.             if (ih.hasTargeters()) { // Still got targeters?
  967.                 targetList.add(ih);
  968.                 buf.append(ih.toString(true)).append(" ");
  969.                 ih.setNext(ih.setPrev(null));
  970.             } else {
  971.                 ih.dispose();
  972.             }
  973.         }
  974.         buf.append("}");
  975.         if (!targetList.isEmpty()) {
  976.             throw new TargetLostException(targetList.toArray(InstructionHandle.EMPTY_ARRAY), buf.toString());
  977.         }
  978.     }

  979.     /**
  980.      * Remove observer for this object.
  981.      */
  982.     public void removeObserver(final InstructionListObserver o) {
  983.         if (observers != null) {
  984.             observers.remove(o);
  985.         }
  986.     }

  987.     /**
  988.      * Replace all references to the old constant pool with references to the new constant pool
  989.      */
  990.     public void replaceConstantPool(final ConstantPoolGen oldCp, final ConstantPoolGen newCp) {
  991.         for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) {
  992.             final Instruction i = ih.getInstruction();
  993.             if (i instanceof CPInstruction) {
  994.                 final CPInstruction ci = (CPInstruction) i;
  995.                 final Constant c = oldCp.getConstant(ci.getIndex());
  996.                 ci.setIndex(newCp.addConstant(c, oldCp));
  997.             }
  998.         }
  999.     }

  1000.     public void setPositions() { // TODO could be package-protected? (some test code would need to be repackaged)
  1001.         setPositions(false);
  1002.     }

  1003.     /**
  1004.      * Give all instructions their position number (offset in byte stream), i.e., make the list ready to be dumped.
  1005.      *
  1006.      * @param check Perform sanity checks, e.g. if all targeted instructions really belong to this list
  1007.      */
  1008.     public void setPositions(final boolean check) { // called by code in other packages
  1009.         int maxAdditionalBytes = 0;
  1010.         int additionalBytes = 0;
  1011.         int index = 0;
  1012.         int count = 0;
  1013.         final int[] pos = new int[length];
  1014.         /*
  1015.          * Pass 0: Sanity checks
  1016.          */
  1017.         if (check) {
  1018.             for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) {
  1019.                 final Instruction i = ih.getInstruction();
  1020.                 if (i instanceof BranchInstruction) { // target instruction within list?
  1021.                     Instruction inst = ((BranchInstruction) i).getTarget().getInstruction();
  1022.                     if (!contains(inst)) {
  1023.                         throw new ClassGenException("Branch target of " + Const.getOpcodeName(i.getOpcode()) + ":" + inst + " not in instruction list");
  1024.                     }
  1025.                     if (i instanceof Select) {
  1026.                         final InstructionHandle[] targets = ((Select) i).getTargets();
  1027.                         for (final InstructionHandle target : targets) {
  1028.                             inst = target.getInstruction();
  1029.                             if (!contains(inst)) {
  1030.                                 throw new ClassGenException("Branch target of " + Const.getOpcodeName(i.getOpcode()) + ":" + inst + " not in instruction list");
  1031.                             }
  1032.                         }
  1033.                     }
  1034.                     if (!(ih instanceof BranchHandle)) {
  1035.                         throw new ClassGenException(
  1036.                             "Branch instruction " + Const.getOpcodeName(i.getOpcode()) + ":" + inst + " not contained in BranchHandle.");
  1037.                     }
  1038.                 }
  1039.             }
  1040.         }
  1041.         /*
  1042.          * Pass 1: Set position numbers and sum up the maximum number of bytes an instruction may be shifted.
  1043.          */
  1044.         for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) {
  1045.             final Instruction i = ih.getInstruction();
  1046.             ih.setPosition(index);
  1047.             pos[count++] = index;
  1048.             /*
  1049.              * Gets an estimate about how many additional bytes may be added, because BranchInstructions may have variable length
  1050.              * depending on the target offset (short vs. int) or alignment issues (TABLESWITCH and LOOKUPSWITCH).
  1051.              */
  1052.             switch (i.getOpcode()) {
  1053.             case Const.JSR:
  1054.             case Const.GOTO:
  1055.                 maxAdditionalBytes += 2;
  1056.                 break;
  1057.             case Const.TABLESWITCH:
  1058.             case Const.LOOKUPSWITCH:
  1059.                 maxAdditionalBytes += 3;
  1060.                 break;
  1061.             default:
  1062.                 // TODO should this be an error?
  1063.                 break;
  1064.             }
  1065.             index += i.getLength();
  1066.         }
  1067.         /*
  1068.          * Pass 2: Expand the variable-length (Branch) Instructions depending on the target offset (short or int) and ensure that
  1069.          * branch targets are within this list.
  1070.          */
  1071.         for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) {
  1072.             additionalBytes += ih.updatePosition(additionalBytes, maxAdditionalBytes);
  1073.         }
  1074.         /*
  1075.          * Pass 3: Update position numbers (which may have changed due to the preceding expansions), like pass 1.
  1076.          */
  1077.         index = count = 0;
  1078.         for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) {
  1079.             final Instruction i = ih.getInstruction();
  1080.             ih.setPosition(index);
  1081.             pos[count++] = index;
  1082.             index += i.getLength();
  1083.         }
  1084.         bytePositions = Arrays.copyOfRange(pos, 0, count); // Trim to proper size
  1085.     }

  1086.     /**
  1087.      * @return length of list (Number of instructions, not bytes)
  1088.      */
  1089.     public int size() {
  1090.         return length;
  1091.     }

  1092.     @Override
  1093.     public String toString() {
  1094.         return toString(true);
  1095.     }

  1096.     /**
  1097.      * @param verbose toggle output format
  1098.      * @return String containing all instructions in this list.
  1099.      */
  1100.     public String toString(final boolean verbose) {
  1101.         final StringBuilder buf = new StringBuilder();
  1102.         for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) {
  1103.             buf.append(ih.toString(verbose)).append("\n");
  1104.         }
  1105.         return buf.toString();
  1106.     }

  1107.     /**
  1108.      * Call notify() method on all observers. This method is not called automatically whenever the state has changed, but
  1109.      * has to be called by the user after he has finished editing the object.
  1110.      */
  1111.     public void update() {
  1112.         if (observers != null) {
  1113.             for (final InstructionListObserver observer : observers) {
  1114.                 observer.notify(this);
  1115.             }
  1116.         }
  1117.     }
  1118. }