org.apache.aries.proxy.impl.common.AbstractWovenProxyAdapter.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.aries.proxy.impl.common.AbstractWovenProxyAdapter.java

Source

/*
 * 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 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.NLS;
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);
            throw new RuntimeException(NLS.MESSAGES.getMessage("cannot.load.superclass",
                    superName.replace('/', '.'), typeBeingWoven.getClassName()), 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);
            throw new RuntimeException(NLS.MESSAGES.getMessage("cannot.load.superclass",
                    superName.replace('/', '.'), typeBeingWoven.getClassName()), 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) {
                // This should never happen! <= famous last words (not)
                throw new RuntimeException(NLS.MESSAGES.getMessage("unexpected.error.processing.class", c.getName(),
                        typeBeingWoven.getClassName()), 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(),
                            NLS.MESSAGES.getMessage("type.lacking.no.arg.constructor",
                                    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();
        className = className.substring(className.lastIndexOf('.') + 1) + ".class";

        //Load the class bytes and copy methods across
        ClassReader cReader = new ClassReader(c.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;
    }
}