/* * 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.common; import static java.lang.String.format; import static org.apache.aries.proxy.impl.ProxyUtils.JAVA_CLASS_VERSION; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.Map.Entry; import java.util.concurrent.Callable; import org.apache.aries.proxy.InvocationListener; import org.apache.aries.proxy.UnableToProxyException; import org.apache.aries.proxy.impl.SystemModuleClassLoader; import org.apache.aries.proxy.impl.gen.Constants; import org.apache.aries.proxy.weaving.WovenProxy; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.commons.AdviceAdapter; import org.objectweb.asm.commons.GeneratorAdapter; import org.objectweb.asm.commons.Method; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This abstract superclass is responsible for providing proxy extensions to * classes being written. Classes processed by this adapter will implement * {@link WovenProxy}, and have a static initialiser that populates * {@link java.lang.reflect.Method} fields for use with the * {@link InvocationListener}. Known subclasses are WovenProxyAdapter, * used to weave classes being loaded by the framework, and InterfaceCombiningClassAdapter * which is used to dynamically create objects that implement multiple interfaces */ public abstract class AbstractWovenProxyAdapter extends ClassVisitor implements Opcodes { private static final Logger LOGGER = LoggerFactory .getLogger(AbstractWovenProxyAdapter.class); /** Access modifier for a public generated method */ private static final int PUBLIC_GENERATED_METHOD_ACCESS = ACC_PUBLIC | ACC_FINAL | ACC_SYNTHETIC; /** The internal name for Throwable */ static final String THROWABLE_INAME = Type.getInternalName(Throwable.class); /** * A static UUID for adding to our method names. * This must not change over time, otherwise uninstalling * and reinstalling the proxy component with a separate * API bundle will cause BIG trouble (NoSuchFieldError) * with subclasses that get woven by the "new" hook */ private static final String UU_ID = "04df3c80_2877_4f6c_99e2_5a25e11d5535"; /** A constant for No Args methods */ static final Type[] NO_ARGS = new Type[0]; /** The annotation types we should add to generated methods and fields */ private static final String[] annotationTypeDescriptors = new String[] { "Ljavax/persistence/Transient;" }; /** the name of the field used to store the {@link InvocationListener} */ protected static final String LISTENER_FIELD = "org_apache_aries_proxy_InvocationListener_" + UU_ID; /** the name of the field used to store the dispatcher */ public static final String DISPATCHER_FIELD = "woven_proxy_dispatcher_" + UU_ID; /* Useful ASM types */ /** The ASM type for the {@link InvocationListener} */ static final Type LISTENER_TYPE = Type.getType(InvocationListener.class); /** The ASM type for the dispatcher */ public static final Type DISPATCHER_TYPE = Type.getType(Callable.class); private static final Type CLASS_TYPE = Type.getType(Class.class); private static final Type CLASS_ARRAY_TYPE = Type.getType(Class[].class); private static final Type STRING_TYPE = Type.getType(String.class); public static final Type OBJECT_TYPE = Type.getType(Object.class); static final Type METHOD_TYPE = Type.getType(java.lang.reflect.Method.class); /** The {@link Type} of the {@link WovenProxy} interface */ static final Type WOVEN_PROXY_IFACE_TYPE = Type.getType(WovenProxy.class); private static final Type NPE_TYPE = Type.getType(NullPointerException.class); private static final Type[] DISPATCHER_LISTENER_METHOD_ARGS = new Type[] { DISPATCHER_TYPE, LISTENER_TYPE }; private static final Method ARGS_CONSTRUCTOR = new Method("<init>", Type.VOID_TYPE, DISPATCHER_LISTENER_METHOD_ARGS); private static final Method NO_ARGS_CONSTRUCTOR = new Method("<init>", Type.VOID_TYPE, NO_ARGS); private static final Method NPE_CONSTRUCTOR = new Method("<init>", Type.VOID_TYPE, new Type[] {STRING_TYPE}); // other new methods we will need static final Method getInovcationTargetMethod = new Method( "getInvocationTarget" + UU_ID, OBJECT_TYPE, NO_ARGS); static final Method listenerPreInvokeMethod = new Method("getListener" + UU_ID, OBJECT_TYPE, new Type[] { OBJECT_TYPE, Type.getType(java.lang.reflect.Method.class), Type.getType(Object[].class) }); /* Instance fields */ /** The type of this class */ protected final Type typeBeingWoven; /** The type of this class's super */ private Type superType; /** The {@link ClassLoader} loading this class */ private final ClassLoader loader; /** * A flag to indicate that we need to weave WovenProxy methods into this class */ private boolean implementWovenProxy = false; /** * A list of un-woven superclasses between this object and {@link Object}, * only populated for classes which will directly implement {@link WovenProxy}. * This list is then used to override any methods that would otherwise be missed * by the weaving process. */ protected final List<Class<?>> nonObjectSupers = new ArrayList<Class<?>>(); /** * Methods we have transformed and need to create static fields for. * Stored as field name to {@link TypeMethod} so we know which Class to reflect * them off */ protected final Map<String, TypeMethod> transformedMethods = new HashMap<String, TypeMethod>(); /** * A set of {@link Method} objects identifying the methods that are in this * class. This is used to prevent us duplicating methods copied from * {@link AbstractWovenProxyAdapter#nonObjectSupers} that are already overridden in * this class. */ private final Set<Method> knownMethods = new HashSet<Method>(); /** * If our super does not have a no-args constructor then we need to be clever * when writing our own constructor. */ private boolean superHasNoArgsConstructor = false; /** * If we have a no-args constructor then we can delegate there rather than * to a super no-args */ private boolean hasNoArgsConstructor = false; /** * If we have a no-args constructor then we can delegate there rather than * to a super no-args */ protected boolean isSerializable = false; /** * The default static initialization method where we will write the proxy init * code. If there is an existing <clinit> then we will change this and write a * static_init_UUID instead (see the overriden * {@link #visitMethod(int, String, String, String, String[])} * for where this swap happens). See also {@link #writeStaticInitMethod()} for * where the method is actually written. */ private Method staticInitMethod = new Method("<clinit>", Type.VOID_TYPE, NO_ARGS); /** * The default access flags for the staticInitMethod. If we find an existing * <clinit> then we will write a static_init_UUID method and add the ACC_PRIVATE_FLAG. * See the overriden {@link #visitMethod(int, String, String, String, String[])} * for where this flag is added. See also {@link #writeStaticInitMethod()} for * where the method is actually written. */ private int staticInitMethodFlags = ACC_SYNTHETIC | ACC_PRIVATE | ACC_STATIC; protected Type currentMethodDeclaringType; protected boolean currentMethodDeclaringTypeIsInterface; public static final boolean IS_AT_LEAST_JAVA_6 = JAVA_CLASS_VERSION >= Opcodes.V1_6; /** * Create a new adapter for the supplied class * * @param writer * The ClassWriter to delegate to * @param className * The name of this class * @param loader * The ClassLoader loading this class */ public AbstractWovenProxyAdapter(ClassVisitor writer, String className, ClassLoader loader) { super(Opcodes.ASM5, writer); typeBeingWoven = Type.getType("L" + className.replace('.', '/') + ";"); //By default we expect to see methods from a concrete class currentMethodDeclaringType = typeBeingWoven; currentMethodDeclaringTypeIsInterface = false; this.loader = loader; } public final 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 }); // always update to the most recent version of the JVM version = JAVA_CLASS_VERSION; superType = Type.getType("L" + superName + ";"); try { // we only want to implement WovenProxy once in the hierarchy. // It's best to do this as high up as possible so we check the // super. By loading it we may end up weaving it, but that's a good thing! Class<?> superClass = Class.forName(superName.replace('/', '.'), false, loader); isSerializable = Serializable.class.isAssignableFrom(superClass) || Arrays.asList(interfaces).contains(Type.getInternalName(Serializable.class)) || checkInterfacesForSerializability(interfaces); if (!!!WovenProxy.class.isAssignableFrom(superClass)) { // We have found a type we need to add WovenProxy information to implementWovenProxy = true; if(superClass != Object.class) { //If our superclass isn't Object, it means we didn't weave all the way //to the top of the hierarchy. This means we need to override all the //methods defined on our parent so that they can be intercepted! nonObjectSupers.add(superClass); Class<?> nextSuper = superClass.getSuperclass(); while(nextSuper != Object.class) { nonObjectSupers.add(nextSuper); nextSuper = nextSuper.getSuperclass(); } //Don't use reflection - it can be dangerous superHasNoArgsConstructor = superHasNoArgsConstructor(superName, name); } else { superHasNoArgsConstructor = true; } // re-work the interfaces list to include WovenProxy String[] interfacesPlusWovenProxy = new String[interfaces.length + 1]; System.arraycopy(interfaces, 0, interfacesPlusWovenProxy, 0, interfaces.length); interfacesPlusWovenProxy[interfaces.length] = WOVEN_PROXY_IFACE_TYPE.getInternalName(); // Write the class header including WovenProxy. cv.visit(version, access, name, signature, superName, interfacesPlusWovenProxy); } else { // Already has a woven proxy parent, but we still need to write the // header! cv.visit(version, access, name, signature, superName, interfaces); } } catch (ClassNotFoundException e) { // If this happens we're about to hit bigger trouble on verify, so we // should stop weaving and fail. Make sure we don't cause the hook to // throw an error though. UnableToProxyException u = new UnableToProxyException(name, e); cannotLoadSuperClassException(superName, u); } } private void cannotLoadSuperClassException(String superName, UnableToProxyException u) { String msg = format("Unable to load the super type %s for class %s.", superName.replace('/', '.'), typeBeingWoven.getClassName()); throw new RuntimeException(msg, u); } /** * This method allows us to determine whether a superclass has a * non-private no-args constructor without causing it to initialize. * This avoids a potential ClassCircularityError on Mac VMs if the * initialization references the subclass being woven. Odd, but seen * in the wild! */ private final boolean superHasNoArgsConstructor(String superName, String name) { ConstructorFinder cf = new ConstructorFinder(); try { InputStream is = loader.getResourceAsStream(superName +".class"); if(is == null) throw new IOException(); new ClassReader(is).accept(cf, ClassReader.SKIP_FRAMES + ClassReader.SKIP_DEBUG + ClassReader.SKIP_CODE); } catch (IOException ioe) { UnableToProxyException u = new UnableToProxyException(name, ioe); cannotLoadSuperClassException(superName, u); } return cf.hasNoArgsConstructor(); } private boolean checkInterfacesForSerializability(String[] interfaces) throws ClassNotFoundException { for(String iface : interfaces) { if(Serializable.class.isAssignableFrom(Class.forName( iface.replace('/', '.'), false, loader))) return true; } return false; } /** * This method is called on each method implemented on this object (but not * for superclass methods) Each of these methods is visited in turn and the * code here generates the byte code for the calls to the InovcationListener * around the existing method */ public final 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 }); Method currentMethod = new Method(name, desc); getKnownMethods().add(currentMethod); MethodVisitor methodVisitorToReturn = null; // Only weave "real" instance methods. Not constructors, initializers or // compiler generated ones. if ((access & (ACC_STATIC | ACC_PRIVATE | ACC_SYNTHETIC | ACC_NATIVE | ACC_BRIDGE)) == 0 && !!!name.equals("<init>") && !!!name.equals("<clinit>")) { // found a method we should weave //Create a field name and store it for later String methodStaticFieldName = "methodField" + getSanitizedUUIDString(); transformedMethods.put(methodStaticFieldName, new TypeMethod( currentMethodDeclaringType, currentMethod)); // Surround the MethodVisitor with our weaver so we can manipulate the code methodVisitorToReturn = getWeavingMethodVisitor(access, name, desc, signature, exceptions, currentMethod, methodStaticFieldName, currentMethodDeclaringType, currentMethodDeclaringTypeIsInterface); } else if (name.equals("<clinit>")){ //there is an existing clinit method, change the fields we use //to write our init code to static_init_UUID instead staticInitMethod = new Method("static_init_" + UU_ID, Type.VOID_TYPE, NO_ARGS); staticInitMethodFlags = staticInitMethodFlags | ACC_FINAL; methodVisitorToReturn = new AdviceAdapter(Opcodes.ASM5, cv.visitMethod(access, name, desc, signature, exceptions), access, name, desc){ @Override protected void onMethodEnter() { //add into the <clinit> a call to our synthetic static_init_UUID invokeStatic(typeBeingWoven, staticInitMethod); super.onMethodEnter(); } }; } else { if(currentMethod.getArgumentTypes().length == 0 && name.equals("<init>")) hasNoArgsConstructor = true; //This isn't a method we want to weave, so just get the default visitor methodVisitorToReturn = cv.visitMethod(access, name, desc, signature, exceptions); } LOGGER.debug(Constants.LOG_EXIT, "visitMethod", methodVisitorToReturn); return methodVisitorToReturn; } /** * Our class may claim to implement WovenProxy, but doesn't have any * implementations! We should fix this. */ public void visitEnd() { LOGGER.debug(Constants.LOG_ENTRY, "visitEnd"); for(Class<?> c : nonObjectSupers) { setCurrentMethodDeclaringType(Type.getType(c), false); try { readClass(c, new MethodCopyingClassAdapter(this, loader, c, typeBeingWoven, getKnownMethods(), transformedMethods)); } catch (IOException e) { String msg = format("Unexpected error processing %s when weaving %s.", c.getName(), typeBeingWoven.getClassName()); throw new RuntimeException(msg, e); } } // If we need to implement woven proxy in this class then write the methods if (implementWovenProxy) { writeFinalWovenProxyMethods(); } // this method is called when we reach the end of the class // so it is time to make sure the static initialiser method is written writeStaticInitMethod(); // Make sure we add the instance specific WovenProxy method to our class, // and give ourselves a constructor to use writeCreateNewProxyInstanceAndConstructor(); // now delegate to the cv cv.visitEnd(); LOGGER.debug(Constants.LOG_EXIT, "visitEnd"); } public Set<Method> getKnownMethods() { return knownMethods; } /** * Get the {@link MethodVisitor} that will weave a given method * @param access * @param name * @param desc * @param signature * @param exceptions * @param currentMethod * @param methodStaticFieldName * @return */ protected abstract MethodVisitor getWeavingMethodVisitor(int access, String name, String desc, String signature, String[] exceptions, Method currentMethod, String methodStaticFieldName, Type currentMethodDeclaringType, boolean currentMethodDeclaringTypeIsInterface); /** * Write the methods we need for wovenProxies on the highest supertype */ private final void writeFinalWovenProxyMethods() { // add private fields for the Callable<Object> dispatcher // and InvocationListener. These aren't static because we can have // multiple instances of the same proxy class. These should not be // serialized, or used in JPA or any other thing we can think of, // so we annotate them as necessary generateField(DISPATCHER_FIELD, Type.getDescriptor(Callable.class)); generateField(LISTENER_FIELD, Type.getDescriptor(InvocationListener.class)); // a general methodAdapter field that we will use to with GeneratorAdapters // to create the methods required to implement WovenProxy GeneratorAdapter methodAdapter; // add a method for unwrapping the dispatcher methodAdapter = getMethodGenerator(PUBLIC_GENERATED_METHOD_ACCESS, new Method( "org_apache_aries_proxy_weaving_WovenProxy_unwrap", DISPATCHER_TYPE, NO_ARGS)); // ///////////////////////////////////////////////////// // Implement the method // load this to get the field methodAdapter.loadThis(); // get the dispatcher field and return methodAdapter.getField(typeBeingWoven, DISPATCHER_FIELD, DISPATCHER_TYPE); methodAdapter.returnValue(); methodAdapter.endMethod(); // ///////////////////////////////////////////////////// // add a method for checking if the dispatcher is set methodAdapter = getMethodGenerator(PUBLIC_GENERATED_METHOD_ACCESS, new Method( "org_apache_aries_proxy_weaving_WovenProxy_isProxyInstance", Type.BOOLEAN_TYPE, NO_ARGS)); // ///////////////////////////////////////////////////// // Implement the method // load this to get the field methodAdapter.loadThis(); // make a label for return true Label returnTrueLabel = methodAdapter.newLabel(); // get the dispatcher field for the stack methodAdapter.getField(typeBeingWoven, DISPATCHER_FIELD, DISPATCHER_TYPE); // check if the dispatcher was non-null and goto return true if it was methodAdapter.ifNonNull(returnTrueLabel); methodAdapter.loadThis(); // get the listener field for the stack methodAdapter.getField(typeBeingWoven, LISTENER_FIELD, LISTENER_TYPE); // check if the listener field was non-null and goto return true if it was methodAdapter.ifNonNull(returnTrueLabel); // return false if we haven't jumped anywhere methodAdapter.push(false); methodAdapter.returnValue(); // mark the returnTrueLable methodAdapter.mark(returnTrueLabel); methodAdapter.push(true); methodAdapter.returnValue(); // end the method methodAdapter.endMethod(); // /////////////////////////////////////////////////////// } /** * We write createNewProxyInstance separately because it isn't final, and is * overridden on each class, we also write a constructor for this method to * use if we don't have one. */ private final void writeCreateNewProxyInstanceAndConstructor() { GeneratorAdapter methodAdapter = getMethodGenerator(ACC_PUBLIC, new Method( "org_apache_aries_proxy_weaving_WovenProxy_createNewProxyInstance", WOVEN_PROXY_IFACE_TYPE, DISPATCHER_LISTENER_METHOD_ARGS)); // ///////////////////////////////////////////////////// // Implement the method // Create and instantiate a new instance, then return it methodAdapter.newInstance(typeBeingWoven); methodAdapter.dup(); methodAdapter.loadArgs(); methodAdapter.invokeConstructor(typeBeingWoven, new Method("<init>", Type.VOID_TYPE, DISPATCHER_LISTENER_METHOD_ARGS)); methodAdapter.returnValue(); methodAdapter.endMethod(); ////////////////////////////////////////////////////////// // Write a protected no-args constructor for this class methodAdapter = getMethodGenerator(ACC_PROTECTED | ACC_SYNTHETIC, ARGS_CONSTRUCTOR); // ///////////////////////////////////////////////////// // Implement the constructor // For the top level supertype we need to invoke a no-args super, on object //if we have to if(implementWovenProxy) { methodAdapter.loadThis(); if (superHasNoArgsConstructor) methodAdapter.invokeConstructor(superType, NO_ARGS_CONSTRUCTOR); else { if(hasNoArgsConstructor) methodAdapter.invokeConstructor(typeBeingWoven, NO_ARGS_CONSTRUCTOR); else throw new RuntimeException(new UnableToProxyException(typeBeingWoven.getClassName(), String.format("The class %s and its superclass %s do not have no-args constructors and cannot be woven.", typeBeingWoven.getClassName(), superType.getClassName()))); } methodAdapter.loadThis(); methodAdapter.loadArg(0); methodAdapter.putField(typeBeingWoven, DISPATCHER_FIELD, DISPATCHER_TYPE); methodAdapter.loadThis(); methodAdapter.loadArg(1); methodAdapter.putField(typeBeingWoven, LISTENER_FIELD, LISTENER_TYPE); } else { //We just invoke the super with args methodAdapter.loadThis(); methodAdapter.loadArgs(); methodAdapter.invokeConstructor(superType, ARGS_CONSTRUCTOR); } //Throw an NPE if the dispatcher is null, return otherwise methodAdapter.loadArg(0); Label returnValue = methodAdapter.newLabel(); methodAdapter.ifNonNull(returnValue); methodAdapter.newInstance(NPE_TYPE); methodAdapter.dup(); methodAdapter.push("The dispatcher must never be null!"); methodAdapter.invokeConstructor(NPE_TYPE, NPE_CONSTRUCTOR); methodAdapter.throwException(); methodAdapter.mark(returnValue); methodAdapter.returnValue(); methodAdapter.endMethod(); ////////////////////////////////////////////////////////// } /** * Create fields and an initialiser for {@link java.lang.reflect.Method} * objects in our class */ private final void writeStaticInitMethod() { // we create a static field for each method we encounter with a *unique* // random name // since each method needs to be stored individually for (String methodStaticFieldName : transformedMethods.keySet()) { // add a private static field for the method cv.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL | ACC_SYNTHETIC, methodStaticFieldName, METHOD_TYPE.getDescriptor(), null, null) .visitEnd(); } GeneratorAdapter staticAdapter = new GeneratorAdapter(staticInitMethodFlags, staticInitMethod, null, null, cv); for (Entry<String, TypeMethod> entry : transformedMethods.entrySet()) { // Add some more code to the static initializer TypeMethod m = entry.getValue(); Type[] targetMethodParameters = m.method.getArgumentTypes(); String methodStaticFieldName = entry.getKey(); Label beginPopulate = staticAdapter.newLabel(); Label endPopulate = staticAdapter.newLabel(); Label catchHandler = staticAdapter.newLabel(); staticAdapter.visitTryCatchBlock(beginPopulate, endPopulate, catchHandler, THROWABLE_INAME); staticAdapter.mark(beginPopulate); staticAdapter.push(m.declaringClass); // push the method name string arg onto the stack staticAdapter.push(m.method.getName()); // 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); staticAdapter.push(t); staticAdapter.arrayStore(CLASS_TYPE); index++; } // invoke the getMethod staticAdapter.invokeVirtual(CLASS_TYPE, new Method("getDeclaredMethod", METHOD_TYPE, new Type[] { STRING_TYPE, CLASS_ARRAY_TYPE})); // store the reflected method in the static field staticAdapter.putStatic(typeBeingWoven, methodStaticFieldName, METHOD_TYPE); Label afterCatch = staticAdapter.newLabel(); staticAdapter.mark(endPopulate); staticAdapter.goTo(afterCatch); staticAdapter.mark(catchHandler); // We don't care about the exception, so pop it off staticAdapter.pop(); // store the reflected method in the static field staticAdapter.visitInsn(ACONST_NULL); staticAdapter.putStatic(typeBeingWoven, methodStaticFieldName, METHOD_TYPE); staticAdapter.mark(afterCatch); } staticAdapter.returnValue(); staticAdapter.endMethod(); } /** * Get a new UUID suitable for use in method and field names * * @return */ public static final String getSanitizedUUIDString() { return UUID.randomUUID().toString().replace('-', '_'); } /** * This method will read the bytes for the supplied {@link Class} using the * supplied ASM {@link ClassVisitor}, the reader will skip DEBUG, FRAMES and CODE. * @param c * @param adapter * @throws IOException */ public static void readClass(Class<?> c, ClassVisitor adapter) throws IOException { String className = c.getName().replace(".", "/") + ".class"; //Load the class bytes and copy methods across ClassLoader loader = c.getClassLoader(); if (loader == null) { //system class, use SystemModuleClassLoader as fallback loader = new SystemModuleClassLoader(); } ClassReader cReader = new ClassReader(loader.getResourceAsStream(className)); cReader.accept(adapter, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); } /** * Generate an instance field that should be "invisible" to normal code * * @param fieldName * @param fieldDescriptor */ private final void generateField(String fieldName, String fieldDescriptor) { FieldVisitor fv = cv.visitField(ACC_PROTECTED | ACC_TRANSIENT | ACC_SYNTHETIC | ACC_FINAL, fieldName, fieldDescriptor, null, null); for (String s : annotationTypeDescriptors) fv.visitAnnotation(s, true).visitEnd(); fv.visitEnd(); } /** * Get a generator for a method, this be annotated with the "invisibility" * annotations (and ensured synthetic) * * @param methodSignature * @return */ private final GeneratorAdapter getMethodGenerator(int access, Method method) { access = access | ACC_SYNTHETIC; GeneratorAdapter ga = new GeneratorAdapter(access, method, null, null, cv); for (String s : annotationTypeDescriptors) ga.visitAnnotation(s, true).visitEnd(); ga.visitCode(); return ga; } public final void setCurrentMethodDeclaringType(Type type, boolean isInterface) { currentMethodDeclaringType = type; currentMethodDeclaringTypeIsInterface = isInterface; } }