Package ch.ethz.inf.iks.jvmai.jvmdi

Source Code of ch.ethz.inf.iks.jvmai.jvmdi.HotSwapSimpleClassWeaver$MethodWeaver$RedefineAdviceListEntry

//
//  This file is part of the prose package.
//
//  The contents of this file are subject to the Mozilla Public License
//  Version 1.1 (the "License"); you may not use this file except in
//  compliance with the License. You may obtain a copy of the License at
//  http://www.mozilla.org/MPL/
//
//  Software distributed under the License is distributed on an "AS IS" basis,
//  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
//  for the specific language governing rights and limitations under the
//  License.
//
//  The Original Code is prose.
//
//  The Initial Developer of the Original Code is Angela Nicoara. Portions
//  created by Angela Nicoara are Copyright (C) 2004 Angela Nicoara.
//  All Rights Reserved.
//
//  Contributor(s):
//  $Id: HotSwapSimpleClassWeaver.java,v 1.2 2008/11/18 11:09:31 anicoara Exp $
//  =====================================================================
//

package ch.ethz.inf.iks.jvmai.jvmdi;

import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;

import java.util.Collections;
import java.util.Collection;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.Map;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.ArrayList;
import java.util.Iterator;

import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.File;

import org.apache.bcel.Constants;
import org.apache.bcel.Repository;
import org.apache.bcel.classfile.Attribute;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.ConstantValue;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.generic.*;
import org.apache.bcel.verifier.Verifier;
import org.apache.bcel.verifier.VerifierFactory;
import org.apache.bcel.verifier.VerificationResult;

import ch.ethz.inf.iks.jvmai.jvmdi.HotSwapSimpleClassWeaver.MethodWeaver.RedefineAdviceListEntry;
import ch.ethz.jvmai.*;

/**
*  Modifies a given class to support advice execution.
*  Container for MethodWeavers, which does the code
*  manipulations. Use the static method {@link
*  #getWeaver(Member)} to get an instance of an
{@link ch.ethz.jvmai.MethodWeaver}.
<P>
*  Simple Class Weaver implementation:
*  Holds one list with all used ClassWeavers. On
*  commit all ClassWeavers in the list
*  are checked, to know if they must be woven or not.
*
*  A ClassWeaver represents a class that will be instrumented
*  with bytecode instructions.
*
@author   Angela Nicoara
@author  Gerald Linhofer
@version $Revision: 1.2 $
*/
public class HotSwapSimpleClassWeaver {

  // status codes ClassWeaver instances
  public static final int WEAVER_STATUS_UNINITIALIZED      = 0x0;
  public static final int WEAVER_STATUS_INITIALIZED      = 0x1;
  /// Indicates modifications of the target class that were not yet woven
  public static final int WEAVER_STATUS_MODIFIED        = 0x2;
  /// Target class was modified
  public static final int WEAVER_STATUS_WOVEN          = 0x4;
  /// If <CODE>0 == status & mask</CODE>, the target class was not yet changed.
  public static final int WEAVER_UNCHANGED_MASK        = 0x6;

  /**
   * Sets <CODE>classStatus</CODE> to reflect the state of the class weaver.
   *
   * <CODE>type</CODE> replaces the <CODE>classStatus</CODE>.
   *
   * @param type type of the status.
   */
  private void setStatus(int type) {
    classStatus = type;
  }

  /**
   * Changes <CODE>classStatus</CODE> to reflect the state of the class weaver.
   *
   * The type will be either added to or removed from the <CODE>classStatus<CODE> flag.
   *
   * @param type type of the status.
   * @param flag <CODE>'true'</CODE> if the type should be set.
   */
  private void changeStatus(int type, boolean flag) {
    classStatus = flag ? (classStatus | type) : (classStatus & ~type);
  }

  /**
   * Indicates if <CODE>type</CODE> was set in <CODE>classStatus</CODE>.
   * @param type
   * @return <CODE>'true'</CODE> if the status is set
   */
  protected boolean hasStatus(int type) {
    return (classStatus & type) > 0;
  }

  /**
   *  String representation of the JVMAI class.
   */
  public static final String JVMAI_CLASS = "ch.ethz.inf.iks.jvmai.jvmdi.HotSwapAspectInterfaceImpl";

  /**
   *  Map with all class weaver. For each Class there
   *  exists exactly one class weaver in the system.
   */
  private static Map classWeavers = new LinkedHashMap(1024);    // for JDK < 1.5 (compatible with newer JVMs)
  //  private static Map<Class,HotSwapClassWeaver> classWeavers = new LinkedHashMap<Class,HotSwapClassWeaver>(1024);  // for JDK >= 1.5 (not compatible with older JVMs)

  /**
   *  Map with all method weavers. For each Method there
   *  exists exactly one method weaver in the system.
   */
  private static Map methodWeavers = new LinkedHashMap(1024);    // for JDK < 1.5 (compatible with newer JVMs)
  //  private static Map<Member,MethodWeaver> methodWeavers = new LinkedHashMap<Member,MethodWeaver>(1024);  // for JDK >= 1.5 (not compatible with older JVMs)

  /**
   *  Maps unique method ids to the method objects.
   */
  private static Member methodMap[] = new Member[1024];

  /**
   *  Manages unique method ids.
   */
  private static UniqueIdArrayManager idMgr = new UniqueIdArrayManager();

  /**
   *  The JVMAspectInterface.
   */
  private static HotSwapAspectInterfaceImpl aspectInterface = null;

  /**
   *  Is there anything to do for {@link #commit}.
   *  <CODE>true</CODE> if any Class Weaver holds uncommited
   *  modifications.
   */
  private static boolean anyModified = false;

  /**
   *  Is there anything to do for {@link #resetAll}.
   *  <CODE>true</CODE> if any class is woven.
   */
  private static boolean anyWoven = false;

  //----------------------------------------------------------------------

  /**
   *  Get a unique method weaver for 'target'.
   *
   * @param target method for which a method weaver will be returned.
   * @return MethodWeaver associated to <CODE>target</CODE>.
   */
  static public MethodWeaver getWeaver(Member target) {
    if( null == target )
      throw new NullPointerException("Parameter 'target' must not be null");

    MethodWeaver result;
    synchronized(methodWeavers) {
      // 1. get the method weaver, if it already exists
      result = (MethodWeaver) methodWeavers.get( target );
      // 2. check if there was already a weaver
      if( null == result ) {
        try {
          // 2.a get or create the ClassWeaver
          HotSwapSimpleClassWeaver cw = getClassWeaver( target.getDeclaringClass() );
          // 2.b create a new MethodWeaver
          result = cw.getNewMethodWeaver( target );
        } catch(ClassNotFoundException e) { throw new JVMAIRuntimeException("BCEL can not load " + target.getDeclaringClass()); }
      }
    }
    return result;
  }

  /**
   *  Get a unique class weaver for 'target'.
   * 
   *  @param target class for which a class weaver will be returned.
   *  @return ClassWeaver associated to <CODE>target</CODE>.
   */
  static protected HotSwapSimpleClassWeaver getClassWeaver(Class target) throws ClassNotFoundException {
    if( null == target )
      throw new NullPointerException("Parameter 'target' must not be null");

    HotSwapSimpleClassWeaver result;
    synchronized(classWeavers) {
      // 1. get the class weaver, if it already exists
      result = (HotSwapSimpleClassWeaver) classWeavers.get( target );
      // 2. check if there was already a weaver
      if( null == result ) {
        // 2.a create a new ClassWeaver
        result = new HotSwapSimpleClassWeaver( target );
        classWeavers.put( target, result );
      }
    }
    return result;
  }

  /**
   *  (Re-)Weaves all modified classes and activates them.
   */
  static public void commit() {
    if( ! anyModified )
      return;

    // 0. synchronize commit and reset
    synchronized( classWeavers ) {
      anyModified = false;
      // list of the java classes that will be woven
      Collection clazzes = new ArrayList();
      // list of the new class definitions (Bytecode), stored as Byte[]
      Collection definitions = new ArrayList();

      Iterator it = classWeavers.values().iterator();

      // generate new class definitions
      // 1. iterate through all class weavers
      while( it.hasNext() ) {
        HotSwapSimpleClassWeaver cw = (HotSwapSimpleClassWeaver) it.next();
        // 2. check if class has unwoven modifications
        if( cw.hasStatus(WEAVER_STATUS_MODIFIED) ) {
          try {
            // 2.a generate the new class definition
            //     and add it to the definitions list
            cw.prepareClassDefinition();
            definitions.add( cw.getClassDefinition() );
            clazzes.add( cw.targetClass );
            cw.setStatus( WEAVER_STATUS_INITIALIZED | WEAVER_STATUS_WOVEN );
            //System.out.println("redefining: " + cw.targetClass.toString() );
          } catch(ClassNotFoundException e) {System.err.println("ERROR HotSwapClassWeaver: Class redefinition failed: " + e);}
          catch(IOException e) {System.err.println("ERROR HotSwapClassWeaver: Class redefinition failed: " + e);}
          //          } catch ( Exception e) {
          //            // System.out.println(e);
          //            // e.printStackTrace();
          //            throw new RuntimeException( e.getMessage() );
          //          }
        }
      }

      // 3. activate new class definitions, if any.
      if( ! clazzes.isEmpty() ) {
        anyWoven = true;
        // 3.a convert list of classes and class definitions
        //     to arrays.
        Class cls[] = new Class[ clazzes.size() ];
        clazzes.toArray( cls );
        byte defs[][] = new byte[definitions.size()][];
        definitions.toArray( defs );
        // 3.b redefine classes.
        //        try {
        redefineClasses( cls, defs );

        /*DELETE-ANGY-forTest
        try {
              FileOutputStream os = new FileOutputStream("RedefinedClass.class");
          os.write(defs[defs.length - 1]);
          os.flush();
          os.close();
          System.err.println("***Classfile writen to: RedefinedClass.class");
        } catch(IOException ie){}
        DELETE-ANGY-forTest*/


        //        } catch(ch.ethz.jvmai.IlligalClassFormatException e){
        //          try {
        //          FileOutputStream os = new FileOutputStream("RedefinedClass.class");
        //          os.write(defs[defs.length - 1]);
        //          os.flush();
        //          os.close();
        //          System.err.println("***Classfile writen to: RedefinedClass.class");
        //          } catch(IOException ie){}
        //          throw new JVMAIRuntimeException(e.toString());
        //        }
      }
    } // end of synchronized
  }

  /**
   *  Resets all woven classes.
   */
  static public void resetAll() {
    if( ! anyWoven )
      return;

    // 0. synchronize commit() and resetAll()
    synchronized( classWeavers ) {
      // list of java classes that will be unwoven
      Collection clazzes = new ArrayList();
      // list of Byte[] holding the original class
      // definitions of the classes
      Collection definitions = new ArrayList();

      Iterator it = classWeavers.values().iterator();

      // 1. get classes and old class definitions for woven classes
      while( it.hasNext() ) {
        // 1.a get class weaver
        HotSwapSimpleClassWeaver cw = (HotSwapSimpleClassWeaver) it.next();
        // 1.b check if class was woven
        if( cw.hasStatus(WEAVER_STATUS_WOVEN) && null != cw.originalCode ) {
          // add class and class definition to the lists
          // so the will be redefined
          clazzes.add( cw.targetClass );
          definitions.add( cw.originalCode );
        }
        // 1.c reset class weavers status
        cw.reset();
      }

      // 2. reset class definitions, if any
      if( ! clazzes.isEmpty() ) {
        // 2.a convert lists to array
        Class cls[] = new Class[ clazzes.size() ];
        clazzes.toArray( cls );
        byte defs[][] = new byte[ definitions.size() ][];
        definitions.toArray( defs );
        // 2.b activate the original class definitions
        redefineClasses( cls, defs );
      }

      // 3. clean up
      classWeavers.clear();
      methodWeavers.clear();
      methodMap = new Member[1024];
      idMgr.reset();
      anyModified = false;
      anyWoven = false;
    }
  }

  /**
   * Returns an identifier for <CODE>method</CODE> and
   * stores the association, so that {@link #idToMethod(int) idToMethod}
   * may be used to get the <CODE>method</CODE> using the id.
   *
   * Note: only the id is unique, if <CODE>createNewMethodId(Method)
   * </CODE> is called more than one time with the same argument,
   * each call will create a new id and a new map entry.
   *
   * @param method that will be associated with a new identifier.
   * @return new identifier for <CODE>method</CODE>.
   */ 
  private static int createNewMethodId( Member method ) {
    if( null == method )
      throw new NullPointerException();

    int result;

    synchronized( methodMap ) {
      result = idMgr.newId();
      // check if map must be expanded
      int mapLength = methodMap.length;
      if( mapLength <= result ) {
        Member newMap[] = new Member[ 2 * mapLength ];
        System.arraycopy( methodMap, 0, newMap, 0, mapLength );
        methodMap = newMap;
      }
      // add method to the map
      methodMap[result] = method;
    }
    return result;
  }

  /**
   * Returns the Member object associated to <CODE>methodId</CODE>
   * or <CODE>null</CODE>, if <CODE>methodID</CODE> is not a valid
   * id.
   *
   * @param methodId id for the Method that will be returned.
   * @return Method associated to  <CODE>methodID<CODE> or <CODE>null</CODE>.
   * @throws ArrayOutOfBoundException
   */ 
  public static Member idToMethod( int methodId ) {
    return methodMap[methodId];
  }

  /**
   * Sets the aspect interface, must be called before any call to
   * {@link #commit commit}.
   *
   * @param ai
   */
  public synchronized static void setAspectInterface( HotSwapAspectInterfaceImpl ai ) {
    aspectInterface = ai;
  }


  //---------------------------------------------------------------------

  /**
   *  Class bound to this weaver
   */
  public final Class targetClass;

  /**
   *  Cached class file of {@link #targetClass}
   */
  public final byte[] originalCode;

  /**
   * Status of this ClassWeaver
   */
  private int classStatus;

  /**
   *  BCEL class generator used during weaving processs.
   *  holds the actual class definition during weaving process.
   */
  private ClassGen clGen;

  /**
   *  BCEL constant pool generator used during weaving process.
   */
  private ConstantPoolGen cpGen;

  /**
   *  BCEL instruction factory used during the weaving process.
   */ 
  private InstructionFactory instructionFactory;

  /**
   *  Collection of member MethodWeavers (of {@link #targetClass})
   *  that are or will be redefined.
   */
  private Map methods;              // for JDK < 1.5 (compatible with newer JVMs)
  // private Map<Member,MethodWeaver> methods;  // for JDK >= 1.5 (not compatible with older JVMs)

  // Used during weaving (introduced for verfyClassSchema)
  private JavaClass bcelClass;

  //-----------------------------------------------------------------------

  /**
   *  Creates a new class weaver. Use the static method
   *  {@link #getClassWeaver(java.lang.Class) getClassWeaver} to obtain a weaver.
   * 
   *  @param target class that will be woven
   */
  protected HotSwapSimpleClassWeaver(Class target) throws ClassNotFoundException {
    targetClass = target;
    methods = new HashMap();

    bcelClass = HotSwapAspectInterfaceImpl.getBCELClassDefinition( targetClass );
    originalCode = bcelClass.getBytes();

    classStatus = WEAVER_STATUS_UNINITIALIZED;
    clGen = null;
    cpGen = null;
  }

  public Class getTargetClass() {
    return targetClass;
  }

  public Collection getMethodWeavers() {
    return methods.values();
  }

  /**
   *  Creates a new <CODE>MethodWeaver</CODE> and adds it to
   *  this weaver.
   *  This adds the new weaver also to the global methodWeavers
   *  table.
   *  Use {@link HotSwapSimpleClassWeaver#getWeaver(java.lang.reflect.Member) getWeaver} to create a new MethodWeaver.
   * 
   *  @param target Method that will be woven
   *  @return MethodWeaver for the <CODE>target</CODE> method.
   */
  protected MethodWeaver getNewMethodWeaver( Member target ) {
    MethodWeaver result = new MethodWeaver( target );
    // add weaver to the global method weaver map
    methodWeavers.put( target, result );
    // add weaver to the local (class wide) method weaver map
    methods.put( target, result );

    return result;
  }

  /**
   *  Prepares a class for weaving. This mean mainly
   *  instrumenting the class file representation,
   *  so that the new definition may be fetched with
   *  {@link #getClassDefinition()}.
   * 
   *  @throws java.io.IOException could not read class file
   *  @throws ClassNotFoundException could not find class file
   */
  protected void prepareClassDefinition() throws java.io.IOException, ClassNotFoundException {
    // 1. initialize the class weaver for weaving
    initPreparation();
    // 2. generate instrumented versions of modified methods
    weaveMethods();
    // 2.a verify the instrumented class definition
    // verify();
    // verifyClassSchema();
    // 3. reset some flags
    finishWeaving();
  }

  /**
   *  Prepare for weaving. Initializes some member field.
   *  This method must only be called once (for each instance
   *  of this class).
   *  called by {@link #prepareClassDefinition()}.
   * 
   *  @throws java.io.IOException could not read class file
   */
  private void initPreparation() throws java.io.IOException {
    if( hasStatus(WEAVER_STATUS_INITIALIZED) ) {
      // weaver was already used, so reset bcelClass to its original state
      ClassParser classParser = new ClassParser( new ByteArrayInputStream(originalCode), bcelClass.getFileName());
      bcelClass = classParser.parse();
    }

    // create bcel generator objects
    clGen = new ClassGen( bcelClass );
    cpGen = clGen.getConstantPool();
    instructionFactory = new InstructionFactory( clGen, cpGen );
  }

  /**
   *  Resets some member fields to the values they should have
   *  after weaving.
   *  Called by {@link #prepareClassDefinition()}-
   */
  private void finishWeaving() {
    // set the new class status
    setStatus( WEAVER_STATUS_INITIALIZED | WEAVER_STATUS_WOVEN );
    // set bcel generators to null to allow gabage collections for them
    bcelClass = clGen.getJavaClass();
    cpGen = null;
    clGen = null;
    instructionFactory = null;
  }

  /**
   *  Generate instrumented versions of the member methods
   *  of {@link #targetClass}.
   *  called by {@link #prepareClassDefinition()}.
   */
  private void weaveMethods() {
    Iterator iter = methods.values().iterator();
    while( iter.hasNext() ) {
      MethodWeaver mw = (MethodWeaver) iter.next();
      mw.weaveMethod();
    }
  }

  /**
   *  Returns the instrumented class file.
   *  {@link #prepareClassDefinition()} must be called first.
   */
  protected byte[] getClassDefinition() {
    //System.out.println("HotSwapClassWeaver.getClassDefinition() for " + targetClass.toString() );
    if( ! hasStatus(WEAVER_STATUS_INITIALIZED) )
      throw new RuntimeException("ClassWeaver not initialized " + classStatus);
    return bcelClass.getBytes();
  }

  /**
   *  Resets the class weavers status to its initial state.
   *  All <CODE>MethodWeavers</CODE> will be removed.
   *  <P>
   <CODE>classStatus</CODE> will be set to initialized!
   *
   */
  private void reset() {
    classStatus &= WEAVER_STATUS_INITIALIZED;

    Iterator it = methods.values().iterator();
    while( it.hasNext() ) {
      MethodWeaver mw = (MethodWeaver) it.next();
      methodWeavers.remove(mw.target);
    }
    methods.clear();
  }

  /**
   * Verifies if the new class definition doesn't change anything,
   * which RedefineClasses can not handle (anything except
   * changing method bodies).
   *
   * @return <CODE>false</CODE> if any incompabilities betwen
   *         the new and the old class definitions where found,
   *         otherwise <CODE>true</CODE>.
   */
  private boolean verifyClassSchema() {
    boolean retval = true;

    if( null == clGen || null == bcelClass )
      throw new RuntimeException( "nothing to verify" );

    //System.out.println( "Verifying " + bcelClass.getClassName() );

    // Get classes 
    ClassParser cp = new ClassParser( new ByteArrayInputStream(originalCode), targetClass.getName().replace( '.', '/') );
    JavaClass originalClass;
    try {
      originalClass = cp.parse();
    }
    catch (java.io.IOException e) {
      throw new RuntimeException( e.getMessage() );
    }
    JavaClass newClass = clGen.getJavaClass();

    // Check class name
    if( ! originalClass.getClassName().equals( newClass.getClassName() ) ) {
      System.out.println( "Class name changed ( old: " + originalClass.getClassName() + ", new: " + newClass.getClassName() + ")" );
      retval = false;
    }
    // Check class modifiers (access flags)
    if( originalClass.getAccessFlags() != newClass.getAccessFlags() ) {
      System.out.println( "Class modifiers changed (old: " + originalClass.getAccessFlags() + ", new: " + newClass.getAccessFlags() + ")" );
      retval = false;
    }

    // Methods
    org.apache.bcel.classfile.Method oldMethods[] = originalClass.getMethods();
    org.apache.bcel.classfile.Method newMethods[] = newClass.getMethods();
    int oldMethodsLength = oldMethods.length;
    // number of methods
    if( oldMethodsLength != newMethods.length ) {
      System.out.println( "Number of methods changed (old: " + oldMethodsLength + ", new: " + newMethods.length + ")" );
      retval = false;
    }
    else {
      for( int i = 0; i < oldMethodsLength; i++ ) {
        org.apache.bcel.classfile.Method oldMeth = oldMethods[i];
        org.apache.bcel.classfile.Method newMeth = newMethods[i];

        // method name
        String oldMName = oldMeth.getName();
        if( ! oldMName.equals( newMeth.getName() ) ) {
          System.out.println( "Method name changed (old: " + oldMName + ", new: " + newMeth.getName() + ")" );
          retval = false;
        }
        // method modifiers
        if( oldMeth.getAccessFlags() != newMeth.getAccessFlags() ) {
          System.out.println( "Method modifiers changed for" + oldMName );
          retval = false;
        }
        // signature
        if( ! oldMeth.getSignature().equals( newMeth.getSignature() ) ) {
          System.out.println( "Method signature changed for " + oldMName );
          retval = false;
        }
      }
    }

    // Check for JVMDI_ERROR_SCHEMA_CHANGE... (Fields)
    org.apache.bcel.classfile.Field newFields[] = newClass.getFields();
    org.apache.bcel.classfile.Field oldFields[] = originalClass.getFields();
    int newFieldsLength = newFields.length;
    if( newFieldsLength != oldFields.length ) {
      System.out.println( "Number of fields changed (old " + oldFields.length + ", new " + newFieldsLength + ")" );
      retval = false;
    }
    else {
      for( int i = 0; i < newFieldsLength; i++ ) {
        org.apache.bcel.classfile.Field nf = newFields[i];
        org.apache.bcel.classfile.Field of = oldFields[i];
        String oldName = of.getName();

        // Name       
        if( ! nf.getName().equals( oldName ) ) {
          System.out.println( "Field name changed ( new: " + nf.getName() + ", old: " + oldName + ")" );
          retval = false;
        }
        // Access flags
        if( nf.getAccessFlags() != of.getAccessFlags() ) {
          System.out.println( "Field access flags changed for " + oldName + "( old: " + of.getAccessFlags() + ", new: " + nf.getAccessFlags() + ")" );
          retval = false;
        }
        // Signature
        if( ! nf.getSignature().equals( of.getSignature() ) ) {
          System.out.println( "Field signature changed for " + oldName + "( old: " + of.getSignature() + ", new: " + nf.getSignature() + ")" );
          retval = false;
        }
        // Constant value (may be 'null')
        ConstantValue oldConst = of.getConstantValue();
        ConstantValue newConst = nf.getConstantValue();
        if( oldConst != newConst ) {
          if( null == oldConst ) {
            System.out.println( "Changed constant field to modifiable field " + oldName );
            retval = false;
          }
          else if( null == newConst ) {
            System.out.println( "Changed modifiable field to constant field " + oldName );
            retval = false;
          }
          else if( ! oldConst.toString().equals( newConst.toString() ) ) {
            System.out.println( "Changed " + oldConst.toString() + " to " + newConst.toString() );
            retval = false;
          }
        }
        // Attributes
        Attribute newAttributes[] = nf.getAttributes();
        Attribute oldAttributes[] = of.getAttributes();
        int oldAttributesLength = oldAttributes.length;
        if( oldAttributesLength != newAttributes.length ) {
          System.out.println( "Number of field attributes changend (old: " + oldAttributesLength + " new: " + newAttributes.length + ")" );
          retval = false;
        }
        else {
          for( int j = 0; j < oldAttributesLength; j++ ) {
            Attribute oldAttr = oldAttributes[j];
            Attribute newAttr = newAttributes[j];
          }
        }
      }
    }
    return retval;
  }

  /**
   * Verify bytecodes with the BCEL verifier.
   * <p>
   * NOTE: Can't be used since even `normal' classes are rejected with Jikes
   * RVM 2.3.0.1.
   *
   */
  private void verify() {
    if( null == clGen )
      throw new RuntimeException( "nothing to verify" );

    JavaClass jc = clGen.getJavaClass();
    Repository.addClass( jc );

    Verifier v = VerifierFactory.getVerifier( jc.getClassName() );
    checkVerificationResult(v.doPass1(), "1");
    checkVerificationResult(v.doPass2(), "2");
    MethodWeaver methodWeavers[] = new MethodWeaver[methods.size()];
    methods.values().toArray( methodWeavers );
    for( int i = 0; i < methods.size(); i++ ) {
      int method_index = methodWeavers[i].targetIndex;
      checkVerificationResult(v.doPass3a(method_index), "3a");
      checkVerificationResult(v.doPass3b(method_index), "3b");
    }

    try {
      String[] warnings = v.getMessages();
      if (warnings.length != 0)
        System.err.println("Messages:");
      for (int j = 0; j < warnings.length; j++)
        System.err.println(warnings[j]);
    } catch(Exception e) { System.err.println("could not verify " + targetClass); }
  }

  /**
   * Check bytecode verification result.
   *
   * @param vr verification result which must be checked
   * @param pass verification pass
   */
  private void checkVerificationResult(VerificationResult vr, String pass) {
    if (vr != VerificationResult.VR_OK) {
      System.err.println("Verification failed in pass " + pass + " for " + targetClass + ".");
      System.err.println(vr);
    }
  }

  /**
   * Redefines existing classes, actually only method bodies may be redefined,
   * but the JVMDI and JVMTI functions requieres the whole class code. The parameters
   * are two arrays of the same size, each entry of one array corresponds to the
   * entry with the same index in the other array.
   * The class that should be redefined must exist in the JVM (they must be loaded
   * in advance).
   *
   * Required for JVMDI/JVMTI HotSwap advice weaving.
   *
   * @param cls Array of Class objects that should get redefined
   * @param definitions Array of class definitions, which are arrays of bytes,
   *        the definition is in the same form like in a class file.
   * @exception UnmodifiableClassException <CODE>cls</CODE> contains a not modifiable
   *        class (for example an interface or a class that's not in the class path).
   * @exception IlligalClassFormatException <CODE>definitions</CODE> contains a invalide
   *        class definition
   * @exception RuntimeException could not apply operation.
   * @exception NullPointerException if <CODE>cls</CODE> is <CODE>null</CODE>.
   * @exception IlligalArgumentExceptin <CODE>definitions</CODE> is <CODE>null</CODE>.
   */
  public static void redefineClasses( Class[] cls, byte[][] definitions ) {
    try{
      HotSwapClassWeaver.redefineClasses( cls, definitions );
    }catch(ch.ethz.jvmai.IlligalClassFormatException e){
      for(int i=0; i<cls.length; i++)
        System.out.println("--->Class: " + cls[i]);
      throw e;
    }
  }


  /**
   * Redefines an existing class, actually only method bodies may be redefined,
   * but the JVMDI and JVMTI functions requieres the whole class code.
   * The class that should be redefined must exist in the JVM (they must be loaded
   * in advance).
   *
   * Required for JVMDI/JVMTI HotSwap advice weaving.
   *
   * @param cl Class objects that should get redefined
   * @param definition class definition,
   *        the definition is in the same form like in a class file.
   * @exception UnmodifiableClassException <CODE>cl</CODE> is a not modifiable
   *        class (for example an interface or a class that's not in the class path).
   * @exception IlligalClassFormatException <CODE>definition</CODE> is a invalide
   *        class definition
   * @exception RuntimeException could not apply operation.
   * @exception NullPointerException if <CODE>cl</CODE> is <CODE>null</CODE>.
   * @exception IlligalArgumentExceptin <CODE>definition</CODE> is <CODE>null</CODE>.
   */
  public static void redefineClass( Class cl, byte[] definition ) {
    HotSwapClassWeaver.redefineClass( cl, definition );
  }


  //----------------------------------------------------------------------

  /**
   *  Implementation of MethodWeaver. Collects redefinition
   *  requests and weaves them into the methods bytecode.
   *
   *  Based on MethodWeaver implementation by Johann Gyger.
   *
   *  @see ch.ethz.jvmai.MethodWeaver
   */
  protected class MethodWeaver implements ch.ethz.jvmai.MethodWeaver  {

    /**
     *  Method bound to this weaver.
     */
    public final Member target;

    /**
     *  Method identifier for {@link #target}.
     *  Generated with {@link HotSwapSimpleClassWeaver#createNewMethodId ClassWeaver.createNewMethodId}.
     */
    public final int targetId;

    /**
     *  Method index (in the array of declared methods of the declaring
     *  class {@link Class#getDeclaredMethods()}) or -1 if not initialized.
     */
    private int targetIndex;

    /**
     * Indicates how the method should be modified.
     * see {@link ch.ethz.jvmai.MethodWeaver}
     */
    private int status;

    /**
     *  Ordered list holding redefine advice methods, if any.
     *  New redefinine advices are put at the end of the list.
     *  <P>
     *  The list will hold entries of type <CODE>RedefineAdviceListEntry</CODE>
     */
    private LinkedList redefineAdvices;                // for JDK < 1.5 (compatible with newer JVMs)
    // private LinkedList<RedefineAdviceListEntry> redefineAdvices; // for JDK >= 1.5 (not compatible with older JVMs)

    /*
     * RedefineAdviceListEntry is a container to store
     * a redefine advice together with its crosscut object
     * in a list entry.
     */
    protected class RedefineAdviceListEntry implements Comparable {
      final Method advice;
      final Object cut;
      final int priority;

      RedefineAdviceListEntry(Method advice, Object cut, int priority){
        this.advice = advice;
        this.cut = cut;
        this.priority = priority;
      }

      public int compareTo(Object o) {
        return ((RedefineAdviceListEntry)o).priority - priority;
      }

      public boolean equals(Object obj) {
        return cut.equals(obj) ||
        (obj instanceof RedefineAdviceListEntry) &&
        cut.equals(((RedefineAdviceListEntry)obj).cut);
      }

      public String toString() {
        return "RedefineAdviceListEntry for " + cut.toString() + " @" + target;
      }
    };


    //    /**
    //     *  Watched method calls (not implemented).
    //     */
    //    private Map methodCalls;
    //   
    //    /**
    //     *  Watched method returns (not implemented).
    //     */
    //    private Map methodReturns;

    /**
     *  Watched field accesses
     */
    private Map fieldAccessors;              // for JDK < 1.5 (compatible with newer JVMs)
    // private Map<String,HotSwapFieldWeaver> fieldAccessors;  // for JDK >= 1.5 (not compatible with older JVMs)

    /**
     *  Watched field modifications
     */
    private Map fieldModifiers;              // for JDK < 1.5 (compatible with newer JVMs)
    // private Map<String,HotSwapFieldWeaver> fieldModifiers;  // for JDK >= 1.5 (not compatible with older JVMs)

    /**
     *  BCELs method generator, used during weaving.
     */
    private MethodGen methodGen;

    /**
     *  BCEL instructions, used during weaving
     *  process (normalwise set to 'null').
     */
    private InstructionList instructions;

    /**
     * Create a new method weaver. Use the static method
     * {@link HotSwapSimpleClassWeaver#getWeaver(java.lang.reflect.Member) ClassWeaver.getWeaver}to obtain a weaver.
     *
     * @param target method that will be woven
     */
    public MethodWeaver( Member target ) {
      this.target     = target;

      targetId       = createNewMethodId( target );
      targetIndex     = -1;
      methodGen       = null;
      instructions     = null;

      //methodCalls     = Collections.synchronizedMap(new LinkedHashMap());
      //methodReturns   = Collections.synchronizedMap(new LinkedHashMap());
      fieldAccessors     = Collections.synchronizedMap(new LinkedHashMap());    // for JDK < 1.5 (compatible with newer JVMs)
      //fieldAccessors   = Collections.synchronizedMap(new LinkedHashMap<String,HotSwapFieldWeaver>());  // for JDK >= 1.5 (not compatible with older JVMs)
      fieldModifiers     = Collections.synchronizedMap(new LinkedHashMap());    // for JDK < 1.5 (compatible with newer JVMs)
      //fieldModifiers   = Collections.synchronizedMap(new LinkedHashMap<String,HotSwapFieldWeaver>());  // for JDK >= 1.5 (not compatible with older JVMs)
      redefineAdvices    = new LinkedList();                    // for JDK < 1.5 (compatible with newer JVMs)
      //redefineAdvices  = new LinkedList<RedefineAdviceListEntry>();      // for JDK >= 1.5 (not compatible with older JVMs)

      status = MWEAVER_STATUS_UNCHANGED;
    }

    /**
     * Changes the status field to reflect that an watch
     * of type <code>'type'</code> is set.
     *
     * @param type type of the watch (see ch.ethz.jvmai.MethodWeaver).
     */
    private void addWatch(int type) {
      status |= type;
      setModified();
    }

    /**
     * Changes the status field to reflect that an watch
     * of type <code>'type'</code> has to be removed.
     *
     * @param type type of the watch (see ch.ethz.jvmai.MethodWeaver).
     */
    private void removeWatch(int type) {
      status &= ~type;
      setModified();
    }

    /**
     * Changes the status field to either add or remove a watch.
     *
     * @param type type of the watch (see ch.ethz.jvmai.MethodWeaver).
     * @param flag <code>'true'</code> to add the watch, false to remove it.
     */
    private void setWatch(int type, boolean flag) {
      status = flag ? (status | type)
          : (status & ~type);
      setModified();
    }

    /**
     * Indicates if a watch is set or not.
     *
     * @param type type of the watch (see ch.ethz.jvmai.MethodWeaver).
     * @return <CODE>'true'</CODE> if the watch is set.
     */
    public boolean hasWatch(int type) {
      return (status & type) > 0;
    }

    /**
     * Returns the method bound to this weaver.
     * @return Method bound to this weaver.
     */
    public Member getTarget() {
      return target;
    }

    /**
     * Returns the identifier of the method bound to this weaver.
     * @return identifier of the method bound to this weaver
     */
    public int getTargetId() {
      return targetId;
    }

    /**
     * Returns the status of this weaver. The codes are defined
     * in {@link ch.ethz.jvmai.MethodWeaver MethodWeaver}.
     *
     * @return int status of the weaver
     */
    public int getStatus() {
      return status;
    }

    /**
     * Redefine the method in this weaver.
     *
     * @param advice method that will be called instead
     *               or null to reset a redefined method.
     * @param cut unique identifier for the crosscut.
     * @param priority priority of the aspect holding <CODE>cut</CODE>
     */
    public void setRedefineAdvice(Method advice, Object cut, int priority) {
      if(null != advice) {
        RedefineAdviceListEntry newEntry = new RedefineAdviceListEntry(advice, cut, priority);
        addRedefineAdvice(newEntry);
      }
      else if(!redefineAdvices.isEmpty()) {
        removeRedefineAdvice(cut);
      }
      setModified();
    }

    /**
     * Adds a new method redefinition advice to <CODE>redefineAdvices</CODE>.
     * <P>
     * NOTE: this does not invoke <CODE>setModified()</CODE>.
     *
     * @param newEntry new entry to insert into <CODE>redefineAdvices</CODE>
     */
    private void addRedefineAdvice(RedefineAdviceListEntry newEntry) {
      // find the right position for insertion
      ListIterator iter = redefineAdvices.listIterator();
      while(iter.hasNext()) {
        if(newEntry.priority <= ((RedefineAdviceListEntry)iter.next()).priority) {
          // a. found the position where newEntry will be inserted
          redefineAdvices.add(iter.previousIndex(), newEntry);
          return;
        }
      }
      // b. the list is empty or all entries have a lower priority number:
      //        append the new entry to the list.
      redefineAdvices.addLast(newEntry);
    }

    /**
     * Removes an entry from <CODE>redefineAdvices</CODE>.
     * <P>
     * NOTE: this does not invoke <CODE>setModified()</CODE>.
     *
     * @param cut crosscut object that will be removed
     * @return <CODE>true</CODE> if an entry for <CODE>cut</CODE> was found
     *       and removed from <CODE>redefineAdvices</CODE>
     */
    private boolean removeRedefineAdvice(Object cut) {
      // find the entry for 'cut'
      for(
          ListIterator iter = redefineAdvices.listIterator();
          iter.hasNext();
      ) {
        RedefineAdviceListEntry e = (RedefineAdviceListEntry) iter.next();
        if(cut == e.cut) {
          // remove the entry for cut
          iter.remove();
          return true// remove only the first occurence (assume that there's only one).
        }
      }

      return false;
    }

    /**
     * Enable method entry join point.
     *
     * @param flag enable/disable
     */
    public void setMethodEntryEnabled(boolean flag) {
      setWatch( MWEAVER_STATUS_ENTRY_ENABLED, flag);
    }

    /**
     * Enable method entry join point.
     *
     * @param flag enable/disable
     */
    public void setMethodExitEnabled(boolean flag) {
      setWatch( MWEAVER_STATUS_EXIT_ENABLED, flag);
    }

    /**
     * Add a method for which a callback should be woven (before each call).
     * <P>
     * Not Implemented, this will always throw a <CODE>NotImplementedException</CODE>.
     *
     * @param m watched method
     */
    public void addMethodCall(Method m) {
      throw new JVMAIRuntimeException("Not implemented");
      //      String key = m.getDeclaringClass().getName() + "#" + m.getName();
      //      if( ! methodCalls.containsKey( key ) ) {
      //        methodCalls.put( key, m );
      //        newMethodCalls.add( key );
      //        addWatch(MWEAVER_STATUS_HAS_METHOD_CALL_WATCH)
      //      }
    }

    /**
     * Remove a method for which a callback was woven (before each call).
     * <P>
     * Not Implemented, this will always throw a <CODE>NotImplementedException</CODE>.
     *
     * @param m watched method
     */
    public void removeMethodCall(Method m) {
      throw new JVMAIRuntimeException("Not implemented");
      //      String key = m.getDeclaringClass().getName() + "#" + m.getName();
      //      if( methodCalls.containsKey( key ) ) {
      //        methodCalls.remove( key );
      //        newMethodCalls.remove( key );    // just in case it was not yet woven
      //        if( methodCalls.isEmpty() )
      //          removeWatch(MWEAVER_STATUS_HAS_METHOD_CALL_WATCH);
      //        else
      //          setModified();
      //      }
    }

    /**
     * Add a method for which a callback should be woven (after each call).
     * <P>
     * Not Implemented, this will always throw a <CODE>NotImplementedException</CODE>.
     *
     * @param m watched method
     */
    public void addMethodReturn(Method m) {
      throw new JVMAIRuntimeException("Not implemented");
      //      String key = m.getDeclaringClass().getName() + "#" + m.getName();
      //      if( ! methodReturns.containsKey( key ) ) {
      //        methodReturns.put( key, m );
      //        newMethodReturns.add( key );
      //        addWatch(MWEAVER_STATUS_HAS_METHOD_RETURN_WATCH)
      //      }
    }

    /**
     * Remove a method for which a callback was woven (after each call).
     * <P>
     * Not Implemented, this will always throw a <CODE>NotImplementedException</CODE>.
     *
     * @param m watched method
     */
    public void removeMethodReturn(Method m) {
      throw new JVMAIRuntimeException("Not implemented");
      //      String key = m.getDeclaringClass().getName() + "#" + m.getName();
      //      if( methodReturns.containsKey( key ) ) {
      //        methodReturns.remove( key );
      //        newMethodReturns.remove( key );    // just in case it was not yet woven
      //        if( methodReturns.isEmpty() )
      //          removeWatch(MWEAVER_STATUS_HAS_METHOD_RETURN_WATCH);
      //        else
      //          setModified();
      //      }
    }

    /**
     * Add a field for which a callback should be woven (for each access).
     *
     * @param fw weaver for the watched field
     */
    public void addFieldAccessor(HotSwapFieldWeaver fw) {
      String f = fw.getKey();

      if( ! fieldAccessors.containsKey( f ) ) {
        fieldAccessors.put( f, fw );
        setModified();
      }
    }

    /**
     * Remove a field for which a callback was woven (for each access).
     *
     * @param fw weaver for the watched field
     */
    public void removeFieldAccessor(HotSwapFieldWeaver fw) {
      String f = fw.getKey();

      if( fieldAccessors.containsKey( f ) ) {
        fieldAccessors.remove( f );
        setModified();
      }
    }

    /**
     * Add a field for which a callback should be woven (for each modification).
     *
     * @param fw weaver for the watched field
     */
    public void addFieldModifier(HotSwapFieldWeaver fw) {
      String f = fw.getKey();

      if( ! fieldModifiers.containsKey( f ) ) {
        fieldModifiers.put( f, fw );
        setModified();
      }
    }

    /**
     * Remove a field for which a callback was woven (for each modification).
     *
     * @param fw weaver for the watched field
     */
    public void removeFieldModifier(HotSwapFieldWeaver fw) {
      String f = fw.getKey();

      if( fieldModifiers.containsKey( f ) ) {
        fieldModifiers.remove( f );
        setModified();
      }
    }

    /**
     * Weave all registered field accesses/modifications.
     */
    private void weaveFieldAdvice() {
      // System.out.println( "HotSwapClassWeaver.weaveFieldAdvice() for " + target.toString() );
      // 1. Iterate through all instructions in methods body
      for (InstructionHandle h = instructions.getStart(); h != null; h = h.getNext()) {
        Instruction instr = h.getInstruction();

        // 2. look for field instructions (get or put)
        if (instr instanceof FieldInstruction) {
          FieldInstruction fi = (FieldInstruction) instr;
          String key = fi.getReferenceType(cpGen).toString() + "#" + fi.getName(cpGen) + "#" + fi.getSignature(cpGen);
          if ((instr instanceof GETSTATIC || instr instanceof GETFIELD) ) {
            // Field Access
            HotSwapFieldWeaver fieldWeaver = (HotSwapFieldWeaver) fieldAccessors.get(key);
            // 3. check if this is a watched field access
            if(null != fieldWeaver) {
              //System.out.println("MethodWeaver.weaveFieldAdvice(): " + target + " GET " + key);

              // 4. weave the callback function call
              instructions.insert(h, createFieldAccessAdviceCallback(fieldWeaver));
            }
          } else if ((instr instanceof PUTSTATIC || instr instanceof PUTFIELD) ) {
            // Field modification
            HotSwapFieldWeaver fieldWeaver = (HotSwapFieldWeaver) fieldModifiers.get(key);
            // 3. check if this is a watched field modification
            if( null != fieldWeaver ) {
              // Store new value to a local variable
              Type field_type = fi.getFieldType(cpGen);
              // The use of the LocalVariableGen is faster
              // than just using max local + 1 (Dont no why, but meassurements showed it.
              int local_index = methodGen.getMaxLocals() + 1;
              instructions.insert(h, InstructionFactory.createStore(field_type, local_index));

              //System.out.println("MethodWeaver.weaveFieldAdvice(): " + target + " PUT " + field);
              // weaver the callback function call
              instructions.insert(h, createFieldModificationAdviceCallback(fieldWeaver, local_index));
              // Load new value
              instructions.insert(h, InstructionFactory.createLoad(field_type, local_index));
              methodGen.setMaxLocals(local_index + field_type.getSize());
            }
          }
        }
      }
    }

    /**
     * Initiates weaving of method redefinition advices.
     *
     */
    private void weaveRedefineAdvice() {
      //System.out.println("MethodWeaver.weaveRedefineAdvice(): " + target);
      if(!redefineAdvices.isEmpty()) {
        Iterator nextAdvices = redefineAdvices.iterator();
        weaveRedefineAdvice((RedefineAdviceListEntry)nextAdvices.next(), nextAdvices);
      }
    }

    /**
     * Weaves a method redefinition advice.
     * 
     * @param entry advice to be woven
     * @param nextAdvices other advices with lower precedence, which will be executed
     *        for 'proceed' statements.
     */
    private void weaveRedefineAdvice(RedefineAdviceListEntry entry, Iterator nextAdvices) {
      //System.out.println("MethodWeaver.weaveRedefineAdvice(" + entry.advice + "): " + target);

      // 1. create BCEL generators
      // 1.1. generators for actual advice
      // 1.1.1. class generator
      Class actualAdviceClass = entry.advice.getDeclaringClass();
      ClassGen actualAdviceClassGen;
      try { actualAdviceClassGen = new ClassGen(HotSwapAspectInterfaceImpl.getBCELClassDefinition(actualAdviceClass)); }
      catch(ClassNotFoundException e) { throw new JVMAIRuntimeException("MethodWeaver.weaveRedefineAdvice(): can not read class file " + actualAdviceClass.getName()); }
      // 1.1.2. constant pool generator
      ConstantPoolGen actualAdviceCPGen = actualAdviceClassGen.getConstantPool();
      // 1.1.3. method generator
      org.apache.bcel.classfile.Method[] aMethods = actualAdviceClassGen.getMethods();
      MethodGen actualAdviceMethodGen = null;
      for( int i = 0; i < aMethods.length; i++ )
        if( "METHOD_ARGS".equals( aMethods[i].getName() ) ) {
          actualAdviceMethodGen = new MethodGen( aMethods[i], actualAdviceClass.getName(), actualAdviceCPGen );
          break;
        }
      if( null == actualAdviceMethodGen )
        throw new JVMAIRuntimeException( "can not find method " + actualAdviceClass.getName() + ".METHOD_ARGS()");

      // 2. method redefinition
      HotSwapProceedWeaver pweaver = new HotSwapProceedWeaver(actualAdviceMethodGen, actualAdviceCPGen, methodGen);
      if(pweaver.prepareAdvice()) {
        // 2.a. 'proceed' statement in actual advice
        //      proceed-weaving required
        // nextAdvice might be either the next advice in the list or it's the target
        if(nextAdvices.hasNext()) {
          // 2.a.a. nextAdvice is an advice method, which need to be prepared first
          RedefineAdviceListEntry nextAdvice = (RedefineAdviceListEntry)nextAdvices.next();
          // prepare nextAdvice (RECURSION!)
          weaveRedefineAdvice(nextAdvice, nextAdvices);
        }
        // 2.a.1. save the original code of 'target' before it is redefined.
        pweaver.setTarget(methodGen, cpGen);
        // 2.a.2. replace the code of target (methodGen) by the code of actualAdvice
        redefineMethod(entry.advice, actualAdviceMethodGen, actualAdviceCPGen);
        // 2.a.3. weave 'proceed' statements, target method is now already
        //        redefined, so methodGen holds now the code of actualAdviceMethodGen
        pweaver.weaveRedefinedMethod();
      }
      else
        // 2.b. no 'proceed' invokation -> no proceed-weaving required
        //      replace the code of target (methodGen) by the code of actualAdvice
        redefineMethod(entry.advice, actualAdviceMethodGen, actualAdviceCPGen);
    }

    /**
     * Redefines the code of 'target' by the code of an advice method
     * @param newMethod advice method
     * @param newMethodGen bcel generator for the advice method
     * @param newCP bcel constant pool of the class declaring the advice method
     */
    private void redefineMethod(Method newMethod, MethodGen newMethodGen, ConstantPoolGen newCP) { 
      //System.out.println("MethodWeaver.redefineMethod(): " + target + " -> " + newMethod);

      // 1. set targets method generator
      methodGen.removeExceptionHandlers();
      instructions = newMethodGen.getInstructionList();
      methodGen.setInstructionList(instructions);

      // 2. transform instructions (where its required)
      HotSwapLocalVariableMapper varMapper = new HotSwapLocalVariableMapper(newMethod,(Method) getTarget());
      //    ('proceed' preparation requires iteration from the last
      //    to the first instruction)
      for (InstructionHandle h = instructions.getEnd(); h != null; h = h.getPrev()) {
        Instruction instr = h.getInstruction();

        // 2.b. Transform local variable (and parameter) access.
        if (instr instanceof LocalVariableInstruction) {
          LocalVariableInstruction lv_instr = (LocalVariableInstruction) instr;
          lv_instr.setIndex(varMapper.getNewSlot(lv_instr.getIndex()));
        }

        // 2.c. Transform constant pool references to constant pool of target class.
        //     Add missing constant pool entries in target class.
        else if (instr instanceof CPInstruction) {
          CPInstruction cp_instr = (CPInstruction) instr;
          org.apache.bcel.classfile.Constant c = newCP.getConstant(cp_instr.getIndex());
          int new_index = cpGen.addConstant(c, newCP);
          cp_instr.setIndex(new_index);
        }
      }
      // 2.d. Set the number of local variable slots used for the redefined method
      methodGen.setMaxLocals(varMapper.getNewMaxLocals(newMethodGen.getMaxLocals()));
      //methodGen.setMaxStack(advice_mg.getMaxStack());

      // 3. Copy exception handlers
      CodeExceptionGen[] cegs = newMethodGen.getExceptionHandlers();
      for (int i = 0; i < cegs.length; i++) {
        CodeExceptionGen ceg = cegs[i];
        methodGen.addExceptionHandler(ceg.getStartPC(), ceg.getEndPC(), ceg.getHandlerPC(), ceg.getCatchType());
      }
    }

    /**
     * Weave method entry advice callback into method body.
     */
    private void weaveMethodEntryAdvice() {
      //System.out.println("MethodWeaver.weaveMethodEntryAdvice(): " + target);
      InstructionHandle start = instructions.getStart();

      if( target instanceof Constructor ) {

        // JVM requires call to super constructors, to be done first,
        // but this works also when the callback is called first.
        // check if the constructor calls another constructor
        InstructionHandle h = usesSuper();
        if( null != h ) {
          // call must be woven after the other constructor was called.
          h = instructions.append( h,  new PUSH(cpGen, targetId) );
          instructions.append( h,
              instructionFactory.createInvoke(
                  JVMAI_CLASS,
                  "doOnConstructor",
                  Type.VOID,
                  new Type[] { Type.INT },
                  Constants.INVOKESTATIC));
        }
        else {
          // Push parameter: int methodId
          instructions.insert(start, new PUSH(cpGen, targetId));

          // Call advice
          instructions.insert( start,
              instructionFactory.createInvoke(
                  JVMAI_CLASS,
                  "doOnConstructor",
                  Type.VOID,
                  new Type[] { Type.INT },
                  Constants.INVOKESTATIC));
        }
      }
      else {

        // Push parameter: int methodId
        instructions.insert(start, new PUSH(cpGen, targetId));

        // Call advice
        instructions.insert( start,
            instructionFactory.createInvoke(
                JVMAI_CLASS,
                "doOnMethodEntry",
                Type.VOID,
                new Type[] { Type.INT },
                Constants.INVOKESTATIC));
      }
    }

    /**
     * Checks if another constructor is called by a constructor.
     * The call is the first thing that a constructor does.
     * @return InstructionHandle of the call or null if no call
     *                   to another constructor was found
     */
    private InstructionHandle usesSuper() {
      //System.out.println("MethodWeaver.usesSuper(): " + target);
      InstructionHandle handle = instructions.getStart();
      while( handle != null ) {
        Instruction inst = handle.getInstruction();
        if( inst instanceof INVOKESPECIAL ) {
          return handle;
        }
        if( ! (inst instanceof StackProducer) )
          break;
        handle = handle.getNext();
      }
      return null;
    }

    /**
     * Weave method exit advice callback into method body.
     */
    private void weaveMethodExitAdvice() {
      //System.out.println("MethodWeaver.weaveMethodExitAdvice(): " + target);
      InstructionHandle try_start = instructions.getStart();
      InstructionHandle try_end = instructions.getEnd();
      InstructionHandle real_end;

      // Weave method exit advice (in a finally block)
      int local_index = methodGen.getMaxLocals();
      InstructionHandle finally_start = instructions.append(new ASTORE(local_index));
      // Push parameter: int methodId
      instructions.append(new PUSH(cpGen, targetId));
      // Push parameter: int local value index of the return value
      // actual local index + 3 because two additional local variables
      // will be created below.
      instructions.append(new PUSH(cpGen, local_index + 3));
      // Call advice
      instructions.append(
          instructionFactory.createInvoke(
              JVMAI_CLASS,
              "doOnMethodExit",
              Type.VOID,
              new Type[] { Type.INT, Type.INT },
              Constants.INVOKESTATIC));

      real_end = instructions.append(new RET(local_index++));

      // Insert exception handler before finally block
      InstructionHandle catch_start = instructions.insert(finally_start, new ASTORE(local_index));
      instructions.insert(finally_start, new JSR(finally_start));
      instructions.insert(finally_start, new ALOAD(local_index++));
      instructions.insert(finally_start, new ATHROW());

      // Jump to finally block before each return
      JumpFinallyVisitor visitor = new JumpFinallyVisitor(instructions, try_start, catch_start, finally_start, local_index);
      visitor.go();
      local_index += visitor.getLocalSize();

      methodGen.setMaxLocals(local_index);
      methodGen.addExceptionHandler(try_start, catch_start.getPrev(), catch_start, null);
    }


    /**
     * Create a field access advice callback that can be woven.
     *
     * @param fieldWeaver weaver for the field.
     * @return List of instructions that make up the callback.
     */
    private InstructionList createFieldAccessAdviceCallback(HotSwapFieldWeaver fieldWeaver) {
      InstructionList il = new InstructionList();

      // Push parameter: Object owner
      if ( Modifier.isStatic( fieldWeaver.getTarget().getModifiers() ) )
        il.append( InstructionConstants.ACONST_NULL );
      else
        il.append( InstructionConstants.DUP );

      // Push parameter: int fieldId
      il.append( new PUSH(cpGen, fieldWeaver.getTargetId() ) );

      // Call advice
      il.append(
          instructionFactory.createInvoke(
              JVMAI_CLASS,
              "doOnFieldAccess",
              Type.VOID,
              new Type[] { Type.OBJECT, Type.INT },
              Constants.INVOKESTATIC));

      return il;
    }

    /**
     * Create a field modification advice callback that can be woven.
     *
     * @param fieldWeaver weaver for the field.
     * @param slot variable table index of the new value for the field
     * @return List of instructions that make up the callback.
     */
    private InstructionList createFieldModificationAdviceCallback(HotSwapFieldWeaver fieldWeaver, int slot) {
      InstructionList il = new InstructionList();

      // Push parameter: Object owner
      if ( Modifier.isStatic( fieldWeaver.getTarget().getModifiers() ) )
        il.append(InstructionConstants.ACONST_NULL);
      else
        il.append(InstructionConstants.DUP);

      // Push parameter: int fieldId
      int fieldId = fieldWeaver.getTargetId();
      il.append(new PUSH(cpGen, fieldId ));

      // push parameter: local variable index of the field
      il.append(new PUSH(cpGen, slot));

      // Call advice
      il.append(
          instructionFactory.createInvoke(
              JVMAI_CLASS,
              "doOnFieldModification",
              Type.VOID,
              new Type[] { Type.OBJECT, Type.INT, Type.INT },
              Constants.INVOKESTATIC));

      return il;
    }

    /**
     * Weave advices that are associated with the method in this weaver.
     *
     * Called by {@link HotSwapSimpleClassWeaver#weaveMethods()}.
     */
    protected void weaveMethod() {
      //System.out.println("HotSwapClassWeaver.weaveMethod() for " + target.toString() );
      // 1. Prepare
      initMethodWeaving();
      // 2. Redefine method
      if( !redefineAdvices.isEmpty() )
        weaveRedefineAdvice();
      // 3. Weave field advices
      if( !fieldAccessors.isEmpty() || !fieldModifiers.isEmpty() )
        weaveFieldAdvice();
      // 4. Weave method entry advice
      if( hasWatch(MWEAVER_STATUS_ENTRY_ENABLED) )
        weaveMethodEntryAdvice();
      // 5. Weaver method exit advice
      if( hasWatch(MWEAVER_STATUS_EXIT_ENABLED) )
        weaveMethodExitAdvice();
      // 6. Finish method weaving
      finishMethodWeaving();
    }

    /**
     * Prepare for weaving by initializing the corresponding fields.
     * This creates the BCEL method generator and extracts the
     * instruction list.
     *
     * Called by {@link #weaveMethod()}.
     */
    private void initMethodWeaving() {
      // 1. Get the method name
      String methodName;
      String signature;
      if( target instanceof Constructor ) {
        // the name of constructors is alway <init>
        methodName = "<init>";
        signature = JNIUtil.jniSignature((Constructor) target);
      }
      else // target is a method
        methodName = target.getName();
        signature = JNIUtil.jniSignature((Method) target);
      }

      // 2. Create the BCEL MethodGen object
      if( 0 > targetIndex ) {
        // 2.a Try to get the BCEL method object
        methodGen = null;
        org.apache.bcel.classfile.Method[] meths = clGen.getMethods();
        // Iterate over all member methods of the class
        for( int i = 0; i < meths.length; i++ ) {
          // Check for target
          if( methodName.equalsIgnoreCase( meths[i].getName() ) && signature.equals( meths[i].getSignature() ) ) {
            targetIndex = i;
            methodGen = new MethodGen( meths[i], methodName, cpGen );
            break;
          }
        }
      }
      else {
        methodGen = new MethodGen( clGen.getMethodAt(targetIndex), methodName, cpGen );
      }

      if( null == methodGen )
        throw new RuntimeException("Method " + methodName + " not found");

      // 3. create BCEL instructions list
      instructions = methodGen.getInstructionList();
    }

    /**
     * Clean up weaving process and replaces the method definition in ClassWeavers
     * class definition.
     *
     * Called by {@link #weaveMethod()}
     */
    private void finishMethodWeaving() {
      methodGen.setMaxStack();
      methodGen.setMaxLocals();
      methodGen.removeLocalVariables();
      methodGen.removeLineNumbers();
      methodGen.removeCodeAttributes();

      clGen.setMethodAt( methodGen.getMethod(), targetIndex );

      // release instructions
      instructions.dispose();
      instructions = null;
      methodGen = null;
    }

    /**
     *  Sets some variables to indicate that the target class
     *  must be woven at the next call of
     *  {@link HotSwapSimpleClassWeaver#commit()}.
     *  <P>
     *  This does not set the {@link #status} variable, which must
     *  be managed too.
     */
    private void setModified() {
      anyModified = true;

      changeStatus(WEAVER_STATUS_MODIFIED,true);
      status |= MWEAVER_STATUS_REDEFINE;
    }

    /**
     * Removes all modifications of the target method, so that it
     * will be restored to its original state after the next commitment
     * ({@link HotSwapSimpleClassWeaver#commit()}).
     */
    private void resetMethod() {
      status = MWEAVER_STATUS_UNCHANGED;
      redefineAdvices.clear();

      //methodCalls.clear();
      //methodReturns.clear();
      fieldAccessors.clear();
      fieldModifiers.clear();

    }

  }
}
TOP

Related Classes of ch.ethz.inf.iks.jvmai.jvmdi.HotSwapSimpleClassWeaver$MethodWeaver$RedefineAdviceListEntry

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.