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

Source Code of ch.ethz.inf.iks.jvmai.jvmdi.HotSwapClassRegister$RegisterMethodVisitorImpl

//
//  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: HotSwapClassRegister.java,v 1.2 2008/11/18 11:09:31 anicoara Exp $
//  =====================================================================
//
package ch.ethz.inf.iks.jvmai.jvmdi;


import java.lang.reflect.Field;
import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.Map;
import java.util.List;
import java.util.Set;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.Arrays;
import java.util.WeakHashMap;

import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Constant;
import org.apache.bcel.classfile.ConstantFieldref;
import org.apache.bcel.classfile.ConstantCP;
import org.apache.bcel.classfile.ConstantPool;
import org.apache.bcel.classfile.ConstantNameAndType;
import org.apache.bcel.generic.*;

import ch.ethz.jvmai.JVMAIRuntimeException;
import ch.ethz.jvmai.MethodWeaver;

/**
* Analyses classes and holds a register all found field
* accesses, field modifications, method calls, exception
* throws and exception catches.
*
* @author  Angela Nicoara
* @author  Gerald Linhofer
* @version $Revision: 1.2 $
*
*/
public class HotSwapClassRegister {

  /**
   * The one and only instance of this class.
   * (Singelton Pattern)
   */
  private static HotSwapClassRegister instance;
  /**
   * The JVMAspectInterface of this system or
   * <CODE>null</CODE> if not initialized.
   */
  private static HotSwapAspectInterfaceImpl aspectInterface;
  /**
   * Prefixes defining the scope of the prose system.
   * (Defines which packages should be affected by
   * Aspects, and which not.)
   */
  private static Set worldPrefixes;
  /**
   * <CODE>false</CODE> if only classes matching
   * the prefixes in {@link #worldPrefixes worldPrefixes}
   * should be scanned. Otherwise classes matching
   * the prefixes will be ignored.
   */
  private static boolean openWorldAssumption;
  /**
   * Holds the names of internal (Prose and Java) packages to
   * prevent them, and there sub packages, from beeing scanned.
   */
  private static Set internalPackages;

  static {
    internalPackages = new HashSet( 6 );
    // fill internalPackages with all packages which may cause
    // trouble during scanning or weaving.
    internalPackages.add( "org.apache.bcel" );        // some classes causes problems
    // add also all classes in the package 'java'
    // just to make shure there will be no problems.
    internalPackages.add( "java" );              // recommended by BCEL
    internalPackages.add( "javax" );            // recommended by BCEL
    internalPackages.add( "sun" );              // recommemded by BCEL
  }

  /**
   * Maps field names to collection of classes, which holds references to them.
   * This Map stores results from <CODE>scanConstantPool</CODE>.
   * The field name is in the form <CODE>Classname#FieldName#JNISignature</CODE>.
   */
  public static Map knownFieldReferences = new HashMap( 1024 );

  //  /**
  //   * Maps method names to collection of classes, which holds references to them.
  //   * This Map stores results from <CODE>scanConstantPool</CODE>.
  //   * The method name is in the form <CODE>Classname#MethodName#JNISignature</CODE>.
  //   */
  //  public static Map knownMethodReferences  = new HashMap( 1024 );

  /**
   * Maps fields to all methods known to access them.
   * Identifier for the fields are the keys, the value
   * is a Collection, holding identifiers for the method.
   * <P>
   * The field identifier has the form <CODE>Classname#FieldName#JNISignature</CODE>
   * and the method identifier <CODE>Classname#MethodName#JNISignature#isStatic</CODE>.
   */
  public static Map knownFieldAccesses = new HashMap( 1024 );
  /**
   * Maps fields to all methods known to modify them.
   * Identifier for the fields are the keys, the value
   * is a Collection, holding identifiers for the method.
   * <P>
   * The field identifier has the form <CODE>Classname#FieldName#JNISignature</CODE>
   * and the method identifier <CODE>Classname#MethodName#JNISignature#isStatic</CODE>.
   */
  public static Map knownFieldModifiers = new HashMap( 1024 );
  //  /**
  //   * Maps methods to all methods known to access them.
  //   * Identifier for the called methods are the keys, the value
  //   * is a Collection, holding identifiers for the caller.
  //   */
  //  public static Map knownMethodCalls        = new HashMap( 1024 );
  //  /**
  //   * Maps exceptions to all methods known to throw them.
  //   * Identifier for the exceptions are the keys, the value
  //   * is a Collection, holding identifiers for the method.
  //   *
  //   * Note: This are the 'declared' exceptions.
  //   */
  //  public static Map knownExceptionThrows    = new HashMap( 1024 );
  //  /**
  //   * Maps exceptions to all methods that may throw them.
  //   * Identifier for the exceptions are the keys, the value
  //   * is a Collection, holding identifiers for the method.
  //   *
  //   * This are potential exceptions, so they may be called
  //   * or not. to set an watch for such an exception, an
  //   * exception handler must be woven around the instruction,
  //   * which may throw the exception. (ignore method calls, to
  //   * avoid repeated advice calling for the same exception).
  //   */
  //  public static Map knownPotentialExceptionThrows  = new HashMap( 1024 );
  //  /**
  //   * Maps exceptions to all methods known to catch them.
  //   * Identifier for the exceptions are the keys, the value
  //   * is a Collection, holding identifiers for the method.
  //   */
  //  public static Map knownExceptionCatches      = new HashMap( 1024 );

  /**
   * <CODE>true</CODE> if all loaded classes should be scanned.
   * The value of this variable triggers if all new loaded classes
   * should be scanned or not.
   */
  private boolean registerAll  = false;
  /**
   * Number of active weavers requested registerAll.
   */
  private int registerAllCount = 0;
  /**
   * Holds packages, which contains classes that should be
   * scanned.
   */
  private Map registeredPackages = new HashMap();
  /**
   * Holds classes for which all direct or indirect sub classes
   * should be scanned.
   */
  private Map registeredSuperClasses = new HashMap();
  /**
   * Remembers the scanned classes and maps them to
   * a collection with all <CODE>known...</CODE> map
   * entries, pointing to methods of this class.
   *
   * This is also used as monitor object to synchronize
   * modification of this map or any of the static known...
   * maps defined above.
   */ 
  private Map scannedClasses = new WeakHashMap( 1024 );

  /// Dummy value for using WeakHashMap as HashSet
  private static Object dummyValue = new Object();

  /**
   * Remembers the classes, which constant pools where scanned for
   * field and method references.
   */
  private Map scannedConstantPools = new WeakHashMap( 1024 );


  //--------------------------------------------------------------------------------------------------------------
  // Internal method to determine if a class should be scanned
  //--------------------------------------------------------------------------------------------------------------

  /**
   * Determines if a class may be scanned. This depends
   * on static constraints, which are hard coded or where
   * set at startup of prose ({@link #worldPrefixes worldPrefixes}
   * and {@link #internalPackages internalPackages}).
   *
   * @param className name of the class.
   * @return <CODE>true</CODE> if scanning of the class is
   *         allowed.
   */ 
  private boolean isScanningAllowed( Class cls ) {
    // Note: check for array class is only required for JVMDI not for JVMTI
    if( null == aspectInterface || cls.isInterface() || cls.isArray() /*|| cls.isPrimitive()*/ /*|| ch.ethz.prose.DefaultAspect.class.isAssignableFrom(cls)*/ || null == cls )
      return false;

    String className = cls.getName();     
    int pos = className.indexOf( '.' );

    if( ! openWorldAssumption ) {
      // check if any super package or the package it self
      // is in 'worldPrefixes'.
      while( -1 < pos ) {
        String packageName = className.substring( 0, pos );
        // Check if it's an internal class
        if( internalPackages.contains( packageName ) )
          return false;

        // Check if it 'part of our world'
        if( worldPrefixes.contains( packageName ) )
          return true;

        pos = className.indexOf( '.', pos + 1 );
      }
      return false;
    }
    else {
      // check if any super package or the package it self
      // is in 'internalPackage' or 'worldPrefixes'.
      while( -1 < pos ) {
        String packageName = className.substring( 0, pos );
        if(
            internalPackages.contains( packageName )
            || worldPrefixes.contains( packageName )
        )
          return false;

        pos = className.indexOf( '.', pos + 1 );
      }
      return true;
    }
  }

  /**
   * Determines if a class should be scanned or not.
   * checks also if scanning is allowed. This checks
   * for 'dynamic' constraints, which may change at
   * each invocation of a <CODE>register...()<CODE>
   * or <CODE>unregister..()<CODE> method.
   *
   * @param cls Class that may be scanned.
   * @return <CODE>true</CODE> if <CODE>cls</CODE>
   *         should be scanned.
   */ 
  private boolean isScanningRequired( Class cls ) {
    return (! scannedConstantPools.containsKey( cls )) &&
    isScanningAllowed( cls ) &&
   
        registerAll ||
        registeredPackages.containsKey( cls.getPackage() ) ||
        hasRegisteredSuperClass( cls )
    );
  }

  /**
   * Returns true if {@link #registeredSuperClasses registeredSuperClasses}
   * contains a super class of <CODE>cls</CODE>.
   *
   * @param cls
   * @return boolean
   */ 
  private boolean hasRegisteredSuperClass( Class cls ) {
    // check for all super classes of cls,
    // if it's in registeredSuperClasses.

    // the problem with checking the super classes is, that
    // it ignores interfaces. so either an additional check
    // for interfaces, or a painful slow checking of all
    // registered super classes is required.

    for(
        Class subClass = cls;
        null != subClass;
        subClass = subClass.getSuperclass()
    )
      if( registeredSuperClasses.containsKey( subClass ) )
        return true;

    Class[] interfaces = cls.getInterfaces();
    for( int i = 0; i < interfaces.length; i++ )
      if( hasRegisteredSuperClass( interfaces[i] ) )
        return true;

    return false;
  }

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

  /**
   * Creates an uninitialized instance of this class.
   * private to force use of {@link #getInstance
   * getInstance()} (Singleton Pattern).
   */
  private HotSwapClassRegister() {
    rmv = new RegisterMethodVisitorImpl();
  }

  /**
   * Returns the instance of this class (this is a
   * 'singleton', so there's only one instance).
   *
   * @return HotSwapClassRegister instance.
   */ 
  public static HotSwapClassRegister getInstance() {
    if( null == instance )
      instance = new HotSwapClassRegister();
    return instance;
  }

  /**
   * Registers the JVMAspectInterface (must be called before the first
   * call to {@link #scan scan(java.lang.Class)}.
   *
   * @param aspectInterface
   * @param packagePrefixes
   * @param openWorldAssumption
   */
  protected static void setAspectInterface( HotSwapAspectInterfaceImpl aspectInterface, String[] packagePrefixes, boolean openWorldAssumption ) {
    HotSwapClassRegister.aspectInterface = aspectInterface;
    HotSwapClassRegister.openWorldAssumption = openWorldAssumption;
    worldPrefixes = new HashSet( Arrays.asList( packagePrefixes ) );
  }

  //--------------------------------------------------------------------------------------------------
  // Public Methods to trigger scanning of classes (register them for scanning)
  //--------------------------------------------------------------------------------------------------
  /**
   * Notification that the class <code>cls</code> will be loaded.
   * Scanns the cls if required, and triggers weaving of the class
   * if it contains any join point.
   *
   * @param cls  the class that has been prepared
   */
  public void classLoaded(Class cls) {
    synchronized( scannedClasses ) {
      if( isScanningRequired( cls ) ) {
        try {
          //System.out.println("on class load: Scanning: " + cls.getName() );
          instance.scanConstantPool( cls );
          // redefine methods if required
          // this must be done after the class was
          // loaded!
          if( instance.weave ) {
            aspectInterface.resumeNotification( Thread.currentThread() );
            //System.out.println("on class load: Weaving: " + cls.getName() );
          }
        }
        catch( ClassNotFoundException e ) {
          System.err.println("can not load classfile for " + cls.getName() );
          System.err.println( e.getMessage() );
        }
      }
    }
  }


  /**
   * Registers a class, which may hold a field, method call or
   * exception join point. This class will be scanned immediately
   * and all potential join points will be registered.
   *
   * @param cls class that may contain join points.
   * @throws java.io.IOException can not read the class file.
   */
  public void registrationRequest( Class cls ) throws ClassNotFoundException {
    synchronized( scannedClasses ) {
      if(
          ( !scannedConstantPools.containsKey( cls )  ) &&
          isScanningAllowed( cls )
      ){
        scanConstantPool( cls );
      }
    }
  }

  /**
   * Registers a package, which may contain classes that may
   * hold a field, method call or exception join point. This classes
   * will be scanned and all potential join points will be registered.
   * <p>
   * New loaded classes will also be scanned until {@link #removeRequestAll
   * removeRequestAll()} is called.
   *
   * @param pkg
   */
  public void registrationRequest( Package pkg ) {
    if( null == aspectInterface )
      throw new JVMAIRuntimeException( this.getClass().getName() + ".setAspectInterface() must be called first");

    Integer regCount = (Integer) registeredPackages.get( pkg )
    if( null == regCount ) {
      if( ! registerAll ) {
        String packageName = pkg.getName();

        // get all loaded classes
        List loadedClasses = aspectInterface.getLoadedClasses();
        Iterator iter = loadedClasses.iterator();
        while( iter.hasNext() ) {
          Class loadedClass =(Class) iter.next();
          // look if the class belongs to pkg
          if( loadedClass.getName().startsWith( packageName ) ) {
            try {
              registrationRequest( loadedClass );
            }
            catch( ClassNotFoundException e ) {
              // just report the error
              System.err.println( this.getClass().getName() + ".registrationRequest(Package): Could not read class file " + loadedClass.getName() );
              System.err.println( "->  " + e.getMessage() );
            }
          }
        }
      }
      registeredPackages.put( pkg, new Integer( 1 ) );
    }
    else
      registeredPackages.put( pkg, new Integer( regCount.intValue() + 1 ) );
  }

  /**
   * Scans all loaded subclasses of <CODE>cls</CODE> for
   * potential join points.
   * <P>
   * New loaded classes will also be scanned.
   *
   * @param cls
   */
  public void registrationRequestSubClasses( Class cls ) {
    if( null == aspectInterface )
      throw new JVMAIRuntimeException( this.getClass().getName() + ".setAspectInterface() must be called first");

    Integer regCount = (Integer) registeredSuperClasses.get( cls );
    if( null == regCount ) {
      if( ! registerAll ) {
        List loadedClasses = aspectInterface.getLoadedClasses();
        Iterator iter = loadedClasses.iterator();
        while( iter.hasNext() ) {
          Class loadedClass = (Class) iter.next();
          if( cls.isAssignableFrom( loadedClass ) )
            try {
              registrationRequest( loadedClass );
            }
          catch( ClassNotFoundException e ) {
            // just report the error
            System.err.println( this.getClass().getName() + ".registrationRequestSubClasses(Class): Could not read class file " + loadedClass.getName() );
            System.err.println( "->  " + e.getMessage() );
          }
        }
      }
      registeredSuperClasses.put( cls, new Integer(1) );
    }
    else {
      registeredSuperClasses.put( cls, new Integer( regCount.intValue() + 1 ) );
    }
  }

  /**
   * Scans all loaded classes for potential field, method call and exception
   * join points.
   * <P>
   * New loaded classes will also be scanned until {@link #removeRequestAll
   * removeRequestAll()} is called.
   */
  public void registrationRequestAll() {
    if( null == aspectInterface )
      throw new JVMAIRuntimeException( this.getClass().getName() + ".setAspectInterface() must be called first");

    registerAllCount++;

    if( ! registerAll ) {
      registerAll = true;

      // registrationRequest all loaded classes
      List loadedClasses = aspectInterface.getLoadedClasses();
      Iterator iter = loadedClasses.iterator();
      while( iter.hasNext() ) {
        Class loadedClass =(Class) iter.next();
        try {
          registrationRequest( loadedClass );
        }
        catch( ClassNotFoundException e ) {
          // just report the error
          //System.err.println(this.getClass().getName() + ".registrationRequestAll(): Could not read class file " + loadedClass.getName() );
          //System.err.println( "->  " + e.getMessage() );
        }
      }
    }
  }

  /**
   * Just for convenience, this method does nothing.
   * @param cls
   */
  public void removeRequest( Class cls ) {
    // Nothing to do
  }

  /**
   * Stops scanning and registration of new loaded classes
   * belonging to Package <CODE>pkg</CODE>. If {@link #registrationRequestAll
   * registrationRequestAll()} is set, the classes will still be scanned until
   * {@link #removeRequestAll removeRequestAll()} is called.
   *
   * @param pkg
   */
  public void removeRequest( Package pkg ) {
    Integer regCount = (Integer) registeredPackages.get( pkg );
    if( null != regCount && (1 < regCount.intValue()))
      registeredPackages.put( pkg, new Integer( regCount.intValue() - 1 ) );
    else
      registeredPackages.remove( pkg );
  }

  /**
   * Stops scanning and registration of new loaded sub classes
   * of <CODE>cls</CODE>. If {@link #registrationRequestAll registrationRequestAll()}
   * is set, the classes will still be scanned until {@link
   * #removeRequestAll removeRequestAll()} is called.
   *
   * @param cls
   */
  public void removeRequestSubClass( Class cls ) {
    Integer regCount = (Integer) registeredSuperClasses.get( cls );
    if( null != regCount && (1 < regCount.intValue() ) )
      registeredSuperClasses.put( cls, new Integer( regCount.intValue() - 1 ) );
    else
      registeredSuperClasses.remove( cls );
  }

  /**
   * Stops scanning and registration of all new loaded classes.
   */
  public void removeRequestAll() {
    if( 1 < registerAllCount ) {
      registerAllCount = 0;
      registerAll = false;
    }
    else
      registerAllCount--;
  }

  /**
   * Clears all registrations and registration requests
   */ 
  public void reset() {
    // 1. Stop scanning of new loaded classes
    registerAll = false;
    registerAllCount = 0;
    registeredPackages.clear();
    registeredSuperClasses.clear();
    // 2. Clear informations
    synchronized( scannedClasses ) {
      knownFieldReferences.clear();
      //knownMethodReferences.clear();
      //knownExceptionCatches.clear();
      //knownExceptionThrows.clear();
      knownFieldAccesses.clear();
      knownFieldModifiers.clear();
      //knownMethodCalls.clear();
      scannedClasses.clear();
      scannedConstantPools.clear();
    }
  }

  //---------------------------------------------------------------------------------------------------------------------------
  // Fields and Methods for scanning class files
  //---------------------------------------------------------------------------------------------------------------------------

  private boolean weave;

  /**
   * This object does the work: Scanning the methods and reporting
   * the mappings.
   */ 
  private RegisterMethodVisitorImpl rmv;

  /**
   * BCEL constant pool object for the class.
   */
  private ConstantPoolGen  cpGen;

  /**
   * Scans the constant pool of class <CODE>cls</CODE> for method and field
   * references.
   *
   * @param cls the class that's constant pool should get scanned.
   * @throws ClassNotFoundException can not find the file defining the class.
   */ 
  protected void scanConstantPool( Class cls ) throws ClassNotFoundException {
    if(scannedClasses.containsKey(cls))
      return;

    String className = cls.getName();
    // 0. Get the BCEL constant pool object
    JavaClass bcelClass = HotSwapAspectInterfaceImpl.getBCELClassDefinition( cls );
    ConstantPool constantPool = bcelClass.getConstantPool();
    Constant[] constants = constantPool.getConstantPool();

    // Remember the class, so it doesn't get scanned again.
    scannedConstantPools.put( cls, dummyValue );

    // 1. Iterate through all constant pool entries
    for( int i = 0; i < constants.length; i++ ) {
      Constant con = constants[i];

      // 2. Get field and method references
      if( con instanceof ConstantCP ) {
        ConstantCP cp = (ConstantCP) con;
        ConstantNameAndType nt = (ConstantNameAndType) constants[cp.getNameAndTypeIndex()];
        // 3. Create a unique key (string) for the field or method
        String key = cp.getClass( constantPool ) + '#' + nt.getName( constantPool ) + '#' + nt.getSignature( constantPool );

        if( con instanceof ConstantFieldref) {
          // Constant is a field reference

          // 4. Look if the field must be woven
          if( HotSwapFieldWeaver.weaverNames.containsKey(key) ) {
            // Scan all methods and return
            //System.out.println("ClassRegister.scanConstantPool: found key: " + key);
            scan( cls );
            return;
          }
          // 5. Get the entry for the field
          Collection col = (Collection) knownFieldReferences.get( key );
          if( null == col ) {
            // 5a. Create a new entry for the field
            col = new LinkedList();
            knownFieldReferences.put( key, col );
          }
          // 6. Add a reference to cls to the entry
          col.add( new WeakReference( cls ) );
        }

        // The lines below will be used for the implementation of
        // Method call join points
        //
        //        else {
        //          // Constant is a method reference (ConstantMethodref or ConstantInterfaceMethodref)
        //          // 4. Get the entry for the method
        //          Collection col = (Collection) knownMethodReferences.get( key );
        //          if( null == col ) {
        //            // 4a. Create a new entry for the method
        //            col = new LinkedList();
        //            knownMethodReferences.put( key, col );
        //          }
        //          // 5. Add cls to the entry
        //          col.add( new WeakReference( cls ) );
        //        }
      }

    }
  }

  /**
   * Scans the method byte codes of a Collection of classes. Already scanned classes
   * will be ignored.
   *
   * @param cls Collection holding {@link java.lang.Class Class} objects, for the classes
   *           that will be scanned.
   */
  public void scanClasses( List cls ) {
    if( null == cls )
      return;

    ListIterator iter = cls.listIterator();
    while( iter.hasNext() ) {
      Class klass = (Class) ((WeakReference)iter.next()).get();
      if( null == klass )
        iter.remove();
      else if( !scannedClasses.containsKey(klass) )
        try{ scan( klass ); }
      catch(ClassNotFoundException e)
      { System.err.println("HotSwapClassRegister.scanClasses: " + e.getMessage() ); }
    }
  }

  /**
   * Scans the target method for field accesses, field modifications,
   * catch exceptions, method calls and method returns, and registers
   * all found actions to the according map.
   *
   * @param cls
   * @throws java.io.IOException can not read the class file
   */
  protected void scan( Class cls ) throws ClassNotFoundException {
    weave = false;

    String className = cls.getName();

    // 1. Get the BCEL JavaClass object for cls
    JavaClass bcelClass;
    bcelClass = HotSwapAspectInterfaceImpl.getBCELClassDefinition( cls );
    // The constant pool is common for all methods belonging to a class, so we store it
    // here to make it accessible to all RegisterMethodVisitors
    cpGen = new ConstantPoolGen( bcelClass.getConstantPool() );

    // 2. Scan each method defined in the class file
    org.apache.bcel.classfile.Method[] methods = bcelClass.getMethods();

    for( int j = 0; j < methods.length; j++ ) {
      org.apache.bcel.classfile.Method method = methods[j];
      if( (! method.isAbstract()) && (! method.isNative()) ) {
        MethodGen methodGen = new MethodGen( methods[j], className, cpGen );
        rmv.init( methodGen/*, entries, declMembers[j]*/ );
        rmv.go();
      }
    }
    cpGen = null;
    scannedClasses.put( cls, dummyValue );
  }


  //------------------------------------------------------------------------------------------------------------
  // Inner class for scaning the method bodies.
  //------------------------------------------------------------------------------------------------------------

  /**
   * BCEL InstructionList visitor implementation, which registers all
   * field accesses, field modifications, potential exception throws
   * (except for JVMExceptions, which may be thrown anywhere)
   * and method calls found in the InstructionList.
   */
  class RegisterMethodVisitorImpl extends EmptyVisitor {
    /**
     * List of instructions (method body)
     */
    private InstructionList  instList;
    /**
     * String holding the class name and the method name of the
     * analyzed method separated by an # and the signature.
     * Used as key for registering actions.
     */
    private String key;
    /**
     * Temporary instruction handle. References the actual
     * instruction while iterating through instList.
     */
    private InstructionHandle handle;


    /**
     * Initializes this object. The same object may be initialized
     * multiple times.
     *
     * @param mg MethodGen for the target method that should be scanned.
     */
    void init( MethodGen mg ) {
      instList = mg.getInstructionList();

      key = mg.getClassName()
      + "#" + mg.getName()
      + '#' + mg.getSignature()
      + (mg.isStatic() ? "#" : "");
    }

    /**
     * Scan the target method, and registers any occurrences of
     * field manipulations, method calls and exception throws
     */
    public synchronized void go() {
      if( null == instList )
        return;

      handle = instList.getStart();
      while ( null != handle ) {
        handle.getInstruction().accept(this);
        handle = handle.getNext();
      }

      instList.dispose();
    }

    // Exceptions and method calls are not yet implemented

    //    public void visitATHROW(ATHROW obj) {
    //      Class[] exceptions = obj.getExceptions();
    //      for( int i = 0; i < exceptions.length; i++ ) {
    //        Class exc = exceptions[i];
    //        // String key = exc.getName();
    //        List mws = (List) knownExceptionThrows.get( exc );
    //        if( null == mws ) {
    //          mws = new LinkedList();
    //          knownExceptionThrows.put( exc, mws );
    //        }
    //        mws.add( key );
    //        entries.add( mws );
    //      }
    //    }

    //    /**
    //     * Instruction may throw an exception.
    //     *
    //     * Note: this registers all potential exception throws, not
    //     *       only the declared throw instructions.
    //     */
    //    public void  visitExceptionThrower(ExceptionThrower obj) {
    //      // Exceptions thrown by InvokeInstruction mostly are thrown at
    //      // an other method.
    //      if( obj instanceof InvokeInstruction ) {
    //        // TODO: implementation that avoid repeated calls
    //      }
    //   
    //      Class[] exceptions = obj.getExceptions();
    //      if( obj instanceof ATHROW)
    //        // Explicit exception throws
    //        for( int i = 0; i < exceptions.length; i++ ) {
    //          Class exc = exceptions[i];
    //          // String key = exc.getName();
    //          List mws = (List) knownExceptionThrows.get( exc );
    //          if( null == mws ) {
    //            mws = new LinkedList();
    //            knownExceptionThrows.put( exc, mws );
    //          }
    //          mws.add( key );
    //          entries.add( mws );
    //        }
    //      else
    //        // Instructions that may throw an exception
    //        for( int i = 0; i < exceptions.length; i++ ) {
    //          Class exc = exceptions[i];
    //          List mws = (List) knownPotentialExceptionThrows.get( exc );
    //          if( null == mws ) {
    //            mws = new LinkedList();
    //            knownPotentialExceptionThrows.put( exc, mws );
    //          }
    //          mws.add( key );
    //          entries.add( mws );
    //        }
    //    }
    /**
     * Instruction accesses a member field.
     */
    public  void visitGETFIELD(GETFIELD obj) {
      doVisitFieldAccess( obj, false );
    }
    /**
     * Instruction accesses a static field.
     */
    public void  visitGETSTATIC(GETSTATIC obj) {
      doVisitFieldAccess( obj, true );
    }
    /**
     * Instruction modifies a member field.
     */
    public void  visitPUTFIELD(PUTFIELD obj) {
      doVisitFieldModification( obj, false );
    }
    /**
     * Instruction modifies a static field.
     */
    public void  visitPUTSTATIC(PUTSTATIC obj) {
      doVisitFieldModification( obj, true );
    }

    //    /**
    //     * Instruction calls a method.
    //     */
    //    public void visitInvokeInstruction(InvokeInstruction obj) {
    //      Member method;
    //      try{ method = aspectInterface.getMethodFromString( obj.getReferenceType(cpGen).toString(), obj.getName(cpGen), obj.getSignature(cpGen), obj instanceof INVOKESTATIC ); }
    //      catch( Exception e ) { throw new JVMAIRuntimeException("can not resolve method: " + obj.getReferenceType(cpGen).toString() + "." + obj.getName(cpGen)); }
    //      List mws = (List) knownMethodCalls.get( method );
    //      if( null == mws ) {
    //        mws = new LinkedList();
    //        knownMethodCalls.put( method, mws );
    //      }
    //      mws.add( key );
    //      entries.add( mws );
    //     
    //      // TODO: register also the method call weaver
    //    }

    //    /**
    //     * Instruction may call a constructor.
    //     */
    //    public void  visitNEW(NEW obj) {
    //    }

    private void doVisitFieldAccess( FieldInstruction obj, boolean isStatic ) {
      String fkey = obj.getReferenceType(cpGen).toString() + '#' + obj.getFieldName( cpGen ) + '#' + obj.getSignature( cpGen );
      // 1. Register the field to knownFieldAccesses

      List mws = (List) knownFieldAccesses.get( fkey );
      if( null == mws ) {
        mws = new LinkedList();
        knownFieldAccesses.put( fkey, mws );
      }
      mws.add( key );

      // 2. Register the field to the MethodWeaver for this method,
      //    if there's a watch for it.
      HotSwapFieldWeaver fw = (HotSwapFieldWeaver) HotSwapFieldWeaver.weaverNames.get( fkey );
      if( null != fw && (HotSwapFieldWeaver.FW_ACCESS_ENABLED & fw.status) > 0 ) {
        // Weave method
        MethodWeaver mw;
        try{ mw = HotSwapClassWeaver.getWeaver( HotSwapAspectInterfaceImpl.getMethodFromString( key ) ); }
        catch(ClassNotFoundException e) { throw new JVMAIRuntimeException( "scanning a class file, but can not find the class: " + e.getMessage() ); }
        mw.addFieldAccessor( fw );
        weave = true;
      }
    }

    private void doVisitFieldModification( FieldInstruction obj, boolean isStatic ) {
      // 1. Register the field to knownFieldModifications
      String fkey = obj.getReferenceType(cpGen).toString() + '#' + obj.getFieldName( cpGen ) + '#' + obj.getSignature( cpGen );

      List mws = (List) knownFieldModifiers.get( fkey );
      if( null == mws ) {
        mws = new LinkedList();
        knownFieldModifiers.put( fkey, mws );
      }
      mws.add( key );

      // 2. Register the field to the MethodWeaver for this method,
      //    if there's a watch for it.
      HotSwapFieldWeaver fw = (HotSwapFieldWeaver) HotSwapFieldWeaver.weaverNames.get( fkey );
      if( null != fw && (HotSwapFieldWeaver.FW_MODIFICATION_ENABLED & fw.status) > 0 ) {
        // Weave method
        MethodWeaver mw;
        try{ mw = HotSwapClassWeaver.getWeaver( HotSwapAspectInterfaceImpl.getMethodFromString( key ) ); }
        catch(ClassNotFoundException e) { throw new JVMAIRuntimeException( "scanning a class file, but can not find the class: " + e.getMessage() ); }
        mw.addFieldModifier( fw );
        weave = true;
      }
    }

    private Field resolveFieldName( FieldInstruction obj, boolean isStatic ) {
      try{ return HotSwapAspectInterfaceImpl.getFieldFromString( obj.getReferenceType(cpGen).toString(), obj.getName(cpGen), obj.getSignature(cpGen), isStatic ); }
      catch(Exception e) { throw new JVMAIRuntimeException("can not resolve field " + obj.getReferenceType(cpGen).toString() + "." + obj.getName(cpGen) + ": " + e.getClass().getName() + ": " + e.getMessage() ); }
    }
  }

}
TOP

Related Classes of ch.ethz.inf.iks.jvmai.jvmdi.HotSwapClassRegister$RegisterMethodVisitorImpl

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.