// 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 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 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 putMethodArgumentOnStack(MethodVisitor methodVisitor, Type type, int index) {
   methodVisitor.visitVarInsn(type.getOpcode(ILOAD), index);
 }
 private void putMethodArgumentOnStack(MethodVisitor methodVisitor, int index) {
   methodVisitor.visitVarInsn(ALOAD, index);
 }
 private void putThirdMethodArgumentOnStack(MethodVisitor methodVisitor) {
   methodVisitor.visitVarInsn(ALOAD, 3);
 }
 private void putSecondMethodArgumentOnStack(MethodVisitor methodVisitor) {
   methodVisitor.visitVarInsn(ALOAD, 2);
 }
 private void putFirstMethodArgumentOnStack(MethodVisitor methodVisitor, Type argType) {
   int loadCode = argType.getOpcode(ILOAD);
   methodVisitor.visitVarInsn(loadCode, 1);
 }