public void addSetMethod(MetaBeanProperty property) throws Exception {
      MetaMethod setter = property.getSetter();
      Type paramType = Type.getType(setter.getParameterTypes()[0].getTheClass());
      Type returnType = Type.getType(setter.getReturnType());
      String setterDescriptor = Type.getMethodDescriptor(returnType, new Type[] {paramType});

      // GENERATE public void <propName>(<type> v) { <setter>(v) }
      String setMethodDescriptor = Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {paramType});
      MethodVisitor methodVisitor =
          visitor.visitMethod(
              Opcodes.ACC_PUBLIC, property.getName(), setMethodDescriptor, null, new String[0]);
      methodVisitor.visitCode();

      // GENERATE <setter>(v)

      methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
      methodVisitor.visitVarInsn(paramType.getOpcode(Opcodes.ILOAD), 1);

      methodVisitor.visitMethodInsn(
          Opcodes.INVOKEVIRTUAL,
          generatedType.getInternalName(),
          setter.getName(),
          setterDescriptor);

      // END

      methodVisitor.visitInsn(Opcodes.RETURN);
      methodVisitor.visitMaxs(0, 0);
      methodVisitor.visitEnd();
    }
    public void addActionMethod(MetaMethod method) throws Exception {
      Type actionImplType = Type.getType(ClosureBackedAction.class);
      Type closureType = Type.getType(Closure.class);

      String methodDescriptor = Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {closureType});

      // GENERATE public void <method>(Closure v) { <method>(new ClosureBackedAction(v)); }
      MethodVisitor methodVisitor =
          visitor.visitMethod(
              Opcodes.ACC_PUBLIC, method.getName(), methodDescriptor, null, new String[0]);
      methodVisitor.visitCode();

      // GENERATE <method>(new ClosureBackedAction(v));
      methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);

      // GENERATE new ClosureBackedAction(v);
      methodVisitor.visitTypeInsn(Opcodes.NEW, actionImplType.getInternalName());
      methodVisitor.visitInsn(Opcodes.DUP);
      methodVisitor.visitVarInsn(Opcodes.ALOAD, 1);
      String constuctorDescriptor =
          Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {closureType});
      methodVisitor.visitMethodInsn(
          Opcodes.INVOKESPECIAL, actionImplType.getInternalName(), "<init>", constuctorDescriptor);

      methodDescriptor =
          Type.getMethodDescriptor(
              Type.getType(method.getReturnType()),
              new Type[] {Type.getType(method.getParameterTypes()[0].getTheClass())});
      methodVisitor.visitMethodInsn(
          Opcodes.INVOKEVIRTUAL,
          generatedType.getInternalName(),
          method.getName(),
          methodDescriptor);

      methodVisitor.visitInsn(Opcodes.RETURN);
      methodVisitor.visitMaxs(0, 0);
      methodVisitor.visitEnd();
    }
    public void addSetter(MetaBeanProperty property) throws Exception {
      MetaMethod setter = property.getSetter();

      // GENERATE public <return-type> <setter>(<type> v) { <return-type> v = super.<setter>(v);
      // <prop>Set = true; return v; }

      Type paramType = Type.getType(setter.getParameterTypes()[0].getTheClass());
      Type returnType = Type.getType(setter.getReturnType());
      String setterDescriptor = Type.getMethodDescriptor(returnType, new Type[] {paramType});
      MethodVisitor methodVisitor =
          visitor.visitMethod(
              Opcodes.ACC_PUBLIC, setter.getName(), setterDescriptor, null, new String[0]);
      methodVisitor.visitCode();

      // GENERATE super.<setter>(v)

      methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
      methodVisitor.visitVarInsn(paramType.getOpcode(Opcodes.ILOAD), 1);

      methodVisitor.visitMethodInsn(
          Opcodes.INVOKESPECIAL,
          superclassType.getInternalName(),
          setter.getName(),
          setterDescriptor);

      // END

      // GENERATE <prop>Set = true

      methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
      methodVisitor.visitLdcInsn(true);
      methodVisitor.visitFieldInsn(
          Opcodes.PUTFIELD,
          generatedType.getInternalName(),
          String.format("%sSet", property.getName()),
          Type.BOOLEAN_TYPE.getDescriptor());

      // END

      methodVisitor.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
      methodVisitor.visitMaxs(0, 0);
      methodVisitor.visitEnd();
    }
    public void addConstructor(Constructor<?> constructor) throws Exception {
      List<Type> paramTypes = new ArrayList<Type>();
      for (Class<?> paramType : constructor.getParameterTypes()) {
        paramTypes.add(Type.getType(paramType));
      }
      String methodDescriptor =
          Type.getMethodDescriptor(Type.VOID_TYPE, paramTypes.toArray(new Type[paramTypes.size()]));

      String signature = signature(constructor);

      MethodVisitor methodVisitor =
          visitor.visitMethod(
              Opcodes.ACC_PUBLIC, "<init>", methodDescriptor, signature, new String[0]);
      methodVisitor.visitCode();

      // this.super(p0 .. pn)
      methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
      for (int i = 0; i < constructor.getParameterTypes().length; i++) {
        methodVisitor.visitVarInsn(
            Type.getType(constructor.getParameterTypes()[i]).getOpcode(Opcodes.ILOAD), i + 1);
      }
      methodVisitor.visitMethodInsn(
          Opcodes.INVOKESPECIAL, superclassType.getInternalName(), "<init>", methodDescriptor);

      if (initDynamicObjectHelper != null) {
        initDynamicObjectHelper.add(methodVisitor);
      }
      if (initConventionAwareHelper != null) {
        initConventionAwareHelper.add(methodVisitor);
      }
      if (initMetaClass != null) {
        initMetaClass.add(methodVisitor);
      }

      methodVisitor.visitInsn(Opcodes.RETURN);
      methodVisitor.visitMaxs(0, 0);
      methodVisitor.visitEnd();
    }
    private void addConventionGetter(String getterName, String flagName, MetaBeanProperty property)
        throws Exception {
      // GENERATE public <type> <getter>() { return
      // (<type>)getConventionMapping().getConventionValue(super.<getter>(), '<prop>', <prop>Set); }
      MetaMethod getter = property.getGetter();

      Type returnType = Type.getType(getter.getReturnType());
      String methodDescriptor = Type.getMethodDescriptor(returnType, new Type[0]);
      MethodVisitor methodVisitor =
          visitor.visitMethod(
              Opcodes.ACC_PUBLIC, getterName, methodDescriptor, null, new String[0]);
      methodVisitor.visitCode();

      methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
      methodVisitor.visitMethodInsn(
          Opcodes.INVOKEINTERFACE,
          conventionAwareType.getInternalName(),
          "getConventionMapping",
          Type.getMethodDescriptor(conventionMappingType, new Type[0]));

      methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
      methodVisitor.visitMethodInsn(
          Opcodes.INVOKESPECIAL, superclassType.getInternalName(), getterName, methodDescriptor);

      Type boxedType = null;
      if (getter.getReturnType().isPrimitive()) {
        // Box value
        boxedType =
            Type.getType(JavaReflectionUtil.getWrapperTypeForPrimitiveType(getter.getReturnType()));
        String valueOfMethodDescriptor =
            Type.getMethodDescriptor(boxedType, new Type[] {returnType});
        methodVisitor.visitMethodInsn(
            Opcodes.INVOKESTATIC, boxedType.getInternalName(), "valueOf", valueOfMethodDescriptor);
      }

      methodVisitor.visitLdcInsn(property.getName());

      methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
      methodVisitor.visitFieldInsn(
          Opcodes.GETFIELD,
          generatedType.getInternalName(),
          flagName,
          Type.BOOLEAN_TYPE.getDescriptor());

      String getConventionValueDesc =
          Type.getMethodDescriptor(
              ConventionMapping.class.getMethod(
                  "getConventionValue", Object.class, String.class, Boolean.TYPE));
      methodVisitor.visitMethodInsn(
          Opcodes.INVOKEINTERFACE,
          conventionMappingType.getInternalName(),
          "getConventionValue",
          getConventionValueDesc);

      if (getter.getReturnType().isPrimitive()) {
        // Unbox value
        methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, boxedType.getInternalName());
        String valueMethodDescriptor = Type.getMethodDescriptor(returnType, new Type[0]);
        methodVisitor.visitMethodInsn(
            Opcodes.INVOKEVIRTUAL,
            boxedType.getInternalName(),
            getter.getReturnType().getName() + "Value",
            valueMethodDescriptor);
      } else {
        // Cast to return type
        methodVisitor.visitTypeInsn(
            Opcodes.CHECKCAST,
            getter.getReturnType().isArray()
                ? "[" + returnType.getElementType().getDescriptor()
                : returnType.getInternalName());
      }

      methodVisitor.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
      methodVisitor.visitMaxs(0, 0);
      methodVisitor.visitEnd();
    }
    public void addDynamicMethods() throws Exception {

      // GENERATE public Object getProperty(String name) { return
      // getAsDynamicObject().getProperty(name); }

      addGetter(
          GroovyObject.class.getDeclaredMethod("getProperty", String.class),
          new MethodCodeBody() {
            public void add(MethodVisitor methodVisitor) throws Exception {
              // GENERATE getAsDynamicObject().getProperty(name);

              methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
              String getAsDynamicObjectDesc =
                  Type.getMethodDescriptor(
                      DynamicObjectAware.class.getDeclaredMethod("getAsDynamicObject"));
              methodVisitor.visitMethodInsn(
                  Opcodes.INVOKEVIRTUAL,
                  generatedType.getInternalName(),
                  "getAsDynamicObject",
                  getAsDynamicObjectDesc);

              methodVisitor.visitVarInsn(Opcodes.ALOAD, 1);
              String getPropertyDesc =
                  Type.getMethodDescriptor(
                      DynamicObject.class.getDeclaredMethod("getProperty", String.class));
              methodVisitor.visitMethodInsn(
                  Opcodes.INVOKEINTERFACE,
                  dynamicObjectType.getInternalName(),
                  "getProperty",
                  getPropertyDesc);

              // END
            }
          });

      // GENERATE public boolean hasProperty(String name) { return
      // getAsDynamicObject().hasProperty(name) }

      String methodDescriptor =
          Type.getMethodDescriptor(
              Type.getType(Boolean.TYPE), new Type[] {Type.getType(String.class)});
      MethodVisitor methodVisitor =
          visitor.visitMethod(
              Opcodes.ACC_PUBLIC, "hasProperty", methodDescriptor, null, new String[0]);
      methodVisitor.visitCode();

      // GENERATE getAsDynamicObject().hasProperty(name);

      methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
      String getAsDynamicObjectDesc =
          Type.getMethodDescriptor(
              DynamicObjectAware.class.getDeclaredMethod("getAsDynamicObject"));
      methodVisitor.visitMethodInsn(
          Opcodes.INVOKEVIRTUAL,
          generatedType.getInternalName(),
          "getAsDynamicObject",
          getAsDynamicObjectDesc);

      methodVisitor.visitVarInsn(Opcodes.ALOAD, 1);
      String getPropertyDesc =
          Type.getMethodDescriptor(
              DynamicObject.class.getDeclaredMethod("hasProperty", String.class));
      methodVisitor.visitMethodInsn(
          Opcodes.INVOKEINTERFACE,
          dynamicObjectType.getInternalName(),
          "hasProperty",
          getPropertyDesc);

      // END
      methodVisitor.visitInsn(Opcodes.IRETURN);
      methodVisitor.visitMaxs(0, 0);
      methodVisitor.visitEnd();

      // GENERATE public void setProperty(String name, Object value) {
      // getAsDynamicObject().setProperty(name, value); }

      addSetter(
          GroovyObject.class.getDeclaredMethod("setProperty", String.class, Object.class),
          new MethodCodeBody() {
            public void add(MethodVisitor methodVisitor) throws Exception {
              // GENERATE getAsDynamicObject().setProperty(name, value)

              methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
              String getAsDynamicObjectDesc =
                  Type.getMethodDescriptor(
                      DynamicObjectAware.class.getDeclaredMethod("getAsDynamicObject"));
              methodVisitor.visitMethodInsn(
                  Opcodes.INVOKEVIRTUAL,
                  generatedType.getInternalName(),
                  "getAsDynamicObject",
                  getAsDynamicObjectDesc);

              methodVisitor.visitVarInsn(Opcodes.ALOAD, 1);
              methodVisitor.visitVarInsn(Opcodes.ALOAD, 2);
              String setPropertyDesc =
                  Type.getMethodDescriptor(
                      DynamicObject.class.getDeclaredMethod(
                          "setProperty", String.class, Object.class));
              methodVisitor.visitMethodInsn(
                  Opcodes.INVOKEINTERFACE,
                  dynamicObjectType.getInternalName(),
                  "setProperty",
                  setPropertyDesc);

              // END
            }
          });

      // GENERATE public Object invokeMethod(String name, Object params) { return
      // getAsDynamicObject().invokeMethod(name, (Object[])params); }

      addGetter(
          GroovyObject.class.getDeclaredMethod("invokeMethod", String.class, Object.class),
          new MethodCodeBody() {
            public void add(MethodVisitor methodVisitor) throws Exception {
              String invokeMethodDesc =
                  Type.getMethodDescriptor(
                      Type.getType(Object.class),
                      new Type[] {Type.getType(String.class), Type.getType(Object[].class)});
              String objArrayDesc = Type.getType(Object[].class).getDescriptor();

              // GENERATE getAsDynamicObject().invokeMethod(name, (args instanceof Object[]) ? args
              // : new Object[] { args })

              methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
              String getAsDynamicObjectDesc =
                  Type.getMethodDescriptor(
                      DynamicObjectAware.class.getDeclaredMethod("getAsDynamicObject"));
              methodVisitor.visitMethodInsn(
                  Opcodes.INVOKEVIRTUAL,
                  generatedType.getInternalName(),
                  "getAsDynamicObject",
                  getAsDynamicObjectDesc);

              methodVisitor.visitVarInsn(Opcodes.ALOAD, 1);

              // GENERATE (args instanceof Object[]) ? args : new Object[] { args }
              methodVisitor.visitVarInsn(Opcodes.ALOAD, 2);
              methodVisitor.visitTypeInsn(Opcodes.INSTANCEOF, objArrayDesc);
              Label end = new Label();
              Label notArray = new Label();
              methodVisitor.visitJumpInsn(Opcodes.IFEQ, notArray);

              // Generate args
              //                            methodVisitor.visitInsn(Opcodes.ACONST_NULL);
              methodVisitor.visitVarInsn(Opcodes.ALOAD, 2);
              methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, objArrayDesc);
              methodVisitor.visitJumpInsn(Opcodes.GOTO, end);

              // Generate new Object[] { args }
              methodVisitor.visitLabel(notArray);
              methodVisitor.visitInsn(Opcodes.ICONST_1);
              methodVisitor.visitTypeInsn(
                  Opcodes.ANEWARRAY, Type.getType(Object.class).getInternalName());
              methodVisitor.visitInsn(Opcodes.DUP);
              methodVisitor.visitInsn(Opcodes.ICONST_0);
              methodVisitor.visitVarInsn(Opcodes.ALOAD, 2);
              methodVisitor.visitInsn(Opcodes.AASTORE);

              methodVisitor.visitLabel(end);

              methodVisitor.visitMethodInsn(
                  Opcodes.INVOKEINTERFACE,
                  dynamicObjectType.getInternalName(),
                  "invokeMethod",
                  invokeMethodDesc);
            }
          });
    }