private void writeSetter(ClassVisitor visitor, Type generatedType, ModelProperty<?> property) {
    WeaklyTypeReferencingMethod<?, Void> weakSetter = property.getSetter();
    // There is no setter for this property
    if (weakSetter == null) {
      return;
    }

    String propertyName = property.getName();
    Class<?> propertyClass = property.getType().getConcreteClass();
    Type propertyType = Type.getType(propertyClass);
    Label calledOutsideOfConstructor = new Label();

    Method setter = weakSetter.getMethod();

    // the regular typed setter
    String methodDescriptor = Type.getMethodDescriptor(Type.VOID_TYPE, propertyType);
    MethodVisitor methodVisitor =
        declareMethod(
            visitor, setter.getName(), methodDescriptor, AsmClassGeneratorUtils.signature(setter));

    putCanCallSettersFieldValueOnStack(methodVisitor, generatedType);
    jumpToLabelIfStackEvaluatesToTrue(methodVisitor, calledOutsideOfConstructor);
    throwExceptionBecauseCalledOnItself(methodVisitor);

    methodVisitor.visitLabel(calledOutsideOfConstructor);
    putStateFieldValueOnStack(methodVisitor, generatedType);
    putConstantOnStack(methodVisitor, propertyName);
    putFirstMethodArgumentOnStack(methodVisitor, propertyType);
    if (propertyClass.isPrimitive()) {
      boxType(methodVisitor, propertyClass);
    }
    invokeStateSetMethod(methodVisitor);

    finishVisitingMethod(methodVisitor);
  }
  private void writeConstructor(
      ClassVisitor visitor,
      Type generatedType,
      Type superclassType,
      StructSchema<?> delegateSchema) {
    String constructorDescriptor;
    Type delegateType;
    if (delegateSchema == null) {
      delegateType = null;
      constructorDescriptor = NO_DELEGATE_CONSTRUCTOR_DESCRIPTOR;
    } else {
      delegateType = Type.getType(delegateSchema.getType().getConcreteClass());
      constructorDescriptor =
          Type.getMethodDescriptor(
              Type.VOID_TYPE, MODEL_ELEMENT_STATE_TYPE, TYPE_CONVERTER_TYPE, delegateType);
    }
    MethodVisitor constructorVisitor =
        declareMethod(visitor, CONSTRUCTOR_NAME, constructorDescriptor, CONCRETE_SIGNATURE);

    invokeSuperConstructor(constructorVisitor, superclassType);
    assignStateField(constructorVisitor, generatedType);
    assignTypeConverterField(constructorVisitor, generatedType);
    if (delegateType != null) {
      assignDelegateField(constructorVisitor, generatedType, delegateType);
    }
    setCanCallSettersField(constructorVisitor, generatedType, true);
    finishVisitingMethod(constructorVisitor);
  }
 private void assignTypeConverterField(MethodVisitor constructorVisitor, Type generatedType) {
   putThisOnStack(constructorVisitor);
   putSecondMethodArgumentOnStack(constructorVisitor);
   constructorVisitor.visitFieldInsn(
       PUTFIELD,
       generatedType.getInternalName(),
       TYPE_CONVERTER_FIELD_NAME,
       TYPE_CONVERTER_TYPE.getDescriptor());
 }
 private void assignStateField(MethodVisitor constructorVisitor, Type generatedType) {
   putThisOnStack(constructorVisitor);
   putFirstMethodArgumentOnStack(constructorVisitor);
   constructorVisitor.visitFieldInsn(
       PUTFIELD,
       generatedType.getInternalName(),
       STATE_FIELD_NAME,
       MODEL_ELEMENT_STATE_TYPE.getDescriptor());
 }
 private void invokeSuperConstructor(MethodVisitor constructorVisitor, Type superclassType) {
   putThisOnStack(constructorVisitor);
   constructorVisitor.visitMethodInsn(
       INVOKESPECIAL,
       superclassType.getInternalName(),
       CONSTRUCTOR_NAME,
       Type.getMethodDescriptor(Type.VOID_TYPE),
       false);
 }
 private void invokeSuperMethod(MethodVisitor methodVisitor, Class<?> superClass, Method method) {
   putThisOnStack(methodVisitor);
   methodVisitor.visitMethodInsn(
       INVOKESPECIAL,
       Type.getInternalName(superClass),
       method.getName(),
       Type.getMethodDescriptor(method),
       false);
 }
 private void boxType(MethodVisitor methodVisitor, Class<?> primitiveType) {
   Class<?> boxedType = BOXED_TYPES.get(primitiveType);
   methodVisitor.visitMethodInsn(
       INVOKESTATIC,
       Type.getInternalName(boxedType),
       "valueOf",
       "(" + Type.getDescriptor(primitiveType) + ")" + Type.getDescriptor(boxedType),
       false);
 }
 private void assignDelegateField(
     MethodVisitor constructorVisitor, Type generatedType, Type delegateType) {
   putThisOnStack(constructorVisitor);
   putThirdMethodArgumentOnStack(constructorVisitor);
   constructorVisitor.visitFieldInsn(
       PUTFIELD,
       generatedType.getInternalName(),
       DELEGATE_FIELD_NAME,
       delegateType.getDescriptor());
 }
  private void throwExceptionBecauseCalledOnItself(MethodVisitor methodVisitor) {
    String exceptionInternalName = Type.getInternalName(UnsupportedOperationException.class);
    methodVisitor.visitTypeInsn(NEW, exceptionInternalName);
    methodVisitor.visitInsn(DUP);
    putConstantOnStack(methodVisitor, "Calling setters of a managed type on itself is not allowed");

    String constructorDescriptor = Type.getMethodDescriptor(Type.VOID_TYPE, STRING_TYPE);
    methodVisitor.visitMethodInsn(
        INVOKESPECIAL, exceptionInternalName, CONSTRUCTOR_NAME, constructorDescriptor, false);
    methodVisitor.visitInsn(ATHROW);
  }
 private void writeManagedTypeStaticField(
     Type generatedType, Class<?> managedTypeClass, MethodVisitor constructorVisitor) {
   constructorVisitor.visitLdcInsn(Type.getType(managedTypeClass));
   constructorVisitor.visitMethodInsn(
       INVOKESTATIC, MODELTYPE_INTERNAL_NAME, "of", MODELTYPE_OF_METHOD_DESCRIPTOR, false);
   constructorVisitor.visitFieldInsn(
       PUTSTATIC,
       generatedType.getInternalName(),
       MANAGED_TYPE_FIELD_NAME,
       Type.getDescriptor(ModelType.class));
 }
 private void writeDelegatedMethod(
     ClassVisitor visitor, Type generatedType, Type delegateType, Method method) {
   MethodVisitor methodVisitor =
       declareMethod(
           visitor,
           method.getName(),
           Type.getMethodDescriptor(method),
           AsmClassGeneratorUtils.signature(method));
   invokeDelegateMethod(methodVisitor, generatedType, delegateType, method);
   Class<?> returnType = method.getReturnType();
   finishVisitingMethod(methodVisitor, returnCode(Type.getType(returnType)));
 }
 private void declareClass(
     ClassVisitor visitor,
     Collection<String> interfaceInternalNames,
     Type generatedType,
     Type superclassType) {
   visitor.visit(
       V1_6,
       ACC_PUBLIC,
       generatedType.getInternalName(),
       null,
       superclassType.getInternalName(),
       Iterables.toArray(interfaceInternalNames, String.class));
 }
 private void invokeDelegateMethod(
     MethodVisitor methodVisitor, Type generatedType, Type delegateType, Method method) {
   putDelegateFieldValueOnStack(methodVisitor, generatedType, delegateType);
   Class<?>[] parameterTypes = method.getParameterTypes();
   for (int paramNo = 0; paramNo < parameterTypes.length; paramNo++) {
     putMethodArgumentOnStack(methodVisitor, Type.getType(parameterTypes[paramNo]), paramNo + 1);
   }
   methodVisitor.visitMethodInsn(
       INVOKEVIRTUAL,
       delegateType.getInternalName(),
       method.getName(),
       Type.getMethodDescriptor(method),
       false);
 }
 private void castFirstStackElement(MethodVisitor methodVisitor, Class<?> returnType) {
   if (returnType.isPrimitive()) {
     unboxType(methodVisitor, returnType);
   } else {
     methodVisitor.visitTypeInsn(CHECKCAST, Type.getInternalName(returnType));
   }
 }
 private FieldVisitor declareStaticField(ClassVisitor visitor, String name, Class<?> fieldClass) {
   return visitor.visitField(
       ACC_PRIVATE | ACC_FINAL | ACC_STATIC | ACC_SYNTHETIC,
       name,
       Type.getDescriptor(fieldClass),
       null,
       null);
 }
 private void setCanCallSettersField(
     MethodVisitor methodVisitor, Type generatedType, boolean canCallSetters) {
   putThisOnStack(methodVisitor);
   methodVisitor.visitLdcInsn(canCallSetters);
   methodVisitor.visitFieldInsn(
       PUTFIELD,
       generatedType.getInternalName(),
       CAN_CALL_SETTERS_FIELD_NAME,
       Type.BOOLEAN_TYPE.getDescriptor());
 }
  private void writeSetMethod(ClassVisitor visitor, Type generatedType, ModelProperty<?> property) {
    if (property.isWritable() && property.getSchema() instanceof ScalarValueSchema) {

      // TODO - should we support this?
      // Adds a void $propName(Object value) method that sets the value
      MethodVisitor methodVisitor =
          declareMethod(
              visitor,
              property.getName(),
              Type.getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE),
              null);
      putThisOnStack(methodVisitor);
      putFirstMethodArgumentOnStack(methodVisitor);
      methodVisitor.visitMethodInsn(
          INVOKEVIRTUAL,
          generatedType.getInternalName(),
          property.getSetter().getName(),
          Type.getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE),
          false);
      finishVisitingMethod(methodVisitor);
    }
  }
 private void writeDelegatingToString(
     ClassVisitor visitor, Type generatedType, Type delegateType) {
   MethodVisitor methodVisitor =
       declareMethod(visitor, "toString", TO_STRING_METHOD_DESCRIPTOR, CONCRETE_SIGNATURE);
   putDelegateFieldValueOnStack(methodVisitor, generatedType, delegateType);
   methodVisitor.visitMethodInsn(
       INVOKEVIRTUAL,
       delegateType.getInternalName(),
       "getDisplayName",
       TO_STRING_METHOD_DESCRIPTOR,
       false);
   finishVisitingMethod(methodVisitor, ARETURN);
 }
 private void unboxType(MethodVisitor methodVisitor, Class<?> primitiveClass) {
   // Float f = (Float) tmp
   // f==null?0:f.floatValue()
   Class<?> boxedType = BOXED_TYPES.get(primitiveClass);
   Type primitiveType = Type.getType(primitiveClass);
   methodVisitor.visitTypeInsn(CHECKCAST, Type.getInternalName(boxedType));
   methodVisitor.visitInsn(DUP);
   Label exit = new Label();
   Label elseValue = new Label();
   methodVisitor.visitJumpInsn(IFNONNULL, elseValue);
   methodVisitor.visitInsn(POP);
   pushDefaultValue(methodVisitor, primitiveClass);
   methodVisitor.visitJumpInsn(GOTO, exit);
   methodVisitor.visitLabel(elseValue);
   methodVisitor.visitMethodInsn(
       INVOKEVIRTUAL,
       Type.getInternalName(boxedType),
       primitiveClass.getSimpleName() + "Value",
       Type.getMethodDescriptor(primitiveType),
       false);
   methodVisitor.visitLabel(exit);
 }
  private void writeGetters(ClassVisitor visitor, Type generatedType, ModelProperty<?> property) {
    Class<?> propertyClass = property.getType().getConcreteClass();
    Type propertyType = Type.getType(propertyClass);
    Set<String> processedNames = Sets.newHashSet();
    for (WeaklyTypeReferencingMethod<?, ?> weakGetter : property.getGetters()) {
      Method getter = weakGetter.getMethod();
      if (!processedNames.add(getter.getName())) {
        continue;
      }
      MethodVisitor methodVisitor =
          declareMethod(
              visitor,
              getter.getName(),
              Type.getMethodDescriptor(propertyType),
              AsmClassGeneratorUtils.signature(getter));

      putStateFieldValueOnStack(methodVisitor, generatedType);
      putConstantOnStack(methodVisitor, property.getName());
      invokeStateGetMethod(methodVisitor);
      castFirstStackElement(methodVisitor, propertyClass);
      finishVisitingMethod(methodVisitor, returnCode(propertyType));
    }
  }
  // the overload of type Object for Groovy coercions:  public void setFoo(Object foo)
  private void createTypeConvertingSetter(
      ClassVisitor visitor, Type generatedType, ModelProperty<?> property) {
    if (!property.isWritable() || !(property.getSchema() instanceof ScalarValueSchema)) {
      return;
    }

    Class<?> propertyClass = property.getType().getConcreteClass();
    Type propertyType = Type.getType(propertyClass);
    Class<?> boxedClass =
        propertyClass.isPrimitive() ? BOXED_TYPES.get(propertyClass) : propertyClass;
    Type boxedType = Type.getType(boxedClass);

    Method setter = property.getSetter().getMethod();
    MethodVisitor methodVisitor =
        declareMethod(
            visitor,
            setter.getName(),
            SET_OBJECT_PROPERTY_DESCRIPTOR,
            SET_OBJECT_PROPERTY_DESCRIPTOR);

    putThisOnStack(methodVisitor);
    putTypeConverterFieldValueOnStack(methodVisitor, generatedType);

    // Object converted = $typeConverter.convert(foo, Float.class, false);
    methodVisitor.visitVarInsn(ALOAD, 1); // put var #1 ('foo') on the stack
    methodVisitor.visitLdcInsn(boxedType); // push the constant Class onto the stack
    methodVisitor.visitInsn(
        propertyClass.isPrimitive()
            ? ICONST_1
            : ICONST_0); // push int 1 or 0 (interpreted as true or false) onto the stack
    methodVisitor.visitMethodInsn(
        INVOKEINTERFACE,
        TYPE_CONVERTER_TYPE.getInternalName(),
        "convert",
        COERCE_TO_SCALAR_DESCRIPTOR,
        true);
    methodVisitor.visitTypeInsn(CHECKCAST, boxedType.getInternalName());

    if (propertyClass.isPrimitive()) {
      unboxType(methodVisitor, propertyClass);
    }

    // invoke the typed setter, popping 'this' and 'converted' from the stack
    methodVisitor.visitMethodInsn(
        INVOKEVIRTUAL,
        generatedType.getInternalName(),
        setter.getName(),
        Type.getMethodDescriptor(Type.VOID_TYPE, propertyType),
        false);
    finishVisitingMethod(methodVisitor);
  }
 private void writeHashCodeMethod(ClassVisitor visitor, Type generatedType) {
   MethodVisitor methodVisitor =
       declareMethod(visitor, "hashCode", HASH_CODE_METHOD_DESCRIPTOR, null);
   methodVisitor.visitVarInsn(ALOAD, 0);
   methodVisitor.visitMethodInsn(
       INVOKEVIRTUAL,
       generatedType.getInternalName(),
       "getBackingNode",
       GET_BACKING_NODE_METHOD_DESCRIPTOR,
       false);
   methodVisitor.visitMethodInsn(
       INVOKEINTERFACE, MUTABLE_MODEL_NODE_TYPE, "hashCode", HASH_CODE_METHOD_DESCRIPTOR, true);
   methodVisitor.visitInsn(IRETURN);
   finishVisitingMethod(methodVisitor, Opcodes.IRETURN);
 }
  private void writeToString(
      ClassVisitor visitor,
      Type generatedType,
      Class<?> viewClass,
      StructSchema<?> delegateSchema) {
    Method toStringMethod = getToStringMethod(viewClass);

    if (toStringMethod != null && !toStringMethod.getDeclaringClass().equals(Object.class)) {
      writeNonAbstractMethodWrapper(visitor, generatedType, viewClass, toStringMethod);
    } else if (delegateSchema != null && delegateSchema.hasProperty("displayName")) {
      writeDelegatingToString(
          visitor, generatedType, Type.getType(delegateSchema.getType().getConcreteClass()));
    } else {
      writeDefaultToString(visitor, generatedType);
    }
  }
  private void writeDelegateMethods(
      final ClassVisitor visitor,
      final Type generatedType,
      StructSchema<?> delegateSchema,
      Set<Class<?>> typesToDelegate) {
    Class<?> delegateClass = delegateSchema.getType().getConcreteClass();
    Type delegateType = Type.getType(delegateClass);
    Map<Equivalence.Wrapper<Method>, Map<Class<?>, Method>> methodsToDelegate = Maps.newHashMap();
    for (Class<?> typeToDelegate : typesToDelegate) {
      for (Method methodToDelegate : typeToDelegate.getMethods()) {
        if (ModelSchemaUtils.isIgnoredMethod(methodToDelegate)) {
          continue;
        }
        Equivalence.Wrapper<Method> methodKey = METHOD_EQUIVALENCE.wrap(methodToDelegate);
        Map<Class<?>, Method> methodsByReturnType = methodsToDelegate.get(methodKey);
        if (methodsByReturnType == null) {
          methodsByReturnType = Maps.newHashMap();
          methodsToDelegate.put(methodKey, methodsByReturnType);
        }
        methodsByReturnType.put(methodToDelegate.getReturnType(), methodToDelegate);
      }
    }
    Set<Equivalence.Wrapper<Method>> delegateMethodKeys =
        ImmutableSet.copyOf(
            Iterables.transform(
                Arrays.asList(delegateClass.getMethods()),
                new Function<Method, Equivalence.Wrapper<Method>>() {
                  @Override
                  public Equivalence.Wrapper<Method> apply(Method method) {
                    return METHOD_EQUIVALENCE.wrap(method);
                  }
                }));
    for (Map.Entry<Equivalence.Wrapper<Method>, Map<Class<?>, Method>> entry :
        methodsToDelegate.entrySet()) {
      Equivalence.Wrapper<Method> methodKey = entry.getKey();
      if (!delegateMethodKeys.contains(methodKey)) {
        continue;
      }

      Map<Class<?>, Method> methodsByReturnType = entry.getValue();
      for (Method methodToDelegate : methodsByReturnType.values()) {
        writeDelegatedMethod(visitor, generatedType, delegateType, methodToDelegate);
      }
    }
  }
 private void writeEqualsMethod(ClassVisitor cw, Type generatedType) {
   MethodVisitor methodVisitor =
       cw.visitMethod(Opcodes.ACC_PUBLIC, "equals", EQUALS_METHOD_DESCRIPTOR, null, null);
   methodVisitor.visitCode();
   methodVisitor.visitVarInsn(ALOAD, 0);
   methodVisitor.visitVarInsn(ALOAD, 1);
   Label notSameLabel = new Label();
   methodVisitor.visitJumpInsn(IF_ACMPNE, notSameLabel);
   methodVisitor.visitInsn(ICONST_1);
   methodVisitor.visitInsn(IRETURN);
   methodVisitor.visitLabel(notSameLabel);
   methodVisitor.visitVarInsn(ALOAD, 1);
   methodVisitor.visitTypeInsn(INSTANCEOF, MANAGED_INSTANCE_TYPE);
   Label notManagedInstanceLabel = new Label();
   methodVisitor.visitJumpInsn(IFNE, notManagedInstanceLabel);
   methodVisitor.visitInsn(ICONST_0);
   methodVisitor.visitInsn(IRETURN);
   methodVisitor.visitLabel(notManagedInstanceLabel);
   methodVisitor.visitVarInsn(ALOAD, 0);
   methodVisitor.visitMethodInsn(
       INVOKEVIRTUAL,
       generatedType.getInternalName(),
       "getBackingNode",
       GET_BACKING_NODE_METHOD_DESCRIPTOR,
       false);
   methodVisitor.visitVarInsn(ALOAD, 1);
   methodVisitor.visitTypeInsn(CHECKCAST, MANAGED_INSTANCE_TYPE);
   methodVisitor.visitMethodInsn(
       INVOKEINTERFACE,
       MANAGED_INSTANCE_TYPE,
       "getBackingNode",
       GET_BACKING_NODE_METHOD_DESCRIPTOR,
       true);
   methodVisitor.visitMethodInsn(
       INVOKEINTERFACE, MUTABLE_MODEL_NODE_TYPE, "equals", EQUALS_METHOD_DESCRIPTOR, true);
   finishVisitingMethod(methodVisitor, Opcodes.IRETURN);
 }
 private int returnCode(Type returnType) {
   return returnType.getOpcode(IRETURN);
 }
 private void putStaticFieldValueOnStack(
     MethodVisitor methodVisitor, Type generatedType, String name, Type fieldType) {
   methodVisitor.visitFieldInsn(
       GETSTATIC, generatedType.getInternalName(), name, fieldType.getDescriptor());
 }
 private void putFieldValueOnStack(
     MethodVisitor methodVisitor, Type generatedType, String name, Type fieldType) {
   putThisOnStack(methodVisitor);
   methodVisitor.visitFieldInsn(
       GETFIELD, generatedType.getInternalName(), name, fieldType.getDescriptor());
 }
 private void putMethodArgumentOnStack(MethodVisitor methodVisitor, Type type, int index) {
   methodVisitor.visitVarInsn(type.getOpcode(ILOAD), index);
 }
 private void putFirstMethodArgumentOnStack(MethodVisitor methodVisitor, Type argType) {
   int loadCode = argType.getOpcode(ILOAD);
   methodVisitor.visitVarInsn(loadCode, 1);
 }