// 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 castFirstStackElement(MethodVisitor methodVisitor, Class<?> returnType) {
   if (returnType.isPrimitive()) {
     unboxType(methodVisitor, returnType);
   } else {
     methodVisitor.visitTypeInsn(CHECKCAST, Type.getInternalName(returnType));
   }
 }
  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);
  }
  protected void injectSetByIndex(
      ClassWriter classWriter, String targetClassName, List<Field> fields) {
    MethodVisitor methodVisitor =
        classWriter.visitMethod(
            ACC_PUBLIC,
            "set",
            "(Ljava/lang/Object;ILjava/lang/Object;)V",
            null,
            new String[] {getInternalName(ILLEGAL_ACCESS_EXCEPTION.getCanonicalName())});

    Boxer boxer = new Boxer(methodVisitor);

    methodVisitor.visitCode();
    methodVisitor.visitVarInsn(ILOAD, 2);

    int maxStack = 6;

    Label[] labels = new Label[fields.size()];
    Label errorLabel = new Label();

    for (int i = 0; i < fields.size(); i++) {
      labels[i] = new Label();
    }

    methodVisitor.visitTableSwitchInsn(0, labels.length - 1, errorLabel, labels);

    if (!fields.isEmpty()) {
      maxStack--;

      for (int i = 0; i < fields.size(); i++) {
        Field field = fields.get(i);
        Class<?> type = field.getType();
        Class<?> inputType = Boxer.wrap(type);
        String fieldDescriptor = Type.getDescriptor(type);
        String inputPath = getInternalName(inputType.getName());

        methodVisitor.visitLabel(labels[i]);

        if (i == 0) methodVisitor.visitFrame(F_APPEND, 1, new Object[] {targetClassName}, 0, null);
        else methodVisitor.visitFrame(F_SAME, 0, null, 0, null);

        if (isPublic(field) && !isFinal(field)) {
          methodVisitor.visitVarInsn(ALOAD, 1);
          methodVisitor.visitTypeInsn(CHECKCAST, targetClassName);
          methodVisitor.visitVarInsn(ALOAD, 3);

          if (!type.isPrimitive()) {
            methodVisitor.visitTypeInsn(CHECKCAST, inputPath);
          } else {
            boxer.unbox(Type.getType(type));
          }

          methodVisitor.visitFieldInsn(PUTFIELD, targetClassName, field.getName(), fieldDescriptor);
        } else {
          injectReflectiveSetter(methodVisitor);
        }

        methodVisitor.visitInsn(RETURN);
      }

      methodVisitor.visitLabel(errorLabel);
      methodVisitor.visitFrame(F_SAME, 0, null, 0, null);
    }

    injectException(methodVisitor, IllegalAccessException.class);
    methodVisitor.visitMaxs(maxStack, 4);
    methodVisitor.visitEnd();
  }