Handle getLambdaAccessMethod(Handle implMethod) {
   if (!implMethod.getOwner().equals(className)) {
     return implMethod;
   }
   if (Flags.hasFlag(classAccess, ACC_INTERFACE)) {
     // the method will be relocated to a companion class
     return implMethod;
   }
   // TODO: do not generate an access method if the impl method is not private (probably not
   // implementable with a single pass)
   String name = "access$lambda$" + lambdaAccessToImplMethods.size();
   String desc = getLambdaAccessMethodDesc(implMethod);
   Handle accessMethod = new Handle(H_INVOKESTATIC, className, name, desc);
   lambdaAccessToImplMethods.put(accessMethod, implMethod);
   return accessMethod;
 }
 @Override
 public void visitEnd() {
   for (Map.Entry<Handle, Handle> entry : lambdaAccessToImplMethods.entrySet()) {
     Handle accessMethod = entry.getKey();
     Handle implMethod = entry.getValue();
     Bytecode.generateDelegateMethod(cv, ACC_STATIC | ACC_SYNTHETIC, accessMethod, implMethod);
   }
   super.visitEnd();
 }
  /**
   * Visit every class/interface this proxy should implement, and generate the appropriate bytecode
   * for delegation if available.
   *
   * @param clazz an class for which to generate bytecode
   */
  private void visitClass(final Class clazz) {
    Method[] methods = clazz.getDeclaredMethods();
    for (Method method : methods) {
      Class<?>[] exceptionTypes = method.getExceptionTypes();
      String[] exceptions = new String[exceptionTypes.length];
      for (int i = 0; i < exceptions.length; i++) {
        exceptions[i] = BytecodeHelper.getClassInternalName(exceptionTypes[i]);
      }
      // for each method defined in the class, generate the appropriate delegation bytecode
      visitMethod(
          method.getModifiers(),
          method.getName(),
          BytecodeHelper.getMethodDescriptor(method.getReturnType(), method.getParameterTypes()),
          null,
          exceptions);
    }
    Constructor[] constructors = clazz.getDeclaredConstructors();
    for (Constructor method : constructors) {
      Class<?>[] exceptionTypes = method.getExceptionTypes();
      String[] exceptions = new String[exceptionTypes.length];
      for (int i = 0; i < exceptions.length; i++) {
        exceptions[i] = BytecodeHelper.getClassInternalName(exceptionTypes[i]);
      }
      // for each method defined in the class, generate the appropriate delegation bytecode
      visitMethod(
          method.getModifiers(),
          "<init>",
          BytecodeHelper.getMethodDescriptor(Void.TYPE, method.getParameterTypes()),
          null,
          exceptions);
    }

    for (Class intf : clazz.getInterfaces()) {
      visitClass(intf);
    }
    Class superclass = clazz.getSuperclass();
    if (superclass != null) visitClass(superclass);

    // Ultimately, methods can be available in the closure map which are not defined by the
    // superclass
    // nor the interfaces
    for (Map.Entry<String, Boolean> entry : delegatedClosures.entrySet()) {
      Boolean visited = entry.getValue();
      if (!visited) {
        String name = entry.getKey();
        if (!"*".equals(name)) {
          // generate a new method
          visitMethod(ACC_PUBLIC, name, "([Ljava/lang/Object;)Ljava/lang/Object;", null, null);
        }
      }
    }
  }
 @Override
 public MethodVisitor visitMethod(
     final int access,
     final String name,
     final String desc,
     final String signature,
     final String[] exceptions) {
   Object key = Arrays.asList(name, desc);
   if (visitedMethods.contains(key)) return EMPTY_VISITOR;
   if (Modifier.isPrivate(access)
       || Modifier.isNative(access)
       || ((access & ACC_SYNTHETIC) != 0)) {
     // do not generate bytecode for private methods
     return EMPTY_VISITOR;
   }
   int accessFlags = access;
   visitedMethods.add(key);
   if ((objectDelegateMethods.contains(name)
           || delegatedClosures.containsKey(name)
           || (!"<init>".equals(name) && hasWildcard))
       && !Modifier.isStatic(access)
       && !Modifier.isFinal(access)) {
     if (!GROOVYOBJECT_METHOD_NAMESS.contains(name)) {
       if (Modifier.isAbstract(access)) {
         // prevents the proxy from being abstract
         accessFlags -= ACC_ABSTRACT;
       }
       if (delegatedClosures.containsKey(name) || (!"<init>".equals(name) && hasWildcard)) {
         delegatedClosures.put(name, Boolean.TRUE);
         return makeDelegateToClosureCall(name, desc, signature, exceptions, accessFlags);
       }
       if (generateDelegateField && objectDelegateMethods.contains(name)) {
         return makeDelegateCall(name, desc, signature, exceptions, accessFlags);
       }
       delegatedClosures.put(name, Boolean.TRUE);
       return makeDelegateToClosureCall(name, desc, signature, exceptions, accessFlags);
     }
   } else if ("<init>".equals(name)
       && (Modifier.isPublic(access) || Modifier.isProtected(access))) {
     return createConstructor(access, name, desc, signature, exceptions);
   } else if (Modifier.isAbstract(access) && !GROOVYOBJECT_METHOD_NAMESS.contains(name)) {
     accessFlags -= ACC_ABSTRACT;
     MethodVisitor mv = super.visitMethod(accessFlags, name, desc, signature, exceptions);
     mv.visitCode();
     Type[] args = Type.getArgumentTypes(desc);
     if (emptyBody) {
       Type returnType = Type.getReturnType(desc);
       if (returnType == Type.VOID_TYPE) {
         mv.visitInsn(RETURN);
       } else {
         int loadIns = getLoadInsn(returnType);
         switch (loadIns) {
           case ILOAD:
             mv.visitInsn(ICONST_0);
             break;
           case LLOAD:
             mv.visitInsn(LCONST_0);
             break;
           case FLOAD:
             mv.visitInsn(FCONST_0);
             break;
           case DLOAD:
             mv.visitInsn(DCONST_0);
             break;
           default:
             mv.visitInsn(ACONST_NULL);
         }
         mv.visitInsn(getReturnInsn(returnType));
         mv.visitMaxs(2, registerLen(args) + 1);
       }
     } else {
       // for compatibility with the legacy proxy generator, we should throw an
       // UnsupportedOperationException
       // instead of an AbtractMethodException
       mv.visitTypeInsn(NEW, "java/lang/UnsupportedOperationException");
       mv.visitInsn(DUP);
       mv.visitMethodInsn(
           INVOKESPECIAL, "java/lang/UnsupportedOperationException", "<init>", "()V");
       mv.visitInsn(ATHROW);
       mv.visitMaxs(2, registerLen(args) + 1);
     }
     mv.visitEnd();
   }
   return EMPTY_VISITOR;
 }
  /**
   * Construct a proxy generator. This generator is used when we need to create a proxy object for a
   * class or an interface given a map of closures.
   *
   * @param closureMap the delegates implementations
   * @param superClass corresponding to the superclass class visitor
   * @param interfaces extra interfaces the proxy should implement
   * @param proxyLoader the class loader which should be used to load the generated proxy
   * @param delegateClass if not null, generate a delegate field with the corresponding class
   * @param emptyBody if set to true, the unimplemented abstract methods will receive an empty body
   *     instead of throwing an {@link UnsupportedOperationException}.
   */
  public ProxyGeneratorAdapter(
      final Map<Object, Object> closureMap,
      final Class superClass,
      final Class[] interfaces,
      final ClassLoader proxyLoader,
      final boolean emptyBody,
      final Class delegateClass) {
    super(new ClassWriter(0));
    this.visitedMethods = new LinkedHashSet<Object>();
    this.delegatedClosures =
        closureMap.isEmpty() ? EMPTY_DELEGATECLOSURE_MAP : new HashMap<String, Boolean>();
    boolean wildcard = false;
    for (Map.Entry<Object, Object> entry : closureMap.entrySet()) {
      String name = entry.getKey().toString();
      if ("*".equals(name)) {
        wildcard = true;
      }
      this.delegatedClosures.put(name, Boolean.FALSE);
    }
    this.hasWildcard = wildcard;

    // if we have to delegate to another object, generate the appropriate delegate field
    // and collect the name of the methods for which delegation is active
    this.generateDelegateField = delegateClass != null;
    this.objectDelegateMethods =
        generateDelegateField
            ? createDelegateMethodList(delegateClass, interfaces)
            : EMPTY_STRING_SET;
    this.delegateClass = delegateClass;

    // a proxy is supposed to be a concrete class, so it cannot extend an interface.
    // If the provided superclass is an interface, then we replace the superclass with Object
    // and add this interface to the list of implemented interfaces
    boolean isSuperClassAnInterface = superClass.isInterface();
    this.superClass = isSuperClassAnInterface ? Object.class : superClass;

    // create the base list of classes which have possible methods to be overloaded
    this.classList = new LinkedList<Class>();
    this.classList.add(superClass);
    if (generateDelegateField) {
      classList.add(delegateClass);
    }
    if (interfaces != null) {
      Collections.addAll(this.classList, interfaces);
    }
    this.proxyName = proxyName();
    this.loader = proxyLoader != null ? new InnerLoader(proxyLoader) : findClassLoader(superClass);
    this.emptyBody = emptyBody;

    // generate bytecode
    ClassWriter writer = (ClassWriter) cv;
    ClassReader cr = createClassVisitor(Object.class);
    cr.accept(this, 0);
    byte[] b = writer.toByteArray();
    //        CheckClassAdapter.verify(new ClassReader(b), true, new PrintWriter(System.err));
    cachedClass = loader.defineClass(proxyName.replace('/', '.'), b);
    // cache no-arg constructor
    Class[] args =
        generateDelegateField ? new Class[] {Map.class, delegateClass} : new Class[] {Map.class};
    Constructor constructor;
    try {
      constructor = cachedClass.getConstructor(args);
    } catch (NoSuchMethodException e) {
      constructor = null;
    }
    cachedNoArgConstructor = constructor;
  }