private static <T> void appendApplyDelayedCellSetterN(
     DelayedCellSetterFactory<T, ?>[] delayedCellSetters, ClassWriter cw, String classType) {
   MethodVisitor mv;
   for (int i = 0; i < delayedCellSetters.length; i++) {
     if (delayedCellSetters[i] != null) {
       mv =
           cw.visitMethod(
               ACC_PRIVATE,
               "applyDelayedCellSetter" + i,
               "()V",
               null,
               new String[] {"java/lang/Exception"});
       mv.visitCode();
       mv.visitVarInsn(ALOAD, 0);
       mv.visitFieldInsn(
           GETFIELD, classType, "delayedCellSetter" + i, "L" + DELAYED_CELL_SETTER_TYPE + ";");
       mv.visitVarInsn(ALOAD, 0);
       mv.visitFieldInsn(GETFIELD, classType, "currentInstance", "Ljava/lang/Object;");
       mv.visitMethodInsn(
           INVOKEINTERFACE, DELAYED_CELL_SETTER_TYPE, "set", "(Ljava/lang/Object;)V", true);
       mv.visitInsn(RETURN);
       mv.visitMaxs(2, 1);
       mv.visitEnd();
     }
   }
 }
  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 static <T> void appendGetDelayedCellSetterSwitch(
      DelayedCellSetterFactory<T, ?>[] delayedCellSetters,
      String classType,
      MethodVisitor mv,
      int switchStart,
      int switchEnd) {
    mv.visitVarInsn(ILOAD, 1);
    Label defaultLabel = new Label();
    Label[] labels = newLabels(switchEnd - switchStart);
    mv.visitTableSwitchInsn(switchStart, switchEnd - 1, defaultLabel, labels);

    for (int i = switchStart; i < switchEnd; i++) {
      mv.visitLabel(labels[i - switchStart]);
      mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
      if (delayedCellSetters != null) {
        mv.visitVarInsn(ALOAD, 0);
        mv.visitFieldInsn(
            GETFIELD, classType, "delayedCellSetter" + i, "L" + DELAYED_CELL_SETTER_TYPE + ";");
      } else {
        mv.visitInsn(ACONST_NULL);
      }
      mv.visitInsn(ARETURN);
    }

    mv.visitLabel(defaultLabel);
    mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
  }
 protected void injectSetByName(ClassWriter classWriter) {
   MethodVisitor methodVisitor =
       classWriter.visitMethod(
           ACC_PUBLIC,
           "set",
           "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)V",
           null,
           new String[] {"java/lang/IllegalAccessException"});
   methodVisitor.visitCode();
   methodVisitor.visitVarInsn(ALOAD, 0);
   methodVisitor.visitVarInsn(ALOAD, 1);
   methodVisitor.visitVarInsn(ALOAD, 0);
   methodVisitor.visitVarInsn(ALOAD, 2);
   methodVisitor.visitMethodInsn(
       INVOKESPECIAL,
       getAccessorNameInternal(this.getTarget(), this.getAccessorType()),
       "getIndex",
       "(Ljava/lang/String;)I",
       false);
   methodVisitor.visitVarInsn(ALOAD, 3);
   methodVisitor.visitMethodInsn(
       INVOKEVIRTUAL,
       getAccessorNameInternal(this.getTarget(), this.getAccessorType()),
       "set",
       "(Ljava/lang/Object;ILjava/lang/Object;)V",
       false);
   methodVisitor.visitInsn(RETURN);
   methodVisitor.visitMaxs(4, 4);
   methodVisitor.visitEnd();
 }
 private static void callDelayedCellValue(MethodVisitor mv, String classType) {
   mv.visitVarInsn(ALOAD, 0);
   mv.visitVarInsn(ALOAD, 1);
   mv.visitVarInsn(ILOAD, 2);
   mv.visitVarInsn(ILOAD, 3);
   mv.visitVarInsn(ILOAD, 4);
   mv.visitMethodInsn(INVOKESPECIAL, classType, "_delayedCellValue", "([CIII)V", false);
 }
 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 writeNonAbstractMethodWrapper(
      ClassVisitor visitor, Type generatedType, Class<?> managedTypeClass, Method method) {
    Label start = new Label();
    Label end = new Label();
    Label handler = new Label();

    MethodVisitor methodVisitor = declareMethod(visitor, method);

    methodVisitor.visitTryCatchBlock(start, end, handler, null);

    setCanCallSettersField(methodVisitor, generatedType, false);

    methodVisitor.visitLabel(start);
    invokeSuperMethod(methodVisitor, managedTypeClass, method);
    methodVisitor.visitLabel(end);

    setCanCallSettersField(methodVisitor, generatedType, true);
    methodVisitor.visitInsn(ARETURN);

    methodVisitor.visitLabel(handler);
    setCanCallSettersField(methodVisitor, generatedType, true);
    methodVisitor.visitInsn(ATHROW);

    methodVisitor.visitMaxs(0, 0);
    methodVisitor.visitEnd();
  }
 private MethodVisitor declareMethod(
     ClassVisitor visitor,
     String methodName,
     String methodDescriptor,
     String methodSignature,
     int access) {
   MethodVisitor methodVisitor =
       visitor.visitMethod(access, methodName, methodDescriptor, methodSignature, NO_EXCEPTIONS);
   methodVisitor.visitCode();
   return methodVisitor;
 }
 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 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 writeDefaultToString(ClassVisitor visitor, Type generatedType) {
   MethodVisitor methodVisitor =
       declareMethod(visitor, "toString", TO_STRING_METHOD_DESCRIPTOR, CONCRETE_SIGNATURE);
   putStateFieldValueOnStack(methodVisitor, generatedType);
   methodVisitor.visitMethodInsn(
       INVOKEINTERFACE,
       MODEL_ELEMENT_STATE_TYPE_INTERNAL_NAME,
       "getDisplayName",
       TO_STRING_METHOD_DESCRIPTOR,
       true);
   finishVisitingMethod(methodVisitor, ARETURN);
 }
 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 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 castFirstStackElement(MethodVisitor methodVisitor, Class<?> returnType) {
   if (returnType.isPrimitive()) {
     unboxType(methodVisitor, returnType);
   } else {
     methodVisitor.visitTypeInsn(CHECKCAST, Type.getInternalName(returnType));
   }
 }
 private void invokeStateSetMethod(MethodVisitor methodVisitor) {
   methodVisitor.visitMethodInsn(
       INVOKEINTERFACE,
       MODEL_ELEMENT_STATE_TYPE_INTERNAL_NAME,
       "set",
       STATE_SET_METHOD_DESCRIPTOR,
       true);
 }
 private void writeManagedInstanceGetBackingNodeMethod(ClassVisitor visitor, Type generatedType) {
   MethodVisitor methodVisitor =
       declareMethod(
           visitor,
           "getBackingNode",
           GET_BACKING_NODE_METHOD_DESCRIPTOR,
           CONCRETE_SIGNATURE,
           ACC_PUBLIC | ACC_SYNTHETIC);
   putStateFieldValueOnStack(methodVisitor, generatedType);
   methodVisitor.visitMethodInsn(
       INVOKEINTERFACE,
       MODEL_ELEMENT_STATE_TYPE_INTERNAL_NAME,
       "getBackingNode",
       GET_BACKING_NODE_METHOD_DESCRIPTOR,
       true);
   finishVisitingMethod(methodVisitor, ARETURN);
 }
 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 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 assignDelegateField(
     MethodVisitor constructorVisitor, Type generatedType, Type delegateType) {
   putThisOnStack(constructorVisitor);
   putThirdMethodArgumentOnStack(constructorVisitor);
   constructorVisitor.visitFieldInsn(
       PUTFIELD,
       generatedType.getInternalName(),
       DELEGATE_FIELD_NAME,
       delegateType.getDescriptor());
 }
 private void pushDefaultValue(MethodVisitor methodVisitor, Class<?> primitiveType) {
   int ins = ICONST_0;
   if (long.class == primitiveType) {
     ins = LCONST_0;
   } else if (double.class == primitiveType) {
     ins = DCONST_0;
   } else if (float.class == primitiveType) {
     ins = FCONST_0;
   }
   methodVisitor.visitInsn(ins);
 }
    private void backportLambda(
        String invokedName, Type invokedType, Handle bsm, Object[] bsmArgs) {
      Class<?> invoker = loadClass(className);
      Handle implMethod = (Handle) bsmArgs[1];
      Handle accessMethod = getLambdaAccessMethod(implMethod);

      LambdaFactoryMethod factory =
          LambdaReifier.reifyLambdaClass(
              implMethod, accessMethod, invoker, invokedName, invokedType, bsm, bsmArgs);
      super.visitMethodInsn(
          INVOKESTATIC, factory.getOwner(), factory.getName(), factory.getDesc(), false);
    }
  private static <T> void appendGetDelayedCellSetter(
      DelayedCellSetterFactory<T, ?>[] delayedCellSetters,
      ClassWriter cw,
      String targetType,
      String classType,
      int maxMethodSize) {
    MethodVisitor mv;
    mv =
        cw.visitMethod(
            ACC_PUBLIC,
            "getDelayedCellSetter",
            "(I)L" + DELAYED_CELL_SETTER_TYPE + ";",
            "(I)L" + DELAYED_CELL_SETTER_TYPE + "<L" + targetType + ";*>;",
            null);
    mv.visitCode();

    if (delayedCellSetters.length != 0) {
      final int switchStart = 0;
      final int switchEnd = delayedCellSetters.length;
      appendGetDelayedCellSetterSwitch(delayedCellSetters, classType, mv, switchStart, switchEnd);
    }
    mv.visitInsn(ACONST_NULL);
    mv.visitInsn(ARETURN);

    mv.visitMaxs(1, 2);
    mv.visitEnd();
  }
  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 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);
 }
  // 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);
  }
 protected void injectGetFieldTable(ClassWriter classWriter) {
   MethodVisitor methodVisitor =
       classWriter.visitMethod(
           ACC_PUBLIC, "getFieldTable", "()[Ljava/lang/reflect/Field;", null, null);
   methodVisitor.visitCode();
   methodVisitor.visitVarInsn(ALOAD, 0);
   methodVisitor.visitFieldInsn(
       GETFIELD,
       getAccessorNameInternal(this.getTarget(), this.getAccessorType()),
       "fieldTable",
       "[Ljava/lang/reflect/Field;");
   methodVisitor.visitInsn(ARETURN);
   methodVisitor.visitMaxs(1, 1);
   methodVisitor.visitEnd();
 }
 @Override
 public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
   if (bsm.getOwner().equals(LambdaNaming.LAMBDA_METAFACTORY)) {
     backportLambda(name, Type.getType(desc), bsm, bsmArgs);
   } else {
     super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
   }
 }