Package ch.ethz.prose.jvmai.jikesrvm.advice_weaver

Source Code of ch.ethz.prose.jvmai.jikesrvm.advice_weaver.ProceedWeaver$HotSwapMultiProceedCompiler

//
//  Copyright (C) 2004 Angela Nicoara.
//
//  $Id: ProceedWeaver.java,v 1.1 2008/11/18 11:41:32 anicoara Exp $
//  =====================================================================
//

package ch.ethz.prose.jvmai.jikesrvm.advice_weaver;

import org.apache.bcel.generic.*;

import ch.ethz.jvmai.IllegalProceedUsageException;
import ch.ethz.jvmai.JVMAIRuntimeException;

import java.util.Vector;
import java.util.Iterator;

/**
* Weaves proceed statements of an advice into redefined methods.
* <P>
* Proceed invokations must be replaced with <CODE>prepareAdvice()</CODE>
* before the proceed subroutine may be inlined into the redefined target method.
* <P>
* The 'proceed' code must be inlined with <CODE>weaveRedefinedMethod()</CODE>
* after <CODE>target</CODE> was redefined with <CODE>advice</CODE>.
* <P>
* Proceed statements will be inlined.
* <P>
* @version $Revision: 1.1 $
* @author  Angela Nicoara
* @author  Gerald Linhofer
*/
public class ProceedWeaver {

  private final static ObjectType rest  = new ObjectType("ch.ethz.prose.crosscut.REST");
  private final static ObjectType any    = new ObjectType("ch.ethz.prose.crosscut.ANY");

  private final static ObjectType booleanType    = new ObjectType("java.lang.Boolean");
  private final static ObjectType byteType    = new ObjectType("java.lang.Byte");
  private final static ObjectType characterType  = new ObjectType("java.lang.Character");
  private final static ObjectType doubleType    = new ObjectType("java.lang.Double");
  private final static ObjectType floatType    = new ObjectType("java.lang.Float");
  private final static ObjectType integerType    = new ObjectType("java.lang.Integer");
  private final static ObjectType longType    = new ObjectType("java.lang.Long");
  private final static ObjectType shortType    = new ObjectType("java.lang.Short");

  /// BCEL generator for the advice method (that may contain proceed statements)
  private final MethodGen advice;
  private final ConstantPoolGen adviceCP;
  /// BCEL generator for the target method (that will be executed for <CODE>proceed()</CODE>)
  private final MethodGen target;

  /// <CODE>true</CODE> after <CODE>prepareAdvice()</CODE> Invokations was invoked.
  private boolean isAdvicePrepared;

  // fields that are initialized after 'prepareProceedInstruction()' was called
  /// Collection that holds all found <CODE>proceed()</CODE> invokations
  private Vector proceedInstructionHandles;              // for JDK < 1.5 (compatible with newer JVMs)
  //private Vector<ProceedListEntry> proceedInstructionHandles;    // for JDK >= 1.5 (not compatible with older JVMs)
  private HotSwapProceedCompiler proceedCompiler = null;

  /**
   * Entry for <CODE>proceedInstructionHandles</CODE>
   */
  private static class ProceedListEntry {
    // handle of the proceed invoking instruction
    final InstructionHandle proceed;
    // types of the arguments explizitely passed to 'proceed()'
    final Type[] arguments; 
    // next instruction that will be executed after proceed, if it terminates regulary
    InstructionHandle executeAfterProceed;
    // instruction that will be executed if proceed throws an exception
    InstructionHandle executeAfterException;

    ProceedListEntry(InstructionHandle proceed, Type[] arguments) {
      this.proceed = proceed;
      this.arguments = arguments;
    }
  }

  /**
   * Constructor.
   *
   * @param advice advice that redefines <CODE>target<CODE>.
   * @param adviceCP constant pool of the class declaring <CODE>advice</CODE>.
   * @param target method that will be redefined by <CODE>advice</CODE>.
   */
  public ProceedWeaver(MethodGen advice, ConstantPoolGen adviceCP, MethodGen target) {
    this.advice = advice;
    this.adviceCP = adviceCP;
    this.target = target;
  }

  /**
   * Constructor.
   *
   * @param advice advice that redefines <CODE>target<CODE>.
   * @param target method that will be redefined by <CODE>advice</CODE>.
   */
  public ProceedWeaver(MethodGen advice, MethodGen target) {
    this(advice, advice.getConstantPool(), target);
  }

  /**
   * Registers all <CODE>proceed()</CODE> invokations for a later weaving of
   * the 'proceed' code.
   * <P>
   * Removes all <CODE>this</CODE> arguments, which are implicitely passed
   * as argument to <CODE>proceed()</CODE> methods.
   * <P>
   * NOTE: Only <CODE>proceed()</CODE> methods that are members of the crosscut
   *       defining <CODE>advice</CODE> are recognized as <CODE>proceed</CODE>
   *       statement.
   * <P>
   * NOTE: the proceed code must still be woven into the redefined target method
   *       with <CODE>weaveRedefinedMethod()</CODE>.
   *      
   * @return <CODE>true</CODE> if any <CODE>proceed</CODE> invokation was replaced.
   */
  public boolean prepareAdvice() {
    if(!isAdvicePrepared) {
      isAdvicePrepared = true;

      // Iterate over all instructions. Check if its a 'proceed' invokation
      // and replace it if so.
      //
      // (Iteration from the last to the first instruction is faster, forward
      // iteration would do also proceed checks for instructions that are later
      // removed by 'prepareProceedInstruction()'.)
      InstructionList instructions = advice.getInstructionList();
      for(InstructionHandle h = instructions.getEnd();
      null != h;
      h = h.getPrev())
      {
        if(isProceedInvokation(h))
          // prepare the proceed invokation for weaving
          prepareProceedInstruction(h, instructions);
      }
    }
    return hasProceed();
  }

  /**
   * Indicates if <CODE>replaceProceedInvokation()</CODE> found any <CODE>proceed()</CODE>
   * statement. This will always return <CODE>false</CODE> if <CODE>replaceProceedInvokation()</CODE>
   * was not invoked before.
   * <P>
   * NOTE: Only <CODE>proceed()</CODE> methods that are members of the crosscut
   *       defining <CODE>advice</CODE> are recognized as <CODE>proceed</CODE>
   *       statement.
   *
   * @return <CODE>true</CODE> if <CODE>replaceProceedInvokations()<CODE> found
   *       any proceed statement.
   */
  public boolean hasProceed() {
    return null != proceedInstructionHandles;
  }

  /**
   * Returns <CODE>true</CODE> if <CODE>ih</CODE> contains a proceed statement.
   * <P>
   * Proceed statements are invokations of a method thats name starts with proceed
   * and which belongs the the <CODE>MethodRedefineCut</CODE> defining <CODE>advice</CODE>
   *
   * @param ih handle of the instruction to check.
   * @return <CODE>true</CODE> if <CODE>ih</CODE> contains a proceed statement
   */
  public boolean isProceedInvokation(InstructionHandle ih) {
    boolean returnValue = false;
    Instruction instr = ih.getInstruction();

    // 1. Check if the instruction is a invoke instruction
    if (instr instanceof InvokeInstruction) {
      InvokeInstruction iinstr = (InvokeInstruction)instr;
      // 2. Check if its invokes a 'proceed*()' method belonging
      //    to the crosscut from which the advice comes.
      String proceedDeclaringClassName = iinstr.getLoadClassType(adviceCP).getClassName()//angy-BUGFIX - Remote aspect insertion
      if (advice.getClassName().equals(proceedDeclaringClassName) || "ch.ethz.prose.crosscut.MethodRedefineCut".equals(proceedDeclaringClassName)) {  //angy-BUGFIX - Remote aspect insertion
        // 3. Check if the complete method name is that of a valid proceed method.
        String methodName = iinstr.getMethodName(adviceCP);
        if (
            "proceed".equals(methodName) ||
            "proceedBoolean".equals(methodName) ||
            "proceedByte".equals(methodName) ||
            "proceedChar".equals(methodName) ||
            "proceedDouble".equals(methodName) ||
            "proceedFloat".equals(methodName) ||
            "proceedInt".equals(methodName) ||
            "proceedLong".equals(methodName) ||
            "proceedObject".equals(methodName) ||
            "proceedBooleanArray".equals(methodName) ||
            "proceedByteArray".equals(methodName) ||
            "proceedCharArray".equals(methodName) ||
            "proceedDoubleArray".equals(methodName) ||
            "proceedFloatArray".equals(methodName) ||
            "proceedIntArray".equals(methodName) ||
            "proceedLongArray".equals(methodName) ||
            "proceedObjectArray".equals(methodName)
        )
          returnValue = true;
      }
    }

    return returnValue;
  }

  /**
   * Initializes for weaving if <CODE>prepareAdvice()</CODE> finds
   * a proceed statement.
   * <P>
   * This make copies of the parts of <CODE>target</CODE> which are
   * required for proceed weaving.
   */
  private void initProceed() {
    if(null == proceedInstructionHandles) {
      proceedInstructionHandles = new Vector();            // for JDK < 1.5 (compatible with newer JVMs)
      //proceedInstructionHandles = new Vector<InstructionHandle>();  // for JDK < 1.5 (compatible with newer JVMs)
    }
  }

  /**
   * Registers a <CODE>proceed()</CODE> invokation instructions handle to
   * <CODE>proceedInstructionHandles</CODE> and removes the <CODE>this</CODE>
   * Argument of the invokation.
   * 
   * @param ih handle for the <CODE>proceed()</CODE> invokation.
   * @param instructions instruction list containing the <CODE>proceed()</CODE> invokation.
   */
  public void prepareProceedInstruction(InstructionHandle ih, InstructionList instructions) {
    // 1. Make sure that the environment is preapared
    initProceed();

    InvokeInstruction instr = (InvokeInstruction)ih.getInstruction();
    Type[] definedParameters = instr.getArgumentTypes(adviceCP);

    // 2. remove VARARGS array (if required)
    int maxDefined = definedParameters.length - 1;

    if(
        maxDefined >= 0 &&
        definedParameters[maxDefined] instanceof ArrayType
    ) {
      // may have varargs (check if the real method has also an array argument at
      // the same position)
      ArrayType varArgType = (ArrayType)definedParameters[maxDefined];
      Type[] targetParameters = target.getArgumentTypes();
      try {
        if(
            targetParameters.length <= maxDefined ||
            !varArgType.isAssignmentCompatibleWith(targetParameters[maxDefined])
        ) {
          // 2.a. has varargs (only in 'proceed()' declaration, but not in the signature
          //        of the redefined method.
          // 2.a.1. remove array instructions (but let the entries on the stack)
          int n = removeVarargsArrayInstructions(ih, instructions);
          // 2.a.2. create replacement for 'definedParameters' holding also the
          //        arguments passed via varargs.
          Type[] newDefinedParameters = new Type[maxDefined + n];
          // 2.a.2.1. copy the existing entries from 'definedParameters' to its replacement
          for(int i = maxDefined - 1; i >= 0; i--)
            newDefinedParameters[i] = definedParameters[i];
          // 2.a.2.2. add the entries from 'varargs' to the new 'definedParameters'
          Type baseType = varArgType.getElementType();
          for(int i = maxDefined; i < newDefinedParameters.length; i++)
            newDefinedParameters[i] = baseType;//Type.OBJECT;
          // 2.a.2.3. replace 'definedParameters'
          definedParameters = newDefinedParameters;
        }
      } catch(ClassNotFoundException e) {throw new JVMAIRuntimeException(e.getMessage());}
    }

    // 3. remove signature pattern arguments (if any).
    //    All instructions accessing arguments of type 'ANY' or 'REST'
    //    must be removed before redefining 'target' method.

    // 3.1. create a mapping from the argument slots to there types
    //      (probably faster than mapping all local variables)
    Type[] adviceParameters = advice.getArgumentTypes();
    // 3.1.1. calculate the number of slots used for arguments
    int argSlot = advice.isStatic() ? 0 : 1;
    int argumentSlots = argSlot;
    for(int i = 0; i < adviceParameters.length; i++)
      argumentSlots += adviceParameters[i].getSize();
    // 3.1.2. create the map
    Type[] localVariableMapper = new Type[argumentSlots];
    // 3.1.3. fill the map (only entries which are mapped to an argument).
    //        if an argument needs more than 1 slot following slot will be
    //        empty.
    for(int i = 0; i < adviceParameters.length; i++) {
      localVariableMapper[argSlot] = adviceParameters[i];
      argSlot += adviceParameters[i].getSize();
    }

    // 3.2. check all instructions that are loading arguments from
    //      the local variable table to the stack if they are accessing
    //      any argument of type 'ANY' or 'REST'
    //
    //      The problem is that 'definedParameters' holds the types for
    //      the arguments used in the declaration of 'proceed()', but
    //      the advice may passe ANY or REST in places where 'proceed()'
    //      declares an argument of type java.lang.Object.
    //
    InstructionHandle h = ih;

    for(int i = definedParameters.length - 1; i >= 0; i--) {
      // 3.2.1. get the instruction
      InstructionHandle currentHandle = getPreviousArgInstr(h);
      Instruction argInstr = currentHandle.getInstruction();
      // 3.2.2. check if it's a load instruction for an unmodified argument (of the advice)
      int slot;
      if(
          argInstr instanceof LoadInstruction &&
          (slot =((LoadInstruction)argInstr).getIndex()) < localVariableMapper.length
      ) {
        // 3.2.2.a. loads an unmodified argument -> remove argument if required
        // 3.2.2.a.a remove argument of type 'REST'
        if(rest.equals(localVariableMapper[slot])) {
          removeInstruction(h.getPrev(), instructions, h);
          definedParameters[i] = rest;
        }
        // 3.2.2.a.b. remove argument of type 'ANY'
        else if(any.equals(localVariableMapper[slot])) {
          removeInstruction(h.getPrev(), instructions, h);
          definedParameters[i] = any;
        }
        else  // 3.2.2.a.c. go ahead (no signuture pattern type)
          h = currentHandle;
      }
      else    // 3.2.3.b. go ahead
        h = currentHandle;
    }

    // 4. remove (implicite) 'this' argument loading instruction if required.
    //    (NOTE: static methods have no 'this' argument!)
    if(!(instr instanceof INVOKESTATIC)) {
      Instruction inst = h.getPrev().getInstruction();
      //      // only for debugging:
      //      if(!(inst instanceof ALOAD) || ((ALOAD)inst).getIndex() != 0) {
      //        //System.err.println(inst + " is not ALOAD_0");
      //        throw new JVMAIRuntimeException(
      //            this.getClass().getName() +
      //            " INTERNAL ERRROR: cannot remove (implicite) this argument of a proceed statement in "
      //            + advice.getClassName()
      //        );
      //      }
      removeInstruction(h.getPrev(), instructions, h);
    }

    // 5. add the new entry to 'proceedInstructionHandles'
    ProceedListEntry newEntry = new ProceedListEntry(ih, definedParameters);
    proceedInstructionHandles.add(newEntry);
  }

  /**
   * Remove instructions that are creating a vararg array. This instructions are created
   * by java compilers to passe arguments to an method defining a vararg argument.
   * <P>
   * The method arguments that where wrapped into the array are left on the stack
   * in order to invoke a method that has the passed arguments explicitly defined
   * in its signature.
   *
   * @param invokeProceed handle holding the <CODE>proceed()</CODE> invokation
   * @param instructions instruction list containing <CODE>inovkeProceed()</CODE>
   * @return number of arguments in the varargs array
   */
  final private int removeVarargsArrayInstructions(InstructionHandle invokeProceed, InstructionList instructions) {
    // Example pseudo bytecode for proceed(Object...o):
    //
    //   ICONST <size>              creation of the array
    //   ANEWARRAY <type>
    //   ---------------------------
    //   DUP                        insertion of the first entry
    //   ICONST 0
    //   (instructions that put the
    //    new entry on the stack)
    //   AASTORE
    //   ---------------------------
    //   ...
    //   ---------------------------
    //   DUP                        insertion of the last entry
    //   ICONST <max index>
    //   ...
    //   AASTORE
    //   ---------------------------
    //   INVOKE                     proceed statement
    //  

    //System.out.println("Entering: ProceedWeaver.removeVarargsArrayInstructions()");
    int retVal = 0;
    InstructionHandle currentHandle = invokeProceed.getPrev();

    // remove setting of array entries (let the entries on the stack)
    Instruction instr;
    while(
        !((instr = currentHandle.getInstruction()) instanceof ANEWARRAY)
        && !(instr instanceof NEWARRAY)
    ) {
      // remove one array element insertion
      InstructionHandle entryHandle = getPreviousArgInstr(currentHandle);
      // 1. remove 'AASTORE' (last insertion instruction, stores the new entry into the array)
      //      // only for debugging:
      //      if(!(instr instanceof ArrayInstruction))
      //        throw new JVMAIRuntimeException(this.getClass().getName() + " INTERNAL ERROR on vararg handling: expected <AASTORE> but <" + instr + ">");
      removeInstruction(currentHandle, instructions, currentHandle.getNext());
      // 2. remove 'DUP' (first insertion instruction, copies the reference to the array)
      currentHandle = entryHandle.getPrev();
      //      // only for debugging:
      //      if(!(currentHandle.getInstruction() instanceof ICONST))
      //        throw new JVMAIRuntimeException(this.getClass().getName() + " INTERNAL ERROR on vararg handling: expected <ICONST x> but <" + instr + ">");
      removeInstruction(currentHandle, instructions, entryHandle);
      // 3. remove 'ICONST <index>' (second insertion instruction, sets the index of the entry)
      currentHandle = entryHandle.getPrev();
      //      // only for debugging:
      //      if(!(currentHandle.getInstruction() instanceof DUP))
      //        throw new JVMAIRuntimeException(this.getClass().getName() + " INTERNAL ERROR on vararg handling: expected <DUP> but <" + instr + ">");
      removeInstruction(currentHandle, instructions, entryHandle);

      currentHandle = entryHandle.getPrev();
      retVal++;
    }

    // remove array creation code
    //    // only for debugging: check if we did all right
    //    if(retVal != ((ICONST)currentHandle.getPrev().getInstruction()).getValue().intValue())
    //      throw new JVMAIRuntimeException(
    //          "ProceedWeaver INTERNAL ERROR: something went wrong during codegeneration for unwrapping VARARGS"
    //      );
    // remove pushing of array size onto the stack
    removeInstruction(currentHandle.getPrev(), instructions, currentHandle.getNext());
    // remove array creation instruction
    removeInstruction(currentHandle, instructions, currentHandle.getNext());

    return retVal;
  }

  /**
   * Finds and returns the first instruction in a 'sequence' of instructions, which are
   * producing a non-void value on the stack.
   * <P>
   * NOTE: The 'sequence' may consist of only one single instruction.
   * <P>
   * Example for such a bytecode sequence: <CODE>
   * |...|ILOAD_1|ICONST_1|IADD|ISTORE_2|...|
   *       first           last   after
   * </CODE>
   *
   * @param afterSequence first instruction after the 'sequence'.
   * @return first instruction of a 'sequence' of instructions putting a (non-void) value
   *         on the stack. (this may be <CODE>null</CODE> if there is no such sequence in front of
   *         <CODE>afterSequence</CODE>).
   */
  final private InstructionHandle getPreviousArgInstr(InstructionHandle afterSequence) {
    InstructionHandle retVal = afterSequence.getPrev();
    int stackSize = 0;

    // precondition:  'retVal' is the last instruction for a sequence of instructions
    //                (with minimal length 1), which are putting an value (of unknown
    //                'non-void' type) on the stack.
    // postcondition: 'retVal' reference the first instruction in the sequence described
    //                above
    while(null  != retVal) {
      Instruction instr = retVal.getInstruction();
      stackSize += instr.produceStack(adviceCP);
      stackSize -= instr.consumeStack(adviceCP);
      if(0 < stackSize)
        // as soon as an instruction is reached, which brings the cummulated
        // 'stackSize' to a positive value is reached, the sequence is complete.
        //
        // assumption: long and double values are always produced by instructions
        //             that push/pop the full value at once to/from the stack
        //             (in other words: no different instructions for the uppor
        //             and lower part).
        break;
      retVal = retVal.getPrev();
    }

    return retVal;
  }

  /**
   * Removes instruction <CODE>ih</CODE> form instruction list <CODE>il</CODE>.
   * References to <CODE>ih</CODE> will be redirected to <CODE>newTarget</CODE>.
   *
   * @param ih handle for the instruction to remove.
   * @param il instruction list containing <CODE>ih</CODE> and <CODE>newTarget</CODE>.
   * @param newTarget handle to which all existing references to <CODE>ih</CODE> will
   *        be redirected.
   */
  static final private void removeInstruction(InstructionHandle ih, InstructionList il, InstructionHandle newTarget) {
    try {il.delete(ih);}
    catch(TargetLostException e) {
      InstructionHandle[] targets = e.getTargets();
      for (int i = 0; i < targets.length; i++) {
        InstructionTargeter[] etargeters = targets[i].getTargeters();
        for (int j = 0; j < etargeters.length; j++) {
          try {
            etargeters[j].updateTarget(targets[i], newTarget);
          } catch(ClassGenException cge) {
            // TODO: why are there still some exceptions here?
            //            System.err.println();
            //            System.err.println(cge.getMessage() + ":");
            //            System.err.println(etargeters[i]);
            //            System.err.println();
            //            cge.printStackTrace();
            //            System.err.println();
          }
        }
      }
    }
  }

  /**
   * Replaces an instruction by another one
   * @param newInstr instruction to insert.
   * @param oldInstr handle for the instruction to be replaced.
   * @param instructions instruction list containing <CODE>oldInstr</CODE>.
   * @return the handle of the inserted instruction.
   */
  static final private InstructionHandle replaceInstruction(BranchInstruction newInstr, BranchHandle oldInstr, InstructionList instructions) {
    // replace instruction
    oldInstr.setInstruction(newInstr);
    return oldInstr;
  }

  /**
   * Replaces an instruction by another one
   * @param newInstr instruction to insert.
   * @param oldInstr handle for the instruction to be replaced.
   * @param instructions instruction list containing <CODE>oldInstr</CODE>.
   * @return the handle of the inserted instruction.
   */
  static final private InstructionHandle replaceInstruction(BranchInstruction newInstr, InstructionHandle oldInstr, InstructionList instructions) {
    // replace handle
    InstructionHandle retVal = instructions.append(oldInstr, newInstr);
    removeInstruction(oldInstr, instructions, retVal);
    return retVal;
  }

  /**
   * Replaces an instruction by another one
   * @param newInstr instruction to insert.
   * @param oldInstr handle for the instruction to be replaced.
   * @param instructions instruction list containing <CODE>oldInstr</CODE>.
   * @return the handle of the inserted instruction.
   */
  static final private InstructionHandle replaceInstruction(Instruction newInstr, BranchHandle oldInstr, InstructionList instructions) {
    // replace handle
    InstructionHandle retVal = instructions.append(oldInstr, newInstr);
    removeInstruction(oldInstr, instructions, retVal);
    return retVal;
  }

  /**
   * Replaces an instruction by another one
   * @param newInstr instruction to insert.
   * @param oldInstr handle for the instruction to be replaced.
   * @param instructions instruction list containing <CODE>oldInstr</CODE>.
   * @return the handle of the inserted instruction.
   */
  static final private InstructionHandle replaceInstruction(Instruction newInstr, InstructionHandle oldInstr, InstructionList instructions) {
    // replace instruction
    oldInstr.setInstruction(newInstr);
    return oldInstr;
  }

  /**
   * Appends instructions to copy a local variable from one slot to another.
   * @param oldSlot local variable slot of the original variable
   * @param newSlot local variable slot for the copy of the variable
   * @param type type of the local variable
   * @param instructions instruction list to append the new instructions
   */
  private final static void copyLocalVariable(int oldSlot, int newSlot, Type type, InstructionList instructions) {
    // 1. push the original argument from the local variable table to the stack
    instructions.append(InstructionFactory.createLoad(type, oldSlot));
    // 2. copy the argument to its new place in the local variable table
    instructions.append(InstructionFactory.createStore(type, newSlot));
  }

  /**
   * Creates instructions for <A HREF=http://java.sun.com/j2se/1.5.0/docs/guide/language/autoboxing.html>Autoboxing</A>
   * if required.
   * <P>
   * Autoboxing wrappes primitive types into java objects and vice versa, if an primitive
   * type is assigned to an object or an object to a primitive type.
   * <P>
   * The created instruction(s) (if any is created) takes the top value from the stack
   * and replaces it with an in- or outboxed version.
   *
   * @param inType
   * @param outType
   * @param instructions
   * @param cpGen
   */
  private final static void insertAutoBoxingInstructions(Type inType, Type outType, InstructionList instructions, ConstantPoolGen cpGen) {
    if(inType.equals(outType))
      // No boxing required if inType and outType are of same type
      return;

    if(inType instanceof ReferenceType && outType instanceof ReferenceType)
      try{
        if(((ReferenceType)inType).isAssignmentCompatibleWith(outType))
          // No boxing required if inType may be assigned to outType
          return;
        //gery-angy    else
        //gery-angy      throw new IllegalProceedUsageException("incompatible argument or return type for 'proceed()'");
      } catch(ClassNotFoundException e) {throw new JVMAIRuntimeException(e.getMessage());}

      if(!(inType instanceof ObjectType) && !(outType instanceof ObjectType))
        throw new IllegalProceedUsageException("cannot convert from " + inType + " to " + outType + " for 'proceed()' statement (wrong type for an argument or return value)");

      if(inType instanceof ObjectType) {
        // outboxing (object -> primitive type)
        //
        // NOTE: insert inserts always at the begining of the instruction list
        //       so the instructions are inserted in reverse order
        //       (the last inserted instruction will be the first in the list).
        //
        //       Casting may cause runtime exceptions, it may be safer to not
        //       insert the 'CHECKCAST' instruction.
        //
        try {
          ObjectType rType = (ObjectType)inType;
          boolean noCast;

          if(Type.BOOLEAN.equals(outType)) {
            if(
                (noCast = rType.isAssignmentCompatibleWith(booleanType))
                || booleanType.isAssignmentCompatibleWith(rType)
            ) {
              instructions.insert(new INVOKEVIRTUAL(cpGen.addMethodref("java.lang.Boolean", "booleanValue", "()Z")));
              if(!noCast
                instructions.insert(new CHECKCAST(cpGen.addClass("java.lang.Boolean")));
              return;
            }
          }
          else if(Type.BYTE.equals(outType)) {
            if(
                (noCast = rType.isAssignmentCompatibleWith(byteType))
                || byteType.isAssignmentCompatibleWith(rType)
            ) {
              instructions.insert(new INVOKEVIRTUAL(cpGen.addMethodref("java.lang.Byte", "byteValue", "()B")));
              if(!noCast)
                instructions.insert(new CHECKCAST(cpGen.addClass("java.lang.Byte")));
              return;
            }
          }
          else if(Type.CHAR.equals(outType)) {
            if(
                (noCast = rType.isAssignmentCompatibleWith(characterType))
                || characterType.isAssignmentCompatibleWith(rType)
            ) {
              instructions.insert(new INVOKEVIRTUAL(cpGen.addMethodref("java.lang.Character", "charValue", "()C")));
              if(!noCast)
                instructions.insert(new CHECKCAST(cpGen.addClass("java.lang.Character")));
              return;
            }
          }
          else if(Type.DOUBLE.equals(outType)) {
            if(
                (noCast = rType.isAssignmentCompatibleWith(doubleType))
                || doubleType.isAssignmentCompatibleWith(rType)
            ) {
              instructions.insert(new INVOKEVIRTUAL(cpGen.addMethodref("java.lang.Double", "doubleValue", "()D")));
              if(!noCast)
                instructions.insert(new CHECKCAST(cpGen.addClass("java.lang.Double")));
              return;
            }
          }
          else if(Type.FLOAT.equals(outType)) {
            if(
                (noCast = rType.isAssignmentCompatibleWith(floatType))
                || floatType.isAssignmentCompatibleWith(rType)
            ) {
              instructions.insert(new INVOKEVIRTUAL(cpGen.addMethodref("java.lang.Float", "floatValue", "()F")));
              if(!noCast)
                instructions.insert(new CHECKCAST(cpGen.addClass("java.lang.Float")));
              return;
            }
          }
          else if(Type.INT.equals(outType)) {
            if(
                (noCast = rType.isAssignmentCompatibleWith(integerType))
                || integerType.isAssignmentCompatibleWith(rType)
            ) {
              instructions.insert(new INVOKEVIRTUAL(cpGen.addMethodref("java.lang.Integer", "intValue", "()I")));
              if(!noCast)
                instructions.insert(new CHECKCAST(cpGen.addClass("java.lang.Integer")));
              return;
            }
          }
          else if(Type.LONG.equals(outType)) {
            if(
                (noCast = rType.isAssignmentCompatibleWith(longType))
                || longType.isAssignmentCompatibleWith(rType)
            ) {
              instructions.insert(new INVOKEVIRTUAL(cpGen.addMethodref("java.lang.Long", "longValue", "()J")));
              if(!noCast)
                instructions.insert(new CHECKCAST(cpGen.addClass("java.lang.Long")));
              return;
            }
          }
          else if(Type.SHORT.equals(outType)) {
            if(
                (noCast = rType.isAssignmentCompatibleWith(shortType))
                || shortType.isAssignmentCompatibleWith(rType)
            ) {
              instructions.insert(new INVOKEVIRTUAL(cpGen.addMethodref("java.lang.Short", "shortValue", "()S")));
              if(!noCast)
                instructions.insert(new CHECKCAST(cpGen.addClass("java.lang.Short")));
              return;
            }
          }
          else { //gery-angy
            instructions.insert(new CHECKCAST(cpGen.addClass(outType.toString()))); //gery-angy
            return//gery-angy
          //gery-angy
          //        throw new IllegalProceedUsageException("cannot outbox from " + inType + " to " + outType);  //BEFORE
        } catch(ClassNotFoundException e) { throw new JVMAIRuntimeException(e.getMessage()); }
      }
      else {
        // inboxing (primitive type -> object)
        //
        // Stack instructions have to move the arguments for the constructor
        // into the right position on the stack.
        // for types that need one word:
        //               word2, word1 -> word1, word1, word2
        // for types that need two words:
        //               word3, word2, word1 -> word1, word1, word3, word2
        //
        try {
          if(Type.BOOLEAN.equals(inType)) {
            if(booleanType.isAssignmentCompatibleWith(outType)) {
              //instructions.insert(new INVOKESTATIC(cpGen.addMethodref("java.lang.Boolean", "valueOf", "(Z)Ljava/lang/Boolean;")));  // Only for JDK > 5
              instructions.insert(new INVOKESPECIAL(cpGen.addMethodref("java.lang.Boolean", "<init>", "(Z)V")))// For JDK < 5 (works also with newer JDKs)
              instructions.insert(new SWAP());                                  // For JDK < 5 (works also with newer JDKs)
              instructions.insert(new DUP_X1());                                  // For JDK < 5 (works also with newer JDKs)
              instructions.insert(new NEW(cpGen.addClass("java.lang.Boolean")));                  // For JDK < 5 (works also with newer JDKs)
              return;
            }
          }
          else if(Type.BYTE.equals(inType)) {
            if(byteType.isAssignmentCompatibleWith(outType)) {
              //instructions.insert(new INVOKESTATIC(cpGen.addMethodref("java.lang.Byte", "valueOf", "(B)Ljava/lang/Byte;")));  // Only for JDK > 5
              instructions.insert(new INVOKESPECIAL(cpGen.addMethodref("java.lang.Byte", "<init>", "(B)V")));    // For JDK < 5 (works also with newer JDKs)
              instructions.insert(new SWAP());                                  // For JDK < 5 (works also with newer JDKs)
              instructions.insert(new DUP_X1());                                  // For JDK < 5 (works also with newer JDKs)
              instructions.insert(new NEW(cpGen.addClass("java.lang.Byte")));                    // For JDK < 5 (works also with newer JDKs)
              return;
            }
          }
          else if(Type.CHAR.equals(inType)) {
            if(characterType.isAssignmentCompatibleWith(outType)) {
              //instructions.insert(new INVOKESTATIC(cpGen.addMethodref("java.lang.Character", "valueOf", "(C)Ljava/lang/Character;")));  // Only for JDK > 5
              instructions.insert(new INVOKESPECIAL(cpGen.addMethodref("java.lang.Character", "<init>", "(C)V")))// For JDK < 5 (works also with newer JDKs)
              instructions.insert(new SWAP());                                    // For JDK < 5 (works also with newer JDKs)
              instructions.insert(new DUP_X1());                                    // For JDK < 5 (works also with newer JDKs)
              instructions.insert(new NEW(cpGen.addClass("java.lang.Character")));                  // For JDK < 5 (works also with newer JDKs)
              return;
            }
          }
          else if(Type.DOUBLE.equals(inType)) {
            if(doubleType.isAssignmentCompatibleWith(outType)) {
              //instructions.insert(new INVOKESTATIC(cpGen.addMethodref("java.lang.Double", "valueOf", "(D)Ljava/lang/Double;")));  // Only for JDK > 5
              instructions.insert(new INVOKESPECIAL(cpGen.addMethodref("java.lang.Double", "<init>", "(D)V")))// For JDK < 5 (works also with newer JDKs)
              instructions.insert(new POP2());                                  // For JDK < 5 (works also with newer JDKs)
              instructions.insert(new DUP2_X2());                                  // For JDK < 5 (works also with newer JDKs)
              instructions.insert(new DUP());                                    // For JDK < 5 (works also with newer JDKs)
              instructions.insert(new NEW(cpGen.addClass("java.lang.Double")));                  // For JDK < 5 (works also with newer JDKs)
              return;
            }
          }
          else if(Type.FLOAT.equals(inType)) {
            if(floatType.isAssignmentCompatibleWith(outType)) {
              //instructions.insert(new INVOKESTATIC(cpGen.addMethodref("java.lang.Float", "valueOf", "(F)Ljava/lang/Float;")));  // Only for JDK > 5
              instructions.insert(new INVOKESPECIAL(cpGen.addMethodref("java.lang.Float", "<init>", "(F)V")))// For JDK < 5 (works also with newer JDKs)
              instructions.insert(new SWAP());                                  // For JDK < 5 (works also with newer JDKs)
              instructions.insert(new DUP_X1());                                  // For JDK < 5 (works also with newer JDKs)
              instructions.insert(new NEW(cpGen.addClass("java.lang.Float")));                  // For JDK < 5 (works also with newer JDKs)
              return;
            }
          }
          else if(Type.INT.equals(inType)) {
            if(integerType.isAssignmentCompatibleWith(outType)) {
              //instructions.insert(new INVOKESTATIC(cpGen.addMethodref("java.lang.Integer", "valueOf", "(I)Ljava/lang/Integer;")));  // Only for JDK > 5
              instructions.insert(new INVOKESPECIAL(cpGen.addMethodref("java.lang.Integer", "<init>", "(I)V")))// For JDK < 5 (works also with newer JDKs)
              instructions.insert(new SWAP());                                  // For JDK < 5 (works also with newer JDKs)
              instructions.insert(new DUP_X1());                                  // For JDK < 5 (works also with newer JDKs)
              instructions.insert(new NEW(cpGen.addClass("java.lang.Integer")));                  // For JDK < 5 (works also with newer JDKs)
              return;
            }
          }
          else if(Type.LONG.equals(inType)) {
            if(longType.isAssignmentCompatibleWith(outType)) {
              //instructions.insert(new INVOKESTATIC(cpGen.addMethodref("java.lang.Long", "valueOf", "(J)Ljava/lang/Long;")));  // Only for JDK > 5
              instructions.insert(new INVOKESPECIAL(cpGen.addMethodref("java.lang.Long", "<init>", "(J)V")));    // For JDK < 5 (works also with newer JDKs)
              instructions.insert(new POP2());                                  // For JDK < 5 (works also with newer JDKs)
              instructions.insert(new DUP2_X2());                                  // For JDK < 5 (works also with newer JDKs)
              instructions.insert(new DUP());                                    // For JDK < 5 (works also with newer JDKs)
              instructions.insert(new NEW(cpGen.addClass("java.lang.Long")));                    // For JDK < 5 (works also with newer JDKs)
              return;
            }
          }
          else if(Type.SHORT.equals(inType)) {
            if(shortType.isAssignmentCompatibleWith(outType)) {
              //instructions.insert(new INVOKESTATIC(cpGen.addMethodref("java.lang.Short", "valueOf", "(S)Ljava/lang/Short;")));  // Only for JDK > 5
              instructions.insert(new INVOKESPECIAL(cpGen.addMethodref("java.lang.Short", "<init>", "(S)V")))// For JDK < 5 (works also with newer JDKs)
              instructions.insert(new SWAP());                                  // For JDK < 5 (works also with newer JDKs)
              instructions.insert(new DUP_X1());                                  // For JDK < 5 (works also with newer JDKs)
              instructions.insert(new NEW(cpGen.addClass("java.lang.Short")));                  // For JDK < 5 (works also with newer JDKs)
              return;
            }
          }
          throw new IllegalProceedUsageException("cannot inboxing from " + inType + " to " + outType);
        } catch(ClassNotFoundException e) { throw new JVMAIRuntimeException(e.getMessage()); }
      }
  }

  private interface HotSwapProceedCompiler {
    /**
     * Inlines proceed routines in the redefined <CODE>target</CODE> method.
     * <P>
     * Note: the advice has to be prepared with <CODE>prepareAdvice()</CODE>
     * before weaving.
     * <P>
     * @param redefinedMethod original method (after redefinition)
     * @param redefinedConstantPool
     * @throws JVMAIRuntimeException if the advice was not prepared before weaving.
     * @throws IllegalProceedUsageException if incompatible argument or return value type
     *         where defined for <CODE>proceed()</CODE>.
     */
    public abstract void weaveRedefinedMethod();
  }

  /**
   * Weaves proceed statements of an advice into redefined methods.
   * <P>
   * Proceed invokations must be replaced with <CODE>prepareAdvice()</CODE>
   * before the proceed code may be inlined into the redefined target method.
   * <P>
   * The 'proceed' code must be inlined with <CODE>weaveRedefinedMethod()</CODE>
   * after <CODE>target</CODE> was redefined with <CODE>advice</CODE>.
   * <P>
   * 'Proceed' code is directly inlined into the redefinied target method
   *  instead of the invoke instruction for all 'proceed' invokations.
   *
   */
  private class HotSwapSingleProceedCompiler implements HotSwapProceedCompiler {
    /// BCEL generator for the target method (that will be executed for <CODE>proceed()</CODE>)
    private MethodGen target;
    private ConstantPoolGen targetCP;

    /// <CODE>true</CODE> after <CODE>prepareProceedBody</CODE> was invoked. (prevents multiple invokations)
    private boolean isProceedBodyPrepared;

    // fields that are initialized after 'prepareProceedInstruction()' was called
    /// bytecode to be inlined for proceed statements
    private InstructionList proceedCode;
    /// exception handlers targeting the bytecode above
    private CodeExceptionGen[] proceedExceptionHandlers;
    /// local variable slots used for proceed
    private int maxLocalsForProceed;

    /**
     * Constructor.
     *
     * @param target method that will be executed for 'proceed' invokations.
     * @param targetCP constant pool of the class defining <CODE>target</CODE>.
     */
    public HotSwapSingleProceedCompiler(MethodGen target, ConstantPoolGen targetCP) {
      this.target = target;
      this.targetCP = targetCP;

      proceedCode = target.getInstructionList();
      proceedExceptionHandlers = target.getExceptionHandlers();
      maxLocalsForProceed = target.getMaxLocals() + 2 + target.getReturnType().getSize();

      //System.out.println("SingleProceedCompiler generated (for one single proceed statement in " + advice);
    }

    /**
     * Constructor.
     *
     * @param target method that will be executed for 'proceed' invokations.
     */
    public HotSwapSingleProceedCompiler(MethodGen target) {
      this(target, target.getConstantPool());
    }

    /**
     * Inlines proceed routines into the redefined method.
     * <P>
     * Note: the advice has to be prepared with <CODE>prepareAdvice()</CODE>
     * before weaving.
     *
     * @throws JVMAIRuntimeException if the advice was not prepared before weaving.
     * @throws IllegalProceedUsageException if incompatible argument or return value type
     *         where defined for <CODE>proceed()</CODE>.
     */
    public void weaveRedefinedMethod() {
      if(!hasProceed())
        return// nothing to do

      InstructionList code = target.getInstructionList();

      // Shortcut for proceed invoking an 'empty' method
      // (method that has only a (void) return instruction in its body)
      if(proceedCode.getStart() == proceedCode.getEnd()/*proceedCode.getLength() <= 1*/) {
        // remove all 'proceed()' invokations
        Iterator iter = proceedInstructionHandles.iterator();
        while(iter.hasNext()) {
          InstructionHandle ih = ((ProceedListEntry)iter.next()).proceed;
          removeInstruction(ih, code, ih.getNext());
        }
        // done
        return;
      }

      // 1. get first slot available for 'proceed'
      int minSlot = target.getMaxLocals()

      // 2. prepare instruction list to inline it for 'proceed()' statements
      prepareProceedBody(minSlot);

      // 3. replace 'proceed()' invokations with inlined bytecode
      for(int i = 1; i < proceedInstructionHandles.size(); i++)
        weaveProceedStatment((ProceedListEntry)proceedInstructionHandles.get(i), code, minSlot, false);
      weaveProceedStatment((ProceedListEntry)proceedInstructionHandles.get(0), code, minSlot, true);

      // 4. set MaxLocals manually (faster)
      target.setMaxLocals(minSlot + maxLocalsForProceed - 1);
    }

    /**
     * Replaces a <CODE>proceed()</CODE> invokation by inlined <CODE>proceed()</CODE> code.
     *
     * @param proceedInvokation handle for the invoke statment that is to be replaced.
     * @param code instruction list containing <CODE>proceedInvokation</CODE>
     * @param minLocal number of local variable slots used by <CODE>proceedInvokation</CODE> (without the slots for proceed).
     * @param isLastProceed <CODE>true</CODE> if this method will not be called another time.
     * @throws IllegalProceedUsageException
     */
    private void weaveProceedStatment(ProceedListEntry entry, InstructionList code, int minLocal, boolean isLastProceed) {
      InstructionHandle proceedInvokation = entry.proceed;
      InvokeInstruction instr = (InvokeInstruction)proceedInvokation.getInstruction();
      // 1. insert instructions
      // 1.1. get body
      InstructionList proceed;
      CodeExceptionGen[] pEx;
      if(isLastProceed) {
        // 1.1.a. only the last proceed statement that is woven is allowed
        //        to consume the original instruction list.
        proceed = proceedCode;
        pEx = proceedExceptionHandlers;
      }
      else {
        // 1.1.b. if there are also other 'proceed()' statments to be woven,
        //        then the instruction must be copied, because It will be
        //        consumed by the redefined methods instruction list on insertion.
        proceed = proceedCode.copy();
        pEx = copyExceptionHandlers(proceedExceptionHandlers, proceedCode, proceed);
      }
      // 1.2. add epilog (argument handling) to the body
      proceed.append(getProceedEpilog(instr));
      // 1.3. add prolog (return value handling) to the body
      proceed.insert(getProceedProlog(entry.arguments, minLocal));
      // 1.4. remember the begin and the and of the inserted code
      //      (for curbing exception handlers)
      final InstructionHandle first = proceed.getStart();
      final InstructionHandle last = proceed.getEnd();
      // 1.5. insert the code for proceed()
      code.append(proceedInvokation, proceed);
      //
      // NOTE: WHAT IS CALLED EXCEPTION HANDLER BELOW IS ABOUT THE ATTRIBUTE
      //       AND NOT ABOUT THE INSTRUCTIONS THAT ARE HANDLING AN CATCHED EXCEPTION!!!
      //
      // 2. curb/adapt exception handlers
      // 2.1. adapt targets exception handlers that are pointing
      //      to the 'proceed()' statement
      if(proceedInvokation.hasTargeters()) {
        // 2.1.1. Check all exception handler attributes referencing to 'proceedInvokation'
        //        If the proceed invokation instruction is in an exception handler clause
        //        All instructions of its inlined code must also get in this clause.
        InstructionTargeter[] targeters = proceedInvokation.getTargeters();
        for(int i = 0; i < targeters.length; i++) {
          if(targeters[i] instanceof CodeExceptionGen) {
            CodeExceptionGen ceg = (CodeExceptionGen)targeters[i];
            // 2.1.2. adapt startPC (if required)
            if(ceg.getStartPC() == proceedInvokation)
              ceg.setStartPC(first);
            // 2.1.3. adapt endPC (if required)
            if(ceg.getEndPC() == proceedInvokation)
              ceg.setEndPC(last);
            // 2.1.4. adapt handlerPC (if required)
            if(ceg.getHandlerPC() == proceedInvokation)
              ceg.setHandlerPC(first);
          }
        }
      }
      // 2.2. curb proceeds exception handlers
      for(int i = 0; i < pEx.length; i++) {
        CodeExceptionGen eh = pEx[i];
        target.addExceptionHandler(
            eh.getStartPC(),
            eh.getEndPC(),
            eh.getHandlerPC(),
            eh.getCatchType()
        );
      }
      // 5. remove invoke instruction
      removeInstruction(proceedInvokation, code, first);
    }

    /**
     * Make a copy of <CODE>orig</CODE>, adapting the ranges
     * for <CODE>newCode</CODE>, which must be an unmodified copy of
     * <CODE>origCode</CODE>.
     * <P>
     * <B>NOTE: This is about the Attribute (BCEL type <CODE>CodeExceptionGen</CODE>) and
     * not about the instructions that will run when an exception is catched!!!</B> (yes this
     * is also called exception handler)
     *
     * @param orig original exception handlers
     * @param origCode instructions for <CODE>orig</CODE>
     * @param newCode instructions for the copy
     * @return
     */
    private CodeExceptionGen[] copyExceptionHandlers(CodeExceptionGen[] orig, InstructionList origCode, InstructionList newCode) {
      //      // alternative implementation
      //      CodeExceptionGen[] retVal = orig.clone();
      //     
      //      InstructionHandle newH = newCode.getStart();
      //      InstructionHandle oldH = origCode.getStart();
      //     
      //      while(null != newH) {
      //        newCode.redirectExceptionHandlers(retVal, oldH, newH);
      //        oldH = oldH.getNext();
      //        newH = newH.getNext();
      //      }

      // 1. create an array for the exception handlers (The Attribute, not the instructions handling the exception!!!).
      CodeExceptionGen[] retVal = new CodeExceptionGen[orig.length];

      // 2. calculate (and set) the instructions position in booth instruction lists.
      newCode.setPositions();
      origCode.setPositions();

      // 3. create new exception handler referencing to instructions in
      //    'newCode' for each exception handler in 'orig'
      for(int i = 0; i < retVal.length; i++) {
        CodeExceptionGen eh = orig[i];
        retVal[i] = new CodeExceptionGen(
            newCode.findHandle(eh.getStartPC().getPosition()),
            newCode.findHandle(eh.getEndPC().getPosition()),
            newCode.findHandle(eh.getHandlerPC().getPosition()),
            eh.getCatchType()
        );
      }

      return retVal;
    }

    /**
     * Generates code to handle the arguments for <CODE>proceed()</CODE>.
     * The returned instructions should be woven in front of the inlined proceed body
     * into the redefined methods bytecode.
     *
     * @param proceedArgTypes argument types defined for proceed.
     * @param minSlot first available slot number for proceed
     * @return instruction list holding the prolog (argument handling) instructions
     * @throws IllegalProceedUsageException <CODE>proceed()</CODE> was defined with incompatible
     *         arguments.
     */
    private InstructionList getProceedProlog(Type[] proceedArgTypes, int slotIncr) {
      // 1. initialize local variables
      InstructionList retVal = new InstructionList();
      //    the argument types that are passed to the real method
      //    (aka the redefined method)
      final Type[] realArgTypes = target.getArgumentTypes();
      //    the first slot used for explicitely defined method arguments
      //    (used by the redefined method, 'proceed()' uses 'origSlot' + 'slotIncr')
      int origSlot = (target.isStatic()) ? 0 : 1;
      //    holds the number of arguments that where explicitely passed to 'proceed()'
      int maxCustomArg = proceedArgTypes.length;

      // 2. handle rest like a undefined argument
      if(maxCustomArg > 0 && rest.equals(proceedArgTypes[maxCustomArg - 1]))
        maxCustomArg--;

      // 3. proceed may not define more arguments than the real method
      if(realArgTypes.length < maxCustomArg)
        throw new IllegalProceedUsageException("more arguments for proceed (" + maxCustomArg + ") in " + advice + " than for the redefined method (" + realArgTypes.length + "): " + target);

      // 4. write instructions for explicitly passed (user defined) arguments
      int argCount = 0// number of processed arguments
      while(argCount < maxCustomArg) {
        // 4.a. argument of type ANY
        if(any.equals(proceedArgTypes[argCount]))
          copyLocalVariable(origSlot, origSlot + slotIncr, realArgTypes[argCount], retVal);
        // 4.b. argument of type REST
        else if(rest.equals(proceedArgTypes[argCount]))
          throw new IllegalProceedUsageException("'REST' is only allowed as last argument for 'proceed()'");
        // 4.c. other arguments
        else {
          // 4.c.1. move the argument to its new place in the local variable table
          retVal.insert(InstructionFactory.createStore(realArgTypes[argCount], origSlot + slotIncr));
          // 4.c.2. do in- or outboxing if required (will be inserted infront of the instruction inserted above)
          insertAutoBoxingInstructions(proceedArgTypes[argCount], realArgTypes[argCount], retVal, targetCP);
        }

        origSlot += realArgTypes[argCount].getSize();
        argCount++;
      }

      // 5. write instructions for implicite arguments
      while(argCount < realArgTypes.length) {
        copyLocalVariable(origSlot, origSlot + slotIncr, realArgTypes[argCount], retVal);

        origSlot += realArgTypes[argCount].getSize();
        argCount++;
      }

      return retVal;
    }

    /**
     * Generates code to handle the return value for <CODE>proceed()</CODE>.
     * The returned instructions should be woven after the inlined proceed body
     * into the redefined methods bytecode.
     * <P>
     * The generated code will pop the return value from the stack in case
     * of <CODE>proceed()</CODE> statements without return value are replaced
     * by methods that has return values.
     *
     * @param invokeProceed
     * @return list holding the instructions for return value handling
     *         (the list is either empty or it holds one instruction that
     *         pops the return value from the stack).
     * @throws IllegalProceedUsageException if the real return value is not
     *         compatible to the type defined for proceed.
     */
    private InstructionList getProceedEpilog(InvokeInstruction invokeProceed) {
      InstructionList retVal = new InstructionList();
      final Type proceedReturnType = invokeProceed.getReturnType(targetCP);
      final Type realReturnType = target.getReturnType();

      if(Type.VOID.equals(proceedReturnType) && !(Type.VOID.equals(realReturnType))) {
        // a. If the advice did not expect a return value from proceed.
        //    than pop the return value from the stack ()
        retVal.append(InstructionFactory.createPop(realReturnType.getSize()));
      }
      else
        // b. Check if the real return value is compatible to that one
        //    of 'proceed()'
        insertAutoBoxingInstructions(realReturnType, proceedReturnType, retVal, targetCP);

      // c. else (proceed push the return value on the stack that the advice
      //    expects there) NOTHING TO DO.

      return retVal;
    }

    /**
     * Adapts the method bytecode in <CODE>proceedCode</CODE> to inline it
     * into redefined <CODE>target</CODE>
     *
     * @param minSlot first available slot for 'proceed()'
     */
    private void prepareProceedBody(final int minSlot) {
      // 0. do this only once
      if(!isProceedBodyPrepared) {
        // 1. append a NOP (Null Operation) to mark the end of 'proceed()'
        InstructionHandle endOfProceed = proceedCode.append(new NOP());
        // 2. adapt method bytecode for 'proceed()'
        InstructionHandle ih = proceedCode.getStart();
        while(null != ih) {
          // 2.1. get instruction
          Instruction inst = ih.getInstruction();
          // 2.2. adapt local variable accesses
          if (inst instanceof LocalVariableInstruction) {
            LocalVariableInstruction lv_instr = (LocalVariableInstruction) inst;
            int oSlot = lv_instr.getIndex();
            if(0 < oSlot) {              // dont change references to 'this'
              lv_instr.setIndex(oSlot + minSlot);
            }
          }
          // 2.3. replace RETURN (return from method) by GOTO instructions
          else if (inst instanceof ReturnInstruction) {
            if(ih.getNext() == endOfProceed) {
              // it's faster to remove the instruction than to replace
              // it with a jump to its successor.
              removeInstruction(ih, proceedCode, endOfProceed);
              break;
            }
            ih = replaceInstruction(new GOTO(endOfProceed), ih, proceedCode);
          }
          // 2.4. go to next instruction
          ih = ih.getNext();
        }
        isProceedBodyPrepared = true;
      }
    }
  }


  /**
   * Weaves proceed statements of an advice into redefined methods.
   * <P>
   * Proceed invokations must be replaced with <CODE>prepareAdvice()</CODE>
   * before the proceed code may be inlined into the redefined target method.
   * <P>
   * The 'proceed' code must be inlined with <CODE>weaveRedefinedMethod()</CODE>
   * after <CODE>target</CODE> was redefined with <CODE>advice</CODE>.
   * <P>
   * 'Proceed' code is appended to the redefinied target method and all 'proceed' invokations
   * are replaced by GOTO instructions to that code.
   *
   */
  private class HotSwapMultiProceedCompiler implements HotSwapProceedCompiler {
    /// BCEL generator for the target method (that will be inlined for <CODE>proceed()</CODE>)
    private MethodGen target;
    private ConstantPoolGen targetCP;

    // fields that are initialized after 'prepareProceedInstruction()' was called
    /// bytecode to be inlined for proceed statements
    private InstructionList proceedCode;
    /// exception handlers targeting the bytecode above
    private CodeExceptionGen[] proceedExceptionHandlers;
    /// local variable slots used for proceed
    private int maxLocalsForProceed;


    /**
     * Constructor.
     *
     * @param target method to be redefined by <CODE>advice</CODE>.
     * @param targetCP
     */
    public HotSwapMultiProceedCompiler(MethodGen target, ConstantPoolGen targetCP) {
      this.target = target;
      this.targetCP = targetCP;

      proceedCode = target.getInstructionList();
      proceedExceptionHandlers = target.getExceptionHandlers();
      maxLocalsForProceed = target.getMaxLocals() + 2 + target.getReturnType().getSize();

      //System.out.println("MultiProceedCompiler generated (for proceed statements in " + advice);
    }

    /**
     * Constructor.
     *
     * @param target method to be redefined by <CODE>advice</CODE>.
     */
    public HotSwapMultiProceedCompiler(MethodGen target) {
      this(target, target.getConstantPool());
    }

    /* (non-Javadoc)
     * @see ch.ethz.inf.iks.jvmai.jvmdi.HotSwapProceedCompiler#weaveRedefinedMethod()
     */
    public void weaveRedefinedMethod() {
      //      if(!isAdvicePrepared)
      //        throw new JVMAIRuntimeException("'replaceProceedInvokations()' must be called before 'weaveProceedSubroutine'");

      if(!hasProceed())
        return// nothing to do

      if(null == target)
        throw new JVMAIRuntimeException("ProceedWeaver.weaveRedefinedMethod(): target must be set before!!!");

      InstructionList code = target.getInstructionList();

      // Shortcut for proceed invoking an 'empty' method
      // (method that has only a (void) return instruction in its body)
      if(proceedCode.getStart() == proceedCode.getEnd()/*proceedCode.getLength() <= 1*/) {
        // remove all 'proceed()' invokations
        Iterator iter = proceedInstructionHandles.iterator();
        while(iter.hasNext()) {
          InstructionHandle ih = ((ProceedListEntry)iter.next()).proceed;
          removeInstruction(ih, code, ih.getNext());
        }
        // done
        return;
      }

      // 1. get first slot available for 'proceed'
      int minSlot = target.getMaxLocals()
      // 2. Append first instruction of the proceed code
      //    (which stores the identifier for the caller location to a slot)
      InstructionHandle proceedStart;
      if(proceedInstructionHandles.size() > 1)
        proceedStart = code.append(new ISTORE(minSlot));
      else
        proceedStart = code.append(new NOP());
      // 3. replace 'proceed()' method invokations with jumps to the appended subroutine
      for(int i = 0; i < proceedInstructionHandles.size(); i++)
        weaveProceedStatment((ProceedListEntry)proceedInstructionHandles.get(i), code, minSlot, proceedStart, i);
      // 4. append full proceed code
      appendProceedBody(code, minSlot);
      // 5. set MaxLocals manually (faster)
      //      target.setMaxLocals(minSlot + maxLocalsForProceed - 1);
      target.setMaxLocals();
    }

    /**
     * Replaces a <CODE>proceed()</CODE> invokation code by a GOTO to appended 'proceed' code
     * and adds parameter and return value handling code.
     *
     * @param proceedInvokation handle for the invoke statment that is to be replaced.
     * @param code instruction list containing <CODE>proceedInvokation</CODE>
     * @param minLocal number of local variable slots used by <CODE>proceedInvokation</CODE> (without the slots for proceed).
     * @param proceedStart first instruction of the proceed subroutine.
     * @throws IllegalProceedUsageException
     */
    private void weaveProceedStatment(ProceedListEntry entry, InstructionList code, int minLocal, InstructionHandle proceedStart, int idx) {
      InstructionHandle proceedInvokation = entry.proceed;
      InvokeInstruction instr = (InvokeInstruction)proceedInvokation.getInstruction();

      // 1. create the instructions that will replace 'proceedInvokation'
      InstructionList replacementCode = new InstructionList();
      // 1.1. push the identifier (index) of this location to the stack
      if(proceedInstructionHandles.size() > 1)
        replacementCode.append(new ICONST(idx));
      // 1.2. add GOTO to the inlined proceed code
      InstructionHandle jumpToProceed = replacementCode.append(new GOTO(proceedStart));
      // 1.3. instruction for rethrowing exceptions in the bounderies of the
      //      exception handlers, which are protecting the proceed invokation.
      entry.executeAfterException = replacementCode.append(new ATHROW());
      // 1.4. add code for handling the return value (epilog).
      replacementCode.append(getProceedEpilog(instr));
      // 1.5. add code for method parameter handling (prolog)
      replacementCode.insert(getProceedProlog(entry.arguments, minLocal));
      // 2. replace proceed invokation
      InstructionHandle replacementEnd = replacementCode.getEnd();
      InstructionHandle replacementStart = replacementCode.getStart();
      // 2.1. append the generated code after 'proceedInvokation'
      code.append(proceedInvokation, replacementCode);
      // 2.2. adapt exception handler attributes referencing 'proceedInvokation'.
      extendExceptionHandlerRanges(proceedInvokation, replacementEnd, replacementStart);
      // 2.3. remove 'proceedInvokation'
      removeInstruction(proceedInvokation, code, jumpToProceed);

      entry.executeAfterProceed = entry.executeAfterException.getNext();
    }

    /**
     * Make a copy of <CODE>orig</CODE>, adapting the ranges
     * for <CODE>newCode</CODE>, which must be an unmodified copy of
     * <CODE>origCode</CODE>.
     * <P>
     * <B>NOTE: This is about the Attribute (BCEL type <CODE>CodeExceptionGen</CODE>) and
     * not about the instructions that will run when an exception is catched!!!</B> (yes this
     * is also called exception handler)
     *
     * @param orig original exception handlers
     * @param origCode instructions for <CODE>orig</CODE>
     * @param newCode instructions for the copy
     * @return
     */
    private CodeExceptionGen[] copyExceptionHandlers(CodeExceptionGen[] orig, InstructionList origCode, InstructionList newCode) {
      //      // alternative implementation
      //      CodeExceptionGen[] retVal = orig.clone();
      //     
      //      InstructionHandle newH = newCode.getStart();
      //      InstructionHandle oldH = origCode.getStart();
      //     
      //      while(null != newH) {
      //        newCode.redirectExceptionHandlers(retVal, oldH, newH);
      //        oldH = oldH.getNext();
      //        newH = newH.getNext();
      //      }

      // 1. create an array for the exception handlers (The Attribute, not the instructions handling the exception!!!).
      CodeExceptionGen[] retVal = new CodeExceptionGen[orig.length];

      // 2. calculate (and set) the instructions position in booth instruction lists.
      newCode.setPositions();
      origCode.setPositions();

      // 3. create new exception handler referencing to instructions in
      //    'newCode' for each exception handler in 'orig'
      for(int i = 0; i < retVal.length; i++) {
        CodeExceptionGen eh = orig[i];
        retVal[i] = new CodeExceptionGen(
            newCode.findHandle(eh.getStartPC().getPosition()),
            newCode.findHandle(eh.getEndPC().getPosition()),
            newCode.findHandle(eh.getHandlerPC().getPosition()),
            eh.getCatchType()
        );
      }

      return retVal;
    }

    /**
     * Change all exception handler references pointing to <CODE>oldTarget</CODE>.
     *
     * @param oldTarget target instruction
     * @param newEnd
     * @param newStart
     */
    private void extendExceptionHandlerRanges(InstructionHandle oldTarget, InstructionHandle newEnd, InstructionHandle newStart) {
      if(oldTarget.hasTargeters()) {
        // 2.1.1. Check all exception handler attributes referencing to 'proceedInvokation'
        //        If the proceed invokation instruction is in an exception handler clause
        //        All instructions of its inlined code must also get in this clause.
        InstructionTargeter[] targeters = oldTarget.getTargeters();
        for(int i = 0; i < targeters.length; i++) {
          if(targeters[i] instanceof CodeExceptionGen) {
            CodeExceptionGen ceg = (CodeExceptionGen)targeters[i];
            // 2.1.2. adapt startPC (if required)
            if(ceg.getStartPC() == oldTarget)
              ceg.setStartPC(newStart);
            // 2.1.3. adapt endPC (if required)
            if(ceg.getEndPC() == oldTarget)
              ceg.setEndPC(newEnd);
            // 2.1.4. adapt handlerPC (if required)
            if(ceg.getHandlerPC() == oldTarget)
              ceg.setHandlerPC(newStart);
          }
        }
      }

    }

    /**
     * Generates code to handle the arguments for <CODE>proceed()</CODE>.
     * The returned instructions should be woven in front of the inlined proceed body
     * into the redefined methods bytecode.
     *
     * @param proceedArgTypes argument types defined for proceed.
     * @param minSlot first available slot number for proceed
     * @return instruction list holding the prolog (argument handling) instructions
     * @throws IllegalProceedUsageException <CODE>proceed()</CODE> was defined with incompatible
     *         arguments.
     */
    private InstructionList getProceedProlog(Type[] proceedArgTypes, int slotIncr) {
      // 1. initialize local variables
      InstructionList retVal = new InstructionList();
      //    the argument types that are passed to the real method
      //    (aka the redefined method)
      final Type[] realArgTypes = target.getArgumentTypes();
      //    the first slot used for explicitely defined method arguments
      //    (used by the redefined method, 'proceed()' uses 'origSlot' + 'slotIncr')
      int origSlot = (target.isStatic()) ? 0 : 1;
      //    holds the number of arguments that where explicitely passed to 'proceed()'
      int maxCustomArg = proceedArgTypes.length;

      // 2. handle rest like a undefined argument
      if(maxCustomArg > 0 && rest.equals(proceedArgTypes[maxCustomArg - 1]))
        maxCustomArg--;

      // 3. proceed may not define more arguments than the real method
      if(realArgTypes.length < maxCustomArg)
        throw new IllegalProceedUsageException("more arguments for proceed (" + maxCustomArg + ") in " + advice + " than for the redefined method (" + realArgTypes.length + "): " + target);

      // 4. write instructions for explicitly passed (user defined) arguments
      int argCount = 0// number of processed arguments
      while(argCount < maxCustomArg) {
        // 4.a. argument of type ANY
        if(any.equals(proceedArgTypes[argCount]))
          copyLocalVariable(origSlot, origSlot + slotIncr, realArgTypes[argCount], retVal);
        // 4.b. argument of type REST
        else if(rest.equals(proceedArgTypes[argCount]))
          throw new IllegalProceedUsageException("'REST' is only allowed as last argument for 'proceed()'");
        // 4.c. other arguments
        else {
          // 4.c.1. move the argument to its new place in the local variable table
          retVal.insert(InstructionFactory.createStore(realArgTypes[argCount], origSlot + slotIncr));
          // 4.c.2. do in- or outboxing if required (will be inserted infront of the instruction inserted above)
          insertAutoBoxingInstructions(proceedArgTypes[argCount], realArgTypes[argCount], retVal, targetCP);
        }

        origSlot += realArgTypes[argCount].getSize();
        argCount++;
      }

      // 5. write instructions for implicite arguments
      while(argCount < realArgTypes.length) {
        copyLocalVariable(origSlot, origSlot + slotIncr, realArgTypes[argCount], retVal);

        origSlot += realArgTypes[argCount].getSize();
        argCount++;
      }

      return retVal;
    }

    /**
     * Generates code to handle the return value for <CODE>proceed()</CODE>.
     * The returned instructions should be woven after the inlined proceed body
     * into the redefined methods bytecode.
     * <P>
     * The generated code will pop the return value from the stack in case
     * of <CODE>proceed()</CODE> statements without return value are replaced
     * by methods that has return values.
     *
     * @param invokeProceed
     * @return list holding the instructions for return value handling
     *         (the list is either empty or it holds one instruction that
     *         pops the return value from the stack or autoboxing code).
     * @throws IllegalProceedUsageException if the real return value is not
     *         compatible to the type defined for proceed.
     */
    private InstructionList getProceedEpilog(InvokeInstruction invokeProceed) {
      InstructionList retVal = new InstructionList();
      final Type proceedReturnType = invokeProceed.getReturnType(targetCP);

      if(!Type.VOID.equals(proceedReturnType))
        // insert autoboxing code if required
        insertAutoBoxingInstructions(target.getReturnType(), proceedReturnType, retVal, targetCP);
      else
        if(!Type.VOID.equals(target.getReturnType()))
          // pop not used return value from stack
          retVal.append(InstructionFactory.createPop(target.getReturnType().getSize()));

      return retVal;
    }

    /**
     * Append TABLESWITCH code for returning from inlined proceed code.
     * <P>
     * @param il instruction list where the generated code will be appended.
     * @param idxSlot slot of the local variable where the identifier (index)
     *        of the return location is stored.
     * @return begin of the appended code
     */
    private InstructionHandle appendTableSwitches(InstructionList il, int idxSlot) {
      final int size = proceedInstructionHandles.size();
      if(1 < size) {
        // 1.   Normal return from proceed
        // 1.1. push the indentifier for the caller location to the stack
        InstructionHandle retVal = il.append(new ILOAD(idxSlot));
        // 1.2. switch statement for normal return from proceed
        int[] idxList = new int[size];
        InstructionHandle[] targetList = new InstructionHandle[size];
        for(int i = 0; i < size; i++) {
          idxList[i] = i;
          targetList[i] = ((ProceedListEntry)proceedInstructionHandles.get(i)).executeAfterProceed;
        }
        il.append(new TABLESWITCH(idxList, targetList, targetList[0]));
        // 2.   Exception handler code
        // 2.1. push the indentifier for the caller location to the stack
        il.append(new ILOAD(idxSlot));
        // 2.2. switch statement for normal return from proceed
        InstructionHandle[] eTargetList = new InstructionHandle[size];
        for(int i = 0; i < size; i++) {
          idxList[i] = i;
          eTargetList[i] = ((ProceedListEntry)proceedInstructionHandles.get(i)).executeAfterException;
        }
        il.append(new TABLESWITCH(idxList, eTargetList, eTargetList[0]));

        return retVal;
      }
      else {
        InstructionHandle retVal = il.append(new GOTO(((ProceedListEntry)proceedInstructionHandles.firstElement()).executeAfterProceed));
        il.append(new GOTO(((ProceedListEntry)proceedInstructionHandles.firstElement()).executeAfterException));

        return retVal;
      }
    }

    /**
     * Adapts the method bytecode in <CODE>proceedCode</CODE> in order to be
     * appended to redefined <CODE>target</CODE> and appends it.
     * <P>
     * Following adaptations are done:
     * <UL>
     * <LI>Adapt local variable slot numbers. The numbers must be increased in order to
     *     prevent conflicts with local variables of the surrounding method.</LI>
     * <LI>Replaces <CODE>RETURN</CODE> instructions with <CODE>GOTO</CODE> instructions.</LI>
     * <LI>Appends general exception handler code.</LI>
     * </UL>
     * <P>
     * @param il instruction list where the proceed body will be appended.
     * @param minSlot first available slot for 'proceed()'
     */
    private void appendProceedBody(InstructionList il, int minSlot) {
      // do this only if the code was not already appended
      if(!proceedCode.isEmpty()) {
        // Adapt method bytecode for 'proceed()'

        // first available slot is reserved for the indentifier (index)
        // of the caller location.
        InstructionHandle ih = proceedCode.getStart();

        // 1. append code for returning from the inlined method.
        //    'tswitch' is the start of the regular return routine,
        //    if the code throws an exception, it must be handled by
        //    'tswitch.getNext().getNext()'.
        InstructionHandle tswitch = appendTableSwitches(proceedCode, minSlot);

        // 2. adapt existing instructions
        while(tswitch != ih) {
          // 2.1. get instruction
          Instruction inst = ih.getInstruction();
          if (inst instanceof LocalVariableInstruction) {
            // 2.2. adapt local variable accesses (increases slot number)
            LocalVariableInstruction lv_instr = (LocalVariableInstruction) inst;
            final int oSlot = lv_instr.getIndex();
            if(0 < oSlot) {              // dont change references to 'this'
              lv_instr.setIndex(oSlot + minSlot);
            }
          }
          else if (inst instanceof ReturnInstruction) {
            // 2.3. replace RETURN (return from method) by GOTO instructions
            if(ih.getNext() == tswitch) {
              removeInstruction(ih, proceedCode, tswitch);
              break;
            }
            ih = replaceInstruction(new GOTO(tswitch), ih, proceedCode);
          }
          // 2.4. go to next instruction
          ih = ih.getNext();
        }
        // 3.   set exception handler attributes for 'target'
        // 3.1. copy original exception handlers from 'target' to the redefined 'target'.
        CodeExceptionGen[] pEx = proceedExceptionHandlers;
        for(int i = 0; i < pEx.length; i++) {
          CodeExceptionGen eh = pEx[i];
          target.addExceptionHandler(
              eh.getStartPC(),
              eh.getEndPC(),
              eh.getHandlerPC(),
              eh.getCatchType()
          );
        }
        // 3.2. set general exception handler (protecting the inlined proceed code,
        //      must be added after the individual handlers, so that they are matched
        //      first)
        if(proceedInstructionHandles.size() > 1)
          target.addExceptionHandler(proceedCode.getStart(), tswitch, tswitch.getNext().getNext(), null);
        else
          target.addExceptionHandler(proceedCode.getStart(), tswitch, tswitch.getNext(), null);
        // 4. append the code
        il.append(proceedCode);
      }
      else
        // A valid java method has at least a RETURN instruction.
        throw new JVMAIRuntimeException("attempt to create the same proceed subroutine a second time");
    }
  }

  /**
   * Sets the target method that will be inlined for 'proceed'.
   * <P>
   * <B>Note:</B> this method must be called before the target method
   * is redefined by <CODE>advice</CODE>, but redefinitions for advices with
   * lower precedence must already be done.
   * <P>
   * @param target class generator for the method that will be executed for
   *        'proceed' invokations.
   * @param targetCP constant pool generator for <CODE>target</CODE>.
   * @see ch.ethz.inf.iks.jvmai.jvmdi.HotSwapProceedCompiler#setTarget(org.apache.bcel.generic.MethodGen, org.apache.bcel.generic.ConstantPoolGen)
   */
  public void setTarget(MethodGen target, ConstantPoolGen targetCP) {
    if(1 < proceedInstructionHandles.size())
      proceedCompiler = new HotSwapMultiProceedCompiler(target, targetCP);
    else
      proceedCompiler = new HotSwapSingleProceedCompiler(target, targetCP);
  }

  /**
   * Inlines proceed routines into the redefined method.
   * <P>
   * <B>Note:</B> the advice has to be prepared with <CODE>prepareAdvice()</CODE>
   * before weaving. And the target method must be redefined with the advice.
   * <P>
   * After this weaving procees the target method is ready for class redefinition.
   * <P>
   * @throws JVMAIRuntimeException if the advice was not prepared before weaving.
   * @throws IllegalProceedUsageException if incompatible argument or return value type
   *         where defined for <CODE>proceed()</CODE>.
   */
  public void weaveRedefinedMethod() {
    if(null == proceedCompiler)
      throw new JVMAIRuntimeException("setTarget() must be called first!");

    proceedCompiler.weaveRedefinedMethod();
  }
}
TOP

Related Classes of ch.ethz.prose.jvmai.jikesrvm.advice_weaver.ProceedWeaver$HotSwapMultiProceedCompiler

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.