public Class<? extends T> generate() {
      visitor.visitEnd();

      byte[] bytecode = visitor.toByteArray();
      return DEFINE_CLASS_METHOD.invoke(
          type.getClassLoader(), typeName, bytecode, 0, bytecode.length);
    }
    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 addGetter(MetaBeanProperty property) throws Exception {
      MetaMethod getter = property.getGetter();

      // GENERATE private boolean <prop>Set;

      String flagName = String.format("%sSet", property.getName());
      visitor.visitField(
          Opcodes.ACC_PRIVATE, flagName, Type.BOOLEAN_TYPE.getDescriptor(), null, null);

      addConventionGetter(getter.getName(), flagName, property);

      String getterName = getter.getName();
      Class<?> returnType = getter.getReturnType();

      // If it's a boolean property, there can be get or is type variants.
      // If this class has both, decorate both.
      if (returnType.equals(Boolean.TYPE)) {
        boolean getterIsIsMethod = getterName.startsWith("is");
        String propertyNameComponent = getterName.substring(getterIsIsMethod ? 2 : 3);
        String alternativeGetterName =
            String.format("%s%s", getterIsIsMethod ? "get" : "is", propertyNameComponent);

        try {
          type.getMethod(alternativeGetterName);
          addConventionGetter(alternativeGetterName, flagName, property);
        } catch (NoSuchMethodException e) {
          // ignore, no method to override
        }
      }
    }
 private void addGetter(String methodName, String methodDescriptor, MethodCodeBody body)
     throws Exception {
   MethodVisitor methodVisitor =
       visitor.visitMethod(
           Opcodes.ACC_PUBLIC, methodName, methodDescriptor, null, new String[0]);
   methodVisitor.visitCode();
   body.add(methodVisitor);
   methodVisitor.visitInsn(Opcodes.ARETURN);
   methodVisitor.visitMaxs(0, 0);
   methodVisitor.visitEnd();
 }
 private void addSetter(Method method, MethodCodeBody body) throws Exception {
   String methodDescriptor = Type.getMethodDescriptor(method);
   MethodVisitor methodVisitor =
       visitor.visitMethod(
           Opcodes.ACC_PUBLIC, method.getName(), methodDescriptor, null, new String[0]);
   methodVisitor.visitCode();
   body.add(methodVisitor);
   methodVisitor.visitInsn(Opcodes.RETURN);
   methodVisitor.visitMaxs(0, 0);
   methodVisitor.visitEnd();
 }
    public void startClass(boolean isConventionAware) {
      List<String> interfaceTypes = new ArrayList<String>();
      if (isConventionAware) {
        interfaceTypes.add(conventionAwareType.getInternalName());
      }
      interfaceTypes.add(dynamicObjectAwareType.getInternalName());
      interfaceTypes.add(extensionAwareType.getInternalName());
      interfaceTypes.add(hasConventionType.getInternalName());
      interfaceTypes.add(groovyObjectType.getInternalName());

      visitor.visit(
          Opcodes.V1_5,
          Opcodes.ACC_PUBLIC,
          generatedType.getInternalName(),
          null,
          superclassType.getInternalName(),
          interfaceTypes.toArray(new String[interfaceTypes.size()]));
    }
    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 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 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);
            }
          });
    }
    public void mixInGroovyObject() throws Exception {

      // GENERATE private MetaClass metaClass =
      // GroovySystem.getMetaClassRegistry().getMetaClass(getClass())

      final String metaClassFieldSignature =
          "L" + MetaClass.class.getName().replaceAll("\\.", "/") + ";";
      visitor.visitField(Opcodes.ACC_PRIVATE, "metaClass", metaClassFieldSignature, null, null);

      initMetaClass =
          new MethodCodeBody() {
            public void add(MethodVisitor visitor) throws Exception {
              visitor.visitVarInsn(Opcodes.ALOAD, 0);

              // GroovySystem.getMetaClassRegistry()
              String getMetaClassRegistryDesc =
                  Type.getMethodDescriptor(
                      GroovySystem.class.getDeclaredMethod("getMetaClassRegistry"));
              visitor.visitMethodInsn(
                  Opcodes.INVOKESTATIC,
                  Type.getType(GroovySystem.class).getInternalName(),
                  "getMetaClassRegistry",
                  getMetaClassRegistryDesc);

              // this.getClass()
              visitor.visitVarInsn(Opcodes.ALOAD, 0);
              String getClassDesc =
                  Type.getMethodDescriptor(Object.class.getDeclaredMethod("getClass"));
              visitor.visitMethodInsn(
                  Opcodes.INVOKEVIRTUAL,
                  Type.getType(Object.class).getInternalName(),
                  "getClass",
                  getClassDesc);

              // getMetaClass(..)
              String getMetaClassDesc =
                  Type.getMethodDescriptor(
                      MetaClassRegistry.class.getDeclaredMethod("getMetaClass", Class.class));
              visitor.visitMethodInsn(
                  Opcodes.INVOKEINTERFACE,
                  Type.getType(MetaClassRegistry.class).getInternalName(),
                  "getMetaClass",
                  getMetaClassDesc);

              visitor.visitFieldInsn(
                  Opcodes.PUTFIELD,
                  generatedType.getInternalName(),
                  "metaClass",
                  metaClassFieldSignature);
            }
          };

      // GENERATE public MetaClass getMetaClass() { return metaClass }

      addGetter(
          GroovyObject.class.getDeclaredMethod("getMetaClass"),
          new MethodCodeBody() {
            public void add(MethodVisitor visitor) throws Exception {
              visitor.visitVarInsn(Opcodes.ALOAD, 0);
              visitor.visitFieldInsn(
                  Opcodes.GETFIELD,
                  generatedType.getInternalName(),
                  "metaClass",
                  metaClassFieldSignature);
            }
          });

      // GENERATE public void setMetaClass(MetaClass class) { this.metaClass = class; }

      addSetter(
          GroovyObject.class.getDeclaredMethod("setMetaClass", MetaClass.class),
          new MethodCodeBody() {
            public void add(MethodVisitor visitor) throws Exception {
              visitor.visitVarInsn(Opcodes.ALOAD, 0);
              visitor.visitVarInsn(Opcodes.ALOAD, 1);
              visitor.visitFieldInsn(
                  Opcodes.PUTFIELD,
                  generatedType.getInternalName(),
                  "metaClass",
                  metaClassFieldSignature);
            }
          });
    }
    public void mixInConventionAware() throws Exception {
      // GENERATE private ConventionMapping mapping = new ConventionAwareHelper(this,
      // getConvention())

      final String mappingFieldSignature =
          "L" + ConventionMapping.class.getName().replaceAll("\\.", "/") + ";";
      final String getConventionDesc = Type.getMethodDescriptor(conventionType, new Type[0]);

      visitor.visitField(Opcodes.ACC_PRIVATE, "mapping", mappingFieldSignature, null, null);

      initConventionAwareHelper =
          new MethodCodeBody() {
            public void add(MethodVisitor visitor) throws Exception {
              Type helperType = Type.getType(ConventionAwareHelper.class);

              // GENERATE mapping = new ConventionAwareHelper(this, getConvention())
              visitor.visitVarInsn(Opcodes.ALOAD, 0);

              visitor.visitTypeInsn(Opcodes.NEW, helperType.getInternalName());
              visitor.visitInsn(Opcodes.DUP);
              visitor.visitVarInsn(Opcodes.ALOAD, 0);

              // GENERATE getConvention()

              visitor.visitVarInsn(Opcodes.ALOAD, 0);
              visitor.visitMethodInsn(
                  Opcodes.INVOKEVIRTUAL,
                  generatedType.getInternalName(),
                  "getConvention",
                  getConventionDesc);

              // END

              String constructorDesc =
                  Type.getMethodDescriptor(
                      Type.VOID_TYPE, new Type[] {conventionAwareType, conventionType});
              visitor.visitMethodInsn(
                  Opcodes.INVOKESPECIAL, helperType.getInternalName(), "<init>", constructorDesc);

              visitor.visitFieldInsn(
                  Opcodes.PUTFIELD,
                  generatedType.getInternalName(),
                  "mapping",
                  mappingFieldSignature);

              // END
            }
          };

      // END

      // GENERATE public ConventionMapping getConventionMapping() { if (mapping != null) { return
      // mapping; } else { return new ConventionAwareHelper(this); } }
      // the null check is for when getConventionMapping() is called by a superclass constructor (eg
      // when the constructor calls a getter method)

      addGetter(
          IConventionAware.class.getDeclaredMethod("getConventionMapping"),
          new MethodCodeBody() {
            public void add(MethodVisitor visitor) {
              // GENERATE if (mapping != null) {... }
              visitor.visitVarInsn(Opcodes.ALOAD, 0);
              visitor.visitFieldInsn(
                  Opcodes.GETFIELD,
                  generatedType.getInternalName(),
                  "mapping",
                  mappingFieldSignature);
              visitor.visitInsn(Opcodes.DUP);
              Label nullBranch = new Label();
              visitor.visitJumpInsn(Opcodes.IFNULL, nullBranch);
              visitor.visitInsn(Opcodes.ARETURN);
              // GENERATE else { return new ConventionAwareHelper(this); }
              visitor.visitLabel(nullBranch);
              Type conventionAwareHelperType = Type.getType(ConventionAwareHelper.class);
              String constructorDesc =
                  Type.getMethodDescriptor(
                      Type.VOID_TYPE, new Type[] {Type.getType(IConventionAware.class)});
              visitor.visitTypeInsn(Opcodes.NEW, conventionAwareHelperType.getInternalName());
              visitor.visitInsn(Opcodes.DUP);
              visitor.visitVarInsn(Opcodes.ALOAD, 0);
              visitor.visitMethodInsn(
                  Opcodes.INVOKESPECIAL,
                  conventionAwareHelperType.getInternalName(),
                  "<init>",
                  constructorDesc);
              visitor.visitInsn(Opcodes.ARETURN);
            }
          });
    }
    public void mixInDynamicAware() throws Exception {
      final Type helperType = Type.getType(MixInExtensibleDynamicObject.class);

      // GENERATE private MixInExtensibleDynamicObject dynamicObjectHelper = new
      // MixInExtensibleDynamicObject(this, super.getAsDynamicObject())

      final String fieldSignature =
          "L" + MixInExtensibleDynamicObject.class.getName().replaceAll("\\.", "/") + ";";
      visitor.visitField(Opcodes.ACC_PRIVATE, "dynamicObjectHelper", fieldSignature, null, null);
      initDynamicObjectHelper =
          new MethodCodeBody() {
            public void add(MethodVisitor visitor) throws Exception {
              String helperTypeConstructorDesc =
                  Type.getMethodDescriptor(
                      Type.VOID_TYPE, new Type[] {Type.getType(Object.class), dynamicObjectType});

              // GENERATE dynamicObjectHelper = new MixInExtensibleDynamicObject(this,
              // super.getAsDynamicObject())

              visitor.visitVarInsn(Opcodes.ALOAD, 0);

              // GENERATE new MixInExtensibleDynamicObject(this, super.getAsDynamicObject())
              visitor.visitTypeInsn(Opcodes.NEW, helperType.getInternalName());
              visitor.visitInsn(Opcodes.DUP);

              visitor.visitVarInsn(Opcodes.ALOAD, 0);

              boolean useInheritedDynamicObject =
                  GroovySystem.getMetaClassRegistry()
                          .getMetaClass(type)
                          .pickMethod("getAsDynamicObject", new Class[0])
                      != null;

              if (useInheritedDynamicObject) {
                // GENERATE super.getAsDynamicObject()
                visitor.visitVarInsn(Opcodes.ALOAD, 0);
                visitor.visitMethodInsn(
                    Opcodes.INVOKESPECIAL,
                    Type.getType(type).getInternalName(),
                    "getAsDynamicObject",
                    Type.getMethodDescriptor(dynamicObjectType, new Type[0]));
              } else {
                // GENERATE null
                visitor.visitInsn(Opcodes.ACONST_NULL);
              }

              visitor.visitMethodInsn(
                  Opcodes.INVOKESPECIAL,
                  helperType.getInternalName(),
                  "<init>",
                  helperTypeConstructorDesc);
              // END

              visitor.visitFieldInsn(
                  Opcodes.PUTFIELD,
                  generatedType.getInternalName(),
                  "dynamicObjectHelper",
                  fieldSignature);
              // END
            }
          };

      // END

      // GENERATE public Convention getConvention() { return dynamicObjectHelper.getConvention(); }

      addGetter(
          HasConvention.class.getDeclaredMethod("getConvention"),
          new MethodCodeBody() {
            public void add(MethodVisitor visitor) throws Exception {

              // GENERATE dynamicObjectHelper.getConvention()

              visitor.visitVarInsn(Opcodes.ALOAD, 0);
              visitor.visitFieldInsn(
                  Opcodes.GETFIELD,
                  generatedType.getInternalName(),
                  "dynamicObjectHelper",
                  fieldSignature);
              String getterDescriptor =
                  Type.getMethodDescriptor(
                      ExtensibleDynamicObject.class.getDeclaredMethod("getConvention"));
              visitor.visitMethodInsn(
                  Opcodes.INVOKEVIRTUAL,
                  helperType.getInternalName(),
                  "getConvention",
                  getterDescriptor);
            }
          });

      // END

      // GENERATE public ExtensionContainer getExtensions() { return getConvention(); }

      addGetter(
          ExtensionAware.class.getDeclaredMethod("getExtensions"),
          new MethodCodeBody() {
            public void add(MethodVisitor visitor) throws Exception {

              // GENERATE getConvention()

              visitor.visitVarInsn(Opcodes.ALOAD, 0);
              String getterDescriptor =
                  Type.getMethodDescriptor(
                      ExtensibleDynamicObject.class.getDeclaredMethod("getConvention"));
              visitor.visitMethodInsn(
                  Opcodes.INVOKEVIRTUAL,
                  generatedType.getInternalName(),
                  "getConvention",
                  getterDescriptor);
            }
          });

      // END

      // GENERATE public DynamicObject.getAsDynamicObject() { return dynamicObjectHelper; }

      addGetter(
          DynamicObjectAware.class.getDeclaredMethod("getAsDynamicObject"),
          new MethodCodeBody() {
            public void add(MethodVisitor visitor) {

              // GENERATE dynamicObjectHelper

              visitor.visitVarInsn(Opcodes.ALOAD, 0);
              visitor.visitFieldInsn(
                  Opcodes.GETFIELD,
                  generatedType.getInternalName(),
                  "dynamicObjectHelper",
                  fieldSignature);
            }
          });

      // END
    }