Package alt.jiapi.reflect

Source Code of alt.jiapi.reflect.InstructionList

package alt.jiapi.reflect;

import java.util.Iterator;
import java.util.List;
import java.util.LinkedList;

import alt.jiapi.file.ConstantPool;
import alt.jiapi.reflect.instruction.CPInstruction;
import alt.jiapi.reflect.instruction.Opcodes;

/**
* InstructionList is a place holder for Instructions.
*
* @author Mika Riekkinen
*/
public class InstructionList {

    private JiapiMethod declaringMethod;
    ConstantPool constantPool;
    List instructions;

    // modified indicates whether or not bytes are up-to-date
    private boolean modified = false;
//     private byte[] bytes;

    public InstructionList() {
        this.constantPool = new ConstantPool();
        this.instructions = new LinkedList();
    }

    /**
     * Constructor for InstructionList. Bytecode of hte method is given
     * as an argument to this Constructor. This bytecode is parsed and
     * Instructions are created for each byte level instruction(opcode).
     *
     * @param byteCode Bytecode of a method.
     */
    InstructionList(byte[] byteCode, ConstantPool cp) {
        this.constantPool = cp;

        InstructionFactory factory = new InstructionFactory(cp);
        instructions = factory.createInstructionList(byteCode);
    }



    /**
     * Constructor for InstructionList.
     *
     * @param byteCode Bytecode of a method.
     */
    private InstructionList(ConstantPool cp, List instructions) {
        this.constantPool = cp;
        this.instructions = instructions;
    }
   

    /**
     * Constructor for InstructionList.
     *
     * @param factory InstructionFactory
     */
    InstructionList(ConstantPool cp) {
        this.constantPool = cp;
        this.instructions = new LinkedList();
    }
   

    /**
     * Create an empty list.
     */
    public InstructionList createEmptyList() {
        return new InstructionList(constantPool);
    }

    /**
     * Gets this InstructionList as a byte array. This byte array
     * may be put into alt.jiapi.file.Method as a code attribute.
     * No checking is being made, whether this byte array
     * makes any sense or not. It is thus the responsibility of
     * the developer to keep this list in a correct form.
     *
     * @return a byte array representing this InstructionList
     */
    public byte[] getBytes() {
        // Calculate byte-size of list
        Iterator i = instructions.iterator();
        int bSize = 0;
        while(i.hasNext()) {
            bSize += ((Instruction)i.next()).length();
        }

        // Copy each instructions bytes to main bytes list
        byte[] bytes = new byte[bSize];
        int bIdx = 0;
        i = instructions.iterator();
        while(i.hasNext()) {
            Instruction ins = (Instruction)i.next();
            byte[] iBytes = ins.getBytes();

            for (int j = 0; j < iBytes.length; j++) {
                bytes[bIdx] = iBytes[j];
                bIdx++;
            }
        }
       
        // Turn this flag to false.
        this.modified = false;

        return bytes;
    }


    /**
     * Adds an Instruction to this InstructionList
     */
    public void add(Instruction i) {
        modified = true;
        i.setAttribute("synthetic");

        if (i instanceof CPInstruction) {
            CPInstruction cpIns = (CPInstruction)i;
            ConstantPool cp = cpIns.getConstantPool();

            if (!constantPool.equals(cp)) {
                cpIns.replaceConstantPool(constantPool);
            }
        }

        instructions.add(i);
    }

    /**
     * Adds all the Instructions in given list to this list.
     *
     * @param il InstructionList to add
     */
    public void add(InstructionList il) {
        modified = true;
        if (il.size() == 0) {
            return;
        }
       
  for (int i = 0; i < il.size(); i++) {
      add(il.get(i));
  }
    }

    /**
     * Insert an Instruction to this InstructionList
     *
     * @param idx index of the Instruction
     * @param i Instruction to insert
     */
    public boolean insert(int idx, Instruction i) {
        modified = true;
        i.setAttribute("synthetic");
       
        if (i instanceof CPInstruction) {
            CPInstruction cpIns = (CPInstruction)i;
            ConstantPool cp = cpIns.getConstantPool();

            if (!constantPool.equals(cp)) {
                cpIns.replaceConstantPool(constantPool);
            }
        }

       
        return instructions.subList(0, idx).add(i);
    }

    /**
     * Insert an InstructionList to this InstructionList
     *
     * @param idx index of the InstructionList
     * @param i Instruction to insert
     */
    public boolean insert(int idx, InstructionList il) {
        modified = true;

        if (il.size() == 0) {
            return true;
        }
       
//         if (constantPool.equals(il.constantPool)) {
//             // If two lists share constantpool, just insert all of them
//             return instructions.subList(0, idx).addAll(il.instructions);
//             //instructions.addAll(il.instructions);
//         }
//         else {
            // else insert instructions one by one
            for (int i = 0; i < il.size(); i++) {
                insert(idx + i, il.get(i));
            }
//         }
       

        return true;
    }


    /**
     * Gets an Instruction at given index.
     * IndexOutOfBoundsException is thrown if index is not within
     * this InstructionList
     *
     * @param i index
     * @return Instruction
     */
    public Instruction get(int i) {
        Instruction ins = (Instruction)instructions.get(i);

        if (ins instanceof TargetInstruction) {
            return ((TargetInstruction)ins).getTarget();
        }

        return ins;
    }


    /**
     * Replace an Instruction at given index
     *
     * @param idx Index of the Instruction to replace
     * @param idx Index of the Instruction to replace
     */
    public void replace(int idx, Instruction i) {
        if (i == null) {
            // fail fast
            throw new NullPointerException("Instruction may not be null");
        }
       
        // NOTE: We should make sure, that branches, exception table,
        // etc. remain valid.
        instructions.set(idx, i);
    }

    public void replace(InstructionList il) {
        replace(0, size(), il);
    }

    /**
     * Replaces instructions with given range. Stack-usages are
     * checked before replace. If stack usages do not match,
     * an exception is thrown.
     * Furthermore, if instructions to be replaced contain
     * some sort of 'target', like with BranchInstruction,
     * an exception is thrown. Only first instruction to be replaced
     * is allowed to be a 'target'. If this is the case, first
     * Instruction in new list becames that target.<p>
     *
     * Note, that exception table might get corrupted with this method.
     *
     * @param start start of old Instructions, inclusive
     * @param end end of old Instructions, exclusive
     * @param il InstructionList containing new Instructions.
     * @exception IllegalArgumentException is thrown, if stack-usages
     *        do not match
     * @exception IllegalArgumentException is thrown, if there is
     *        an instruction in the range, that is a target to some
     *        other entity.
     */
    public void replace(int start, int end, InstructionList il) {
        InstructionList __il = createView(start, end);
        int su1 = __il.stackUsage();
        int su2 = il.stackUsage();

        if (su1 != su2) {
            System.out.println("Replacing ");
            print(__il);
            System.out.println("with");
            print(il);
           
            throw new IllegalArgumentException("Cannot replace instruction list; stack usages differ: " + su1 + "(this list), " + su2 + " (other list)");
        }


        Instruction first = (Instruction)__il.instructions.get(0);
        if (first instanceof TargetInstruction) {
            // If first Instruction is a TargetInstruction,
            // Move it to new list
            TargetInstruction tIns = (TargetInstruction)first;
            tIns.setTarget((Instruction)il.get(0));
            il.instructions.set(0, tIns);
        }

        // Check, if view contains targets. If so, throw a n exception
        // Only forst is allowed to be a target, so skip it
        for (int i = 1; i < __il.size(); i++) {
            if (__il.instructions.get(i) instanceof TargetInstruction) {
                throw new IllegalArgumentException("Cannot replace instructions since there are at least one 'target' instruction in it");
            }
        }

        // replace instructions
        __il.clear();
        __il.add(il);
    }


    /**
     * Calculates stack-usage of this InstructionList
     *
     * @return stack usage
     */
    public int stackUsage() {
        int su = 0;
        for (int i = 0; i < size(); i++) {
            su += get(i).stackUsage();
        }

        return su;
    }

    /**
     * Gets the size of this InstructionList.
     *
     * @return number of instructions in this InstructionList
     */
    public int size() {
        return instructions.size();
    }


    /**
     * Scans this InstructionList for a opcode. Scanning is started from
     * the beginning of the InstructionList.
     *
     * @param ins Instruction to look for
     * @return index of the instruction that was found, or -1 if not found.
     */
    public int indexOf(Instruction ins) {
        return instructions.indexOf(ins);
    }



    /**
     * Scans this InstructionList for a opcode. Scanning is started from
     * the beginning of the InstructionList.
     *
     * @param opcode Opcode to look for
     * @return index of the instruction that was found, or -1 if not found.
     */
    public int indexOf(byte opcode) {
        return indexOf(new byte[]{opcode}, 0);
    }

    /**
     * Scans this InstructionList for a opcode.
     *
     * @param start Index to start scanning from
     * @param opcode Opcode to look for
     * @return index of the instruction that was found, or -1 if not found.
     */
    public int indexOf(byte opcode, int fromIndex) {
        return indexOf(new byte[]{opcode}, fromIndex);
    }

    /**
     * Scans this InstructionList for opcodes.
     *
     * @param fromIndex Index to start scanning from
     * @param opcodes opcode to look for
     * @return index of the instruction that was found, or -1 if not found.
     */
    public int indexOf(byte[] opcodes, int fromIndex) {
        for (int i = fromIndex; i < instructions.size(); i++) {
            Instruction ins = (Instruction)instructions.get(i);
            short opCode = ins.getOpcode();
           
            for (int j = 0; j < opcodes.length; j++) {
                if (opcodes[j] == opCode) {
                    return i;
                }
            }
        }

        return -1;
    }

   
    int offset(Instruction i) {
        return offset(0, i);
    }

    int offset(int start, Instruction ins) {
        int offset = 0;
        for (int i = start; i < instructions.size(); i++) {
            if (instructions.get(i).equals(ins)) {
                return offset;
            }

            offset += ((Instruction)instructions.get(i)).length();
        }

        throw new JiapiRuntimeException("Isntruction not found; " + ins);
    }

    /**
     * Get the Instruction at given offset.
     *
     * @return Instruction at offset, or null if there is no instruction
     *         at given offset.
     */
    Instruction instructionAtOffset(short offset) {
        Iterator i = instructions.iterator();

        while(i.hasNext()) {
            Instruction ins = (Instruction)i.next();
            if (ins.getOffset() == offset) {
                return ins;
            }

            if (ins.getOffset() > offset) {
                break;
            }
        }

        return null;
    }


    public String toString() {
        if (instructions.size() == 0) {
            return "<empty>";
        }

        StringBuffer sb = new StringBuffer();
        Iterator i = instructions.iterator();
        int offset = 0;
        int count = 0;
        while(i.hasNext()) {
            Instruction ins = (Instruction)i.next();
            sb.append("  #");
            sb.append(count);
            if (count < 10) {
                sb.append(' ');
            }
            if (count < 100) {
                if (size() >= 100) {
                    sb.append(' ');
                }
            }

            sb.append(" (");
            sb.append(offset);
            sb.append(")  ");

            sb.append(ins.toString());
            if (i.hasNext()) {
                sb.append('\n');
            }

            count++;
            offset += ins.length();
        }

        return sb.toString();
    }


    private boolean isModified() {
        return modified;
    }

    /**
     * Clears this InstructionList. All the Instructions are removed.
     */
    public void clear() {
        instructions.clear();
    }

    /**
     * Get the InstructionFactory, that is associated with this
     * InstructionList.
     */
    public InstructionFactory getInstructionFactory() {
        return new InstructionFactory(constantPool);
    }

    /**
     * Gets the declaring JiapiMethod of this InstructionList
     */
    public JiapiMethod getDeclaringMethod() {
        return declaringMethod;
    }

    // Package protected methods
    void setDeclaringMethod(JiapiMethod jm) {
        this.declaringMethod = jm;
    }


    /**
     * Creates a view. View is created from <code>start</code> to the
     * end of this list.
     *
     * @param start start of the view, inclusive
     */
    public InstructionList createView(int start) {
        return createView(0, instructions.size());
    }


    /**
     * Creates a view.
     *
     * @param start start of the view, inclusive
     * @param end end of the view, exclusive
     */
    public InstructionList createView(int start, int end) {
        InstructionList il =
            new InstructionList(constantPool,instructions.subList(start, end));
        il.setDeclaringMethod(declaringMethod);

        return il;
    }


    /**
     * Updates offsets of each instruction.
     */
    void updateOffsets() {
        short offset = 0;
        Iterator i = instructions.iterator();

        while(i.hasNext()) {
            Instruction ins = (Instruction)i.next();
            ins.setOffset(offset);

            // BUG: If instruction needs byte padding,
            // we must do it here.
            // if (ins instanceof CaseInstruction) ....
            offset += ins.length();
        }
    }


    private void print(InstructionList il) {
        for (int i = 0; i < il.size(); i++) {
            Instruction ins = il.get(i);
            System.out.println("  #" + (i) + " " + ins + ", stack-usage: " +
                               ins.stackUsage());
        }
    }


        /**
         * Change usage of local variables in target InstructionList so,
         * that it will not overlap with 'other' list.
         *
         * If Advice declares local variables, they start from index 0,
         * but so do local vars in target class. So this method changes
         * local variable accesses in advice to start from
         * <target.getMaxLocals()>
         *
         * @param advice InstructionList to change
         * @param maxLocalsInOtherList Max-Locals in other list
         */
        private Instruction changeLocalVars(Instruction ins) {
//             if (maxLocalsInOtherList == 0) {
//                 // Nothing to do, other list does not contain
//                 // local variables
//                 return advice;
//             }

            // BUG: These should not be needed. For some reason,
            //      JiapiMethod.getMaxLocals() returns wrong values?
            //      Commenting these out has no harm other than
            //      missing unused local variable slots
            int maxLocals = getDeclaringMethod().getMaxLocals();
            // following could be added, if no longs/doubles are in target
            // list; they reserve two slots for local vars. Another
            // silly stuff for longs/doubles in JVMs
            // maxLocalsInOtherList--;
           
            InstructionFactory factory = new InstructionFactory();
      switch(ins.getOpcode()) {
                    // -- ALOAD family --------------------------------------
                    case Opcodes.ALOAD_0:
                        return factory.aload(maxLocals);
                    case Opcodes.ALOAD_1:
                        return factory.aload(maxLocals + 1);
                    case Opcodes.ALOAD_2:
                        return factory.aload(maxLocals + 2);
                    case Opcodes.ALOAD_3:
                        return factory.aload(maxLocals + 3);
                    case Opcodes.ALOAD:
                        // Handle this differently. This
                        // form of handling does not break exception table
                        // So, we minimize damage by handling this differently
                        ins.getBytes()[1] += (byte)maxLocals;
                        break;
                    case Opcodes.ASTORE_0:
                        return factory.astore(maxLocals);
                    case Opcodes.ASTORE_1:
                        return factory.astore(maxLocals + 1);
                    case Opcodes.ASTORE_2:
                        return factory.astore(maxLocals + 2);
                    case Opcodes.ASTORE_3:
                        return factory.astore(maxLocals + 3);
                    case Opcodes.ASTORE:
                        // Handle this differently. This
                        // form of handling does not break exception table
                        // So, we minimize damage by handling this differently
                        ins.getBytes()[1] += (byte)maxLocals;
                        break;

                    // -- ILOAD family --------------------------------------
                    case Opcodes.ILOAD_0:
                        return factory.iload(maxLocals);
                    case Opcodes.ILOAD_1:
                        return factory.iload(maxLocals + 1);
                    case Opcodes.ILOAD_2:
                        return factory.iload(maxLocals + 2);
                    case Opcodes.ILOAD_3:
                        return factory.iload(maxLocals + 3);
                    case Opcodes.ILOAD:
                        // Handle this differently. This
                        // form of handling does not break exception table
                        // So, we minimize damage by handling this differently
                        ins.getBytes()[1] += (byte)maxLocals;
                        break;
                    case Opcodes.ISTORE_0:
                        return factory.istore(maxLocals);
                    case Opcodes.ISTORE_1:
                        return factory.istore(maxLocals + 1);
                    case Opcodes.ISTORE_2:
                        return factory.istore(maxLocals + 2);
                    case Opcodes.ISTORE_3:
                        return factory.istore(maxLocals + 3);
                    case Opcodes.ISTORE:
                        // Handle this differently. This
                        // form of handling does not break exception table
                        // So, we minimize damage by handling this differently
                        ins.getBytes()[1] += (byte)maxLocals;
                        break;

                    // -- DLOAD family --------------------------------------
                    case Opcodes.DLOAD_0:
                        return factory.dload(maxLocals);
                    case Opcodes.DLOAD_1:
                        return factory.dload(maxLocals + 1);
                    case Opcodes.DLOAD_2:
                        return factory.dload(maxLocals + 2);
                    case Opcodes.DLOAD_3:
                        return factory.dload(maxLocals + 3);
                    case Opcodes.DLOAD:
                        // Handle this differently. This
                        // form of handling does not break exception table
                        // So, we minimize damage by handling this differently
                        ins.getBytes()[1] += (byte)maxLocals;
                        break;
                    case Opcodes.DSTORE_0:
                        return factory.dstore(maxLocals);
                    case Opcodes.DSTORE_1:
                        return factory.dstore(maxLocals + 1);
                    case Opcodes.DSTORE_2:
                        return factory.dstore(maxLocals + 2);
                    case Opcodes.DSTORE_3:
                        return factory.dstore(maxLocals + 3);
                    case Opcodes.DSTORE:
                        // Handle this differently. This
                        // form of handling does not break exception table
                        // So, we minimize damage by handling this differently
                        ins.getBytes()[1] += (byte)maxLocals;
                        break;

                    // -- LLOAD family --------------------------------------
                    case Opcodes.LLOAD_0:
                        return factory.lload(maxLocals);
                    case Opcodes.LLOAD_1:
                        return factory.lload(maxLocals + 1);
                    case Opcodes.LLOAD_2:
                        return factory.lload(maxLocals + 2);
                    case Opcodes.LLOAD_3:
                        return factory.lload(maxLocals + 3);
                    case Opcodes.LLOAD:
                        // Handle this differently. This
                        // form of handling does not break exception table
                        // So, we minimize damage by handling this differently
                        ins.getBytes()[1] += (byte)maxLocals;
                        break;
                    case Opcodes.LSTORE_0:
                        return factory.lstore(maxLocals);
                    case Opcodes.LSTORE_1:
                        return factory.lstore(maxLocals + 1);
                    case Opcodes.LSTORE_2:
                        return factory.lstore(maxLocals + 2);
                    case Opcodes.LSTORE_3:
                        return factory.lstore(maxLocals + 3);
                    case Opcodes.LSTORE:
                        // Handle this differently. This
                        // form of handling does not break exception table
                        // So, we minimize damage by handling this differently
                        ins.getBytes()[1] += (byte)maxLocals;
                        break;
                       
                    // -- FLOAD family --------------------------------------
                    case Opcodes.FLOAD_0:
                        return factory.fload(maxLocals);
                    case Opcodes.FLOAD_1:
                        return factory.fload(maxLocals + 1);
                    case Opcodes.FLOAD_2:
                        return factory.fload(maxLocals + 2);
                    case Opcodes.FLOAD_3:
                        return factory.fload(maxLocals + 3);
                    case Opcodes.FLOAD:
                        // Handle this differently. This
                        // form of handling does not break exception table
                        // So, we minimize damage by handling this differently
                        ins.getBytes()[1] += (byte)maxLocals;
                        break;
                    case Opcodes.FSTORE_0:
                        return factory.fstore(maxLocals);
                    case Opcodes.FSTORE_1:
                        return factory.fstore(maxLocals + 1);
                    case Opcodes.FSTORE_2:
                        return factory.fstore(maxLocals + 2);
                    case Opcodes.FSTORE_3:
                        return factory.fstore(maxLocals + 3);
                    case Opcodes.FSTORE:
                        // Handle this differently. This
                        // form of handling does not break exception table
                        // So, we minimize damage by handling this differently
                        ins.getBytes()[1] += (byte)maxLocals;
                        break;
                       
      }

            return ins;
        }
}
TOP

Related Classes of alt.jiapi.reflect.InstructionList

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.