blob: e46aa36015648a7028a65843ced7fb85113c3b55 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.aries.proxy.impl.gen;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Modifier;
import org.apache.aries.proxy.impl.NLS;
import org.apache.aries.proxy.impl.ProxyUtils;
import org.apache.aries.proxy.impl.SystemModuleClassLoader;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.GeneratorAdapter;
import org.objectweb.asm.commons.Method;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ProxySubclassAdapter extends ClassVisitor implements Opcodes
{
private static final Type STRING_TYPE = Type.getType(String.class);
private static final Type CLASS_TYPE = Type.getType(Class.class);
private static final Type CLASSLOADER_TYPE = Type.getType(ClassLoader.class);
private static final Type OBJECT_TYPE = Type.getType(Object.class);
private static final Type METHOD_TYPE = Type.getType(java.lang.reflect.Method.class);
private static final Type IH_TYPE = Type.getType(InvocationHandler.class);
private static final Type[] NO_ARGS = new Type[] {};
private static final String IH_FIELD = "ih";
private static Logger LOGGER = LoggerFactory.getLogger(ProxySubclassAdapter.class);
private String newClassName = null;
private String superclassBinaryName = null;
private Class<?> superclassClass = null;
private ClassLoader loader = null;
private Type newClassType = null;
private GeneratorAdapter staticAdapter = null;
private String currentlyAnalysedClassName = null;
private Class<?> currentlyAnalysedClass = null;
private String currentClassFieldName = null;
public ProxySubclassAdapter(ClassVisitor writer, String newClassName, ClassLoader loader)
{
// call the superclass constructor
super(Opcodes.ASM5, writer);
// the writer is now the cv in the superclass of ClassAdapter
LOGGER.debug(Constants.LOG_ENTRY, "ProxySubclassAdapter", new Object[] { this, writer,
newClassName });
// set the newClassName field
this.newClassName = newClassName;
// set the newClassType descriptor
newClassType = Type.getType("L" + newClassName + ";");
// set the classloader
this.loader = loader;
LOGGER.debug(Constants.LOG_EXIT, "ProxySubclassAdapter", this);
}
/*
* This method visits the class to generate the new subclass.
*
* The following things happen here: 1. The class is renamed to a dynamic
* name 2. The existing class name is changed to be the superclass name so
* that the generated class extends the original class. 3. A private field
* is added to store an invocation handler 4. A constructor is added that
* takes an invocation handler as an argument 5. The constructor method
* instantiates an instance of the superclass 6. The constructor method sets
* the invocation handler so the invoke method can be called from all the
* subsequently rewritten methods 7. Add a getInvocationHandler() method 8.
* store a static Class object of the superclass so we can reflectively find
* methods later
*/
public void visit(int version, int access, String name, String signature, String superName,
String[] interfaces)
{
LOGGER.debug(Constants.LOG_ENTRY, "visit", new Object[] { version, access, name,
signature, superName, interfaces });
// store the superclass binary name
this.superclassBinaryName = name.replaceAll("/", "\\.");
try {
this.superclassClass = Class.forName(superclassBinaryName, false, loader);
} catch (ClassNotFoundException cnfe) {
throw new TypeNotPresentException(superclassBinaryName, cnfe);
}
// keep the same access and signature as the superclass (unless it's abstract)
// remove all the superclass interfaces because they will be inherited
// from the superclass anyway
if((access & ACC_ABSTRACT) != 0) {
//If the super was abstract the subclass should not be!
access &= ~ACC_ABSTRACT;
}
cv.visit(ProxyUtils.getWeavingJavaVersion(), access, newClassName, signature, name, null);
// add a private field for the invocation handler
// this isn't static in case we have multiple instances of the same
// proxy
cv.visitField(ACC_PRIVATE, IH_FIELD, Type.getDescriptor(InvocationHandler.class), null, null);
// create a static adapter for generating a static initialiser method in
// the generated subclass
staticAdapter = new GeneratorAdapter(ACC_STATIC,
new Method("<clinit>", Type.VOID_TYPE, NO_ARGS), null, null, cv);
// add a zero args constructor method
Method m = new Method("<init>", Type.VOID_TYPE, NO_ARGS);
GeneratorAdapter methodAdapter = new GeneratorAdapter(ACC_PUBLIC, m, null, null, cv);
// loadthis
methodAdapter.loadThis();
// List the constructors in the superclass.
Constructor<?>[] constructors = superclassClass.getDeclaredConstructors();
// Check that we've got at least one constructor, and get the 1st one in the list.
if (constructors.length > 0) {
// We now need to construct the proxy class as though it is going to invoke the superclasses constructor.
// We do this because we can no longer call the java.lang.Object() zero arg constructor as the JVM now throws a VerifyError.
// So what we do is build up the calling of the superclasses constructor using nulls and default values. This means that the
// class bytes can be verified by the JVM, and then in the ProxySubclassGenerator, we load the class without invoking the
// constructor.
Method constructor = Method.getMethod(constructors[0]);
Type[] argTypes = constructor.getArgumentTypes();
if (argTypes.length == 0) {
methodAdapter.invokeConstructor(Type.getType(superclassClass), new Method("<init>", Type.VOID_TYPE, NO_ARGS));
} else {
for (Type type : argTypes) {
switch (type.getSort())
{
case Type.ARRAY:
// We need to process any array or multidimentional arrays.
String elementDesc = type.getElementType().getDescriptor();
String typeDesc = type.getDescriptor();
// Iterate over the number of arrays and load 0 for each one. Keep a count of the number of
// arrays as we will need to run different code fo multi dimentional arrays.
int index = 0;
while (! elementDesc.equals(typeDesc)) {
typeDesc = typeDesc.substring(1);
methodAdapter.visitInsn(Opcodes.ICONST_0);
index++;
}
// If we're just a single array, then call the newArray method, otherwise use the MultiANewArray instruction.
if (index == 1) {
methodAdapter.newArray(type.getElementType());
} else {
methodAdapter.visitMultiANewArrayInsn(type.getDescriptor(), index);
}
break;
case Type.BOOLEAN:
methodAdapter.push(true);
break;
case Type.BYTE:
methodAdapter.push(Type.VOID_TYPE);
break;
case Type.CHAR:
methodAdapter.push(Type.VOID_TYPE);
break;
case Type.DOUBLE:
methodAdapter.push(0.0);
break;
case Type.FLOAT:
methodAdapter.push(0.0f);
break;
case Type.INT:
methodAdapter.push(0);
break;
case Type.LONG:
methodAdapter.push(0l);
break;
case Type.SHORT:
methodAdapter.push(0);
break;
default:
case Type.OBJECT:
methodAdapter.visitInsn(Opcodes.ACONST_NULL);
break;
}
}
methodAdapter.invokeConstructor(Type.getType(superclassClass), new Method("<init>", Type.VOID_TYPE, argTypes));
}
}
methodAdapter.returnValue();
methodAdapter.endMethod();
// add a method for getting the invocation handler
Method setter = new Method("setInvocationHandler", Type.VOID_TYPE, new Type[] { IH_TYPE });
m = new Method("getInvocationHandler", IH_TYPE, NO_ARGS);
methodAdapter = new GeneratorAdapter(ACC_PUBLIC | ACC_FINAL, m, null, null, cv);
// load this to get the field
methodAdapter.loadThis();
// get the ih field and return
methodAdapter.getField(newClassType, IH_FIELD, IH_TYPE);
methodAdapter.returnValue();
methodAdapter.endMethod();
// add a method for setting the invocation handler
methodAdapter = new GeneratorAdapter(ACC_PUBLIC | ACC_FINAL, setter, null, null, cv);
// load this to put the field
methodAdapter.loadThis();
// load the method arguments (i.e. the invocation handler) to the stack
methodAdapter.loadArgs();
// set the ih field using the method argument
methodAdapter.putField(newClassType, IH_FIELD, IH_TYPE);
methodAdapter.returnValue();
methodAdapter.endMethod();
// loop through the class hierarchy to get any needed methods off the
// supertypes
// start by finding the methods declared on the class of interest (the
// superclass of our dynamic subclass)
java.lang.reflect.Method[] observedMethods = superclassClass.getDeclaredMethods();
// add the methods to a set of observedMethods
ProxySubclassMethodHashSet<String> setOfObservedMethods = new ProxySubclassMethodHashSet<String>(
observedMethods.length);
setOfObservedMethods.addMethodArray(observedMethods);
// get the next superclass in the hierarchy
Class<?> nextSuperClass = superclassClass.getSuperclass();
while (nextSuperClass != null) {
// set the fields for the current class
setCurrentAnalysisClassFields(nextSuperClass);
// add a static field and static initializer code to the generated
// subclass
// for each of the superclasses in the hierarchy
addClassStaticField(currentlyAnalysedClassName);
LOGGER.debug("Class currently being analysed: {} {}", currentlyAnalysedClassName,
currentlyAnalysedClass);
// now find the methods declared on the current class and add them
// to a set of foundMethods
java.lang.reflect.Method[] foundMethods = currentlyAnalysedClass.getDeclaredMethods();
ProxySubclassMethodHashSet<String> setOfFoundMethods = new ProxySubclassMethodHashSet<String>(
foundMethods.length);
setOfFoundMethods.addMethodArray(foundMethods);
// remove from the set of foundMethods any methods we saw on a
// subclass
// because we want to use the lowest level declaration of a method
setOfFoundMethods.removeAll(setOfObservedMethods);
try {
// read the current class and use a
// ProxySubclassHierarchyAdapter
// to process only methods on that class that are in the list
ClassLoader loader = currentlyAnalysedClass.getClassLoader();
if (loader == null) {
loader = this.loader;
}
InputStream is = loader.getResourceAsStream(currentlyAnalysedClass
.getName().replaceAll("\\.", "/")
+ ".class");
if (is == null) {
//use SystemModuleClassLoader as fallback
ClassLoader classLoader = new SystemModuleClassLoader();
is = classLoader.getResourceAsStream(currentlyAnalysedClass
.getName().replaceAll("\\.", "/")
+ ".class");
}
ClassReader cr = new ClassReader(is);
ClassVisitor hierarchyAdapter = new ProxySubclassHierarchyAdapter(this, setOfFoundMethods);
cr.accept(hierarchyAdapter, ClassReader.SKIP_DEBUG);
} catch (IOException e) {
throw new TypeNotPresentException(currentlyAnalysedClassName, e);
}
// now add the foundMethods to the overall list of observed methods
setOfObservedMethods.addAll(setOfFoundMethods);
// get the next class up in the hierarchy and go again
nextSuperClass = currentlyAnalysedClass.getSuperclass();
}
// we've finished looking at the superclass hierarchy
// set the fields for the immediate superclass of our dynamic subclass
setCurrentAnalysisClassFields(superclassClass);
// add the class static field
addClassStaticField(currentlyAnalysedClassName);
// we do the lowest class last because we are already visiting the class
// when in this adapter code
// now we are ready to visit all the methods on the lowest class
// which will happen by the ASM ClassVisitor implemented in this adapter
LOGGER.debug(Constants.LOG_EXIT, "visit");
}
public void visitSource(String source, String debug)
{
LOGGER.debug(Constants.LOG_ENTRY, "visitSource", new Object[] { source, debug });
// set the source to null since the class is generated on the fly and
// not compiled
cv.visitSource(null, null);
LOGGER.debug(Constants.LOG_EXIT, "visitSource");
}
public void visitEnd()
{
LOGGER.debug(Constants.LOG_ENTRY, "visitEnd");
// this method is called when we reach the end of the class
// so it is time to make sure the static initialiser method is closed
staticAdapter.returnValue();
staticAdapter.endMethod();
// now delegate to the cv
cv.visitEnd();
LOGGER.debug(Constants.LOG_EXIT, "visitEnd");
}
/*
* This method is called on each method of the superclass (and all parent
* classes up to Object) Each of these methods is visited in turn and the
* code here generates the byte code for the InvocationHandler to call the
* methods on the superclass.
*/
public MethodVisitor visitMethod(int access, String name, String desc, String signature,
String[] exceptions)
{
LOGGER.debug(Constants.LOG_ENTRY, "visitMethod", new Object[] { access, name, desc,
signature, exceptions });
/*
* Check the method access and handle the method types we don't want to
* copy: final methods (issue warnings if these are not methods from
* java.* classes) static methods (initialiser and others) private
* methods, constructors (for now we don't copy any constructors)
* everything else we process to proxy. Abstract methods should be made
* non-abstract so that they can be proxied.
*/
if((access & ACC_ABSTRACT) != 0) {
//If the method is abstract then it should not be in the concrete subclass!
access &= ~ACC_ABSTRACT;
}
LOGGER.debug("Method name: {} with descriptor: {}", name, desc);
MethodVisitor methodVisitorToReturn = null;
if (name.equals("<init>")) {
// we may need to do something extra with constructors later
// e.g. include bytecode for calling super with the same args
// since we currently rely on the super having a zero args
// constructor
// we need to issue an error if we don't find one
// for now we return null to ignore them
methodVisitorToReturn = null;
} else if (name.equals("<clinit>")) {
// don't copy static initialisers from the superclass into the new
// subclass
methodVisitorToReturn = null;
} else if ((access & ACC_FINAL) != 0) {
// since we check for final methods in the ProxySubclassGenerator we
// should never get here
methodVisitorToReturn = null;
} else if ((access & ACC_SYNTHETIC) != 0) {
// synthetic methods are generated by the compiler for covariance
// etc
// we shouldn't copy them or we will have duplicate methods
methodVisitorToReturn = null;
} else if ((access & ACC_PRIVATE) != 0) {
// don't copy private methods from the superclass
methodVisitorToReturn = null;
} else if ((access & ACC_STATIC) != 0) {
// don't copy static methods
methodVisitorToReturn = null;
} else if (!(((access & ACC_PUBLIC) != 0) || ((access & ACC_PROTECTED) != 0) || ((access & ACC_PRIVATE) != 0))) {
// the default (package) modifier value is 0, so by using & with any
// of the other
// modifier values and getting a result of zero means that we have
// default accessibility
// check the package in which the method is declared against the
// package
// where the generated subclass will be
// if they are the same process the method otherwise ignore it
if (currentlyAnalysedClass.getPackage().equals(superclassClass.getPackage())) {
processMethod(access, name, desc, signature, exceptions);
methodVisitorToReturn = null;
} else {
methodVisitorToReturn = null;
}
} else {
processMethod(access, name, desc, signature, exceptions);
// return null because we don't want the original method code from
// the superclass
methodVisitorToReturn = null;
}
LOGGER.debug(Constants.LOG_EXIT, "visitMethod", methodVisitorToReturn);
return methodVisitorToReturn;
}
private void processMethod(int access, String name, String desc, String signature,
String[] exceptions)
{
LOGGER.debug(Constants.LOG_ENTRY, "processMethod", new Object[] { access, name, desc,
signature, exceptions });
LOGGER.debug("Processing method: {} with descriptor {}", name, desc);
// identify the target method parameters and return type
Method currentTransformMethod = new Method(name, desc);
Type[] targetMethodParameters = currentTransformMethod.getArgumentTypes();
Type returnType = currentTransformMethod.getReturnType();
// we create a static field for each method we encounter with a name
// like method_parm1_parm2...
StringBuilder methodStaticFieldNameBuilder = new StringBuilder(name);
// for each a parameter get the name and add it to the field removing
// the dots first
for (Type t : targetMethodParameters) {
methodStaticFieldNameBuilder.append("_");
methodStaticFieldNameBuilder.append(t.getClassName().replaceAll("\\[\\]", "Array")
.replaceAll("\\.", ""));
}
String methodStaticFieldName = methodStaticFieldNameBuilder.toString();
// add a private static field for the method
cv.visitField(ACC_PRIVATE | ACC_STATIC, methodStaticFieldName, METHOD_TYPE.getDescriptor(),
null, null);
// visit the method using the class writer, delegated through the method
// visitor and generator
// modify the method access so that any native methods aren't
// described as native
// since they won't be native in proxy form
// also stop methods being marked synchronized on the proxy as they will
// be sync
// on the real object
int newAccess = access & (~ACC_NATIVE) & (~ACC_SYNCHRONIZED);
MethodVisitor mv = cv.visitMethod(newAccess, name, desc, signature, exceptions);
// use a GeneratorAdapter to build the invoke call directly in byte code
GeneratorAdapter methodAdapter = new GeneratorAdapter(mv, newAccess, name, desc);
/*
* Stage 1 creates the bytecode for adding the reflected method of the
* superclass to a static field in the subclass: private static Method
* methodName_parm1_parm2... = null; static{ methodName_parm1_parm2... =
* superClass.getDeclaredMethod(methodName,new Class[]{method args}; }
*
* Stage 2 is to call the ih.invoke(this,methodName_parm1_parm2,args) in
* the new subclass methods Stage 3 is to cast the return value to the
* correct type
*/
/*
* Stage 1 use superClass.getMethod(methodName,new Class[]{method args}
* from the Class object on the stack
*/
// load the static superclass Class onto the stack
staticAdapter.getStatic(newClassType, currentClassFieldName, CLASS_TYPE);
// push the method name string arg onto the stack
staticAdapter.push(name);
// create an array of the method parm class[] arg
staticAdapter.push(targetMethodParameters.length);
staticAdapter.newArray(CLASS_TYPE);
int index = 0;
for (Type t : targetMethodParameters) {
staticAdapter.dup();
staticAdapter.push(index);
switch (t.getSort())
{
case Type.BOOLEAN:
staticAdapter.getStatic(Type.getType(java.lang.Boolean.class), "TYPE", CLASS_TYPE);
break;
case Type.BYTE:
staticAdapter.getStatic(Type.getType(java.lang.Byte.class), "TYPE", CLASS_TYPE);
break;
case Type.CHAR:
staticAdapter.getStatic(Type.getType(java.lang.Character.class), "TYPE", CLASS_TYPE);
break;
case Type.DOUBLE:
staticAdapter.getStatic(Type.getType(java.lang.Double.class), "TYPE", CLASS_TYPE);
break;
case Type.FLOAT:
staticAdapter.getStatic(Type.getType(java.lang.Float.class), "TYPE", CLASS_TYPE);
break;
case Type.INT:
staticAdapter.getStatic(Type.getType(java.lang.Integer.class), "TYPE", CLASS_TYPE);
break;
case Type.LONG:
staticAdapter.getStatic(Type.getType(java.lang.Long.class), "TYPE", CLASS_TYPE);
break;
case Type.SHORT:
staticAdapter.getStatic(Type.getType(java.lang.Short.class), "TYPE", CLASS_TYPE);
break;
default:
case Type.OBJECT:
staticAdapter.push(t);
break;
}
staticAdapter.arrayStore(CLASS_TYPE);
index++;
}
// invoke the getMethod
staticAdapter.invokeVirtual(CLASS_TYPE, new Method("getDeclaredMethod", METHOD_TYPE,
new Type[] { STRING_TYPE, Type.getType(java.lang.Class[].class) }));
// store the reflected method in the static field
staticAdapter.putStatic(newClassType, methodStaticFieldName, METHOD_TYPE);
/*
* Stage 2 call the ih.invoke(this,supermethod,parms)
*/
// load this to get the ih field
methodAdapter.loadThis();
// load the invocation handler from the field (the location of the
// InvocationHandler.invoke)
methodAdapter.getField(newClassType, IH_FIELD, IH_TYPE);
// loadThis (the first arg of the InvocationHandler.invoke)
methodAdapter.loadThis();
// load the method to invoke (the second arg of the
// InvocationHandler.invoke)
methodAdapter.getStatic(newClassType, methodStaticFieldName, METHOD_TYPE);
// load all the method arguments onto the stack as an object array (the
// third arg of the InvocationHandler.invoke)
methodAdapter.loadArgArray();
// generate the invoke method
Method invocationHandlerInvokeMethod = new Method("invoke", OBJECT_TYPE, new Type[] {
OBJECT_TYPE, METHOD_TYPE, Type.getType(java.lang.Object[].class) });
// call the invoke method of the invocation handler
methodAdapter.invokeInterface(IH_TYPE, invocationHandlerInvokeMethod);
/*
* Stage 3 the returned object is now on the top of the stack We need to
* check the type and cast as necessary
*/
switch (returnType.getSort())
{
case Type.BOOLEAN:
methodAdapter.cast(OBJECT_TYPE, Type.getType(Boolean.class));
methodAdapter.unbox(Type.BOOLEAN_TYPE);
break;
case Type.BYTE:
methodAdapter.cast(OBJECT_TYPE, Type.getType(Byte.class));
methodAdapter.unbox(Type.BYTE_TYPE);
break;
case Type.CHAR:
methodAdapter.cast(OBJECT_TYPE, Type.getType(Character.class));
methodAdapter.unbox(Type.CHAR_TYPE);
break;
case Type.DOUBLE:
methodAdapter.cast(OBJECT_TYPE, Type.getType(Double.class));
methodAdapter.unbox(Type.DOUBLE_TYPE);
break;
case Type.FLOAT:
methodAdapter.cast(OBJECT_TYPE, Type.getType(Float.class));
methodAdapter.unbox(Type.FLOAT_TYPE);
break;
case Type.INT:
methodAdapter.cast(OBJECT_TYPE, Type.getType(Integer.class));
methodAdapter.unbox(Type.INT_TYPE);
break;
case Type.LONG:
methodAdapter.cast(OBJECT_TYPE, Type.getType(Long.class));
methodAdapter.unbox(Type.LONG_TYPE);
break;
case Type.SHORT:
methodAdapter.cast(OBJECT_TYPE, Type.getType(Short.class));
methodAdapter.unbox(Type.SHORT_TYPE);
break;
case Type.VOID:
methodAdapter.cast(OBJECT_TYPE, Type.getType(Void.class));
methodAdapter.unbox(Type.VOID_TYPE);
break;
default:
case Type.OBJECT:
// in this case check the cast and cast the object to the return
// type
methodAdapter.checkCast(returnType);
methodAdapter.cast(OBJECT_TYPE, returnType);
break;
}
// return the (appropriately cast) result of the invocation from the
// stack
methodAdapter.returnValue();
// end the method
methodAdapter.endMethod();
LOGGER.debug(Constants.LOG_EXIT, "processMethod");
}
private void addClassStaticField(String classBinaryName)
{
LOGGER.debug(Constants.LOG_ENTRY, "addClassStaticField",
new Object[] { classBinaryName });
currentClassFieldName = classBinaryName.replaceAll("\\.", "_");
/*
* use Class.forName on the superclass so we can reflectively find
* methods later
*
* produces bytecode for retrieving the superclass and storing in a
* private static field: private static Class superClass = null; static{
* superClass = Class.forName(superclass, true, TYPE_BEING_PROXIED.class.getClassLoader()); }
*/
// add a private static field for the superclass Class
cv.visitField(ACC_PRIVATE | ACC_STATIC, currentClassFieldName, CLASS_TYPE.getDescriptor(),
null, null);
// push the String arg for the Class.forName onto the stack
staticAdapter.push(classBinaryName);
//push the boolean arg for the Class.forName onto the stack
staticAdapter.push(true);
//get the classloader
staticAdapter.push(newClassType);
staticAdapter.invokeVirtual(CLASS_TYPE, new Method("getClassLoader", CLASSLOADER_TYPE, NO_ARGS));
// invoke the Class forName putting the Class on the stack
staticAdapter.invokeStatic(CLASS_TYPE, new Method("forName", CLASS_TYPE,
new Type[] { STRING_TYPE, Type.BOOLEAN_TYPE, CLASSLOADER_TYPE }));
// put the Class in the static field
staticAdapter.putStatic(newClassType, currentClassFieldName, CLASS_TYPE);
LOGGER.debug(Constants.LOG_ENTRY, "addClassStaticField");
}
private void setCurrentAnalysisClassFields(Class<?> aClass)
{
LOGGER.debug(Constants.LOG_ENTRY, "setCurrentAnalysisClassFields",
new Object[] { aClass });
currentlyAnalysedClassName = aClass.getName();
currentlyAnalysedClass = aClass;
LOGGER.debug(Constants.LOG_EXIT, "setCurrentAnalysisClassFields");
}
// we don't want to copy fields from the class into the proxy
public FieldVisitor visitField(int access, String name, String desc, String signature,
Object value)
{
return null;
}
// for now we don't do any processing in these methods
public AnnotationVisitor visitAnnotation(String desc, boolean visible)
{
return null;
}
public void visitAttribute(Attribute attr)
{
// no-op
}
public void visitInnerClass(String name, String outerName, String innerName, int access)
{
// no-op
}
public void visitOuterClass(String owner, String name, String desc)
{
// no-op
}
}