//
// 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() ); }
}
}
}