content: /*
   * 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.Map.Entry;
  import java.util.Set;
  import java.util.UUID;
  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.ASM9, 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.ASM9, 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_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;
    }
  }
  