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 static <T> void appendCellValue(
     CellSetter<T>[] setters, boolean ignoreException, ClassWriter cw, String classType) {
   MethodVisitor mv;
   mv = cw.visitMethod(ACC_PUBLIC, "cellValue", "([CIII)V", null, null);
   mv.visitCode();
   if (setters.length != 0) {
     if (ignoreException) {
       callCellValue(mv, classType);
     } else {
       Label l0 = new Label();
       Label l1 = new Label();
       Label l2 = new Label();
       mv.visitTryCatchBlock(l0, l1, l2, "java/lang/Exception");
       mv.visitLabel(l0);
       callCellValue(mv, classType);
       mv.visitLabel(l1);
       Label l3 = new Label();
       mv.visitJumpInsn(GOTO, l3);
       mv.visitLabel(l2);
       mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"java/lang/Exception"});
       mv.visitVarInsn(ASTORE, 5);
       mv.visitVarInsn(ALOAD, 0);
       mv.visitVarInsn(ILOAD, 4);
       mv.visitVarInsn(ALOAD, 5);
       mv.visitMethodInsn(
           INVOKEVIRTUAL, classType, "fieldError", "(ILjava/lang/Exception;)V", false);
       mv.visitLabel(l3);
       mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
     }
   }
   mv.visitInsn(RETURN);
   mv.visitMaxs(5, 6);
   mv.visitEnd();
 }
 private void generateProxyClass(
     ClassWriter visitor,
     StructSchema<?> viewSchema,
     StructSchema<?> delegateSchema,
     Collection<String> interfacesToImplement,
     Set<Class<?>> typesToDelegate,
     Type generatedType,
     Type superclassType) {
   ModelType<?> viewType = viewSchema.getType();
   Class<?> viewClass = viewType.getConcreteClass();
   declareClass(visitor, interfacesToImplement, generatedType, superclassType);
   declareStateField(visitor);
   declareTypeConverterField(visitor);
   declareManagedTypeField(visitor);
   declareCanCallSettersField(visitor);
   writeStaticConstructor(visitor, generatedType, viewClass);
   writeConstructor(visitor, generatedType, superclassType, delegateSchema);
   writeToString(visitor, generatedType, viewClass, delegateSchema);
   writeManagedInstanceMethods(visitor, generatedType);
   if (delegateSchema != null) {
     declareDelegateField(visitor, delegateSchema);
     writeDelegateMethods(visitor, generatedType, delegateSchema, typesToDelegate);
   }
   writeGroovyMethods(visitor, viewClass);
   writePropertyMethods(visitor, generatedType, viewSchema, delegateSchema);
   writeHashCodeMethod(visitor, generatedType);
   writeEqualsMethod(visitor, generatedType);
   visitor.visitEnd();
 }
 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();
     }
   }
 }
 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();
 }
  protected void injectGetByIndex(
      ClassWriter classWriter, String targetClassName, List<Field> fields) {
    MethodVisitor methodVisitor =
        classWriter.visitMethod(
            ACC_PUBLIC,
            "get",
            "(Ljava/lang/Object;I)Ljava/lang/Object;",
            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();
        String fieldDescriptor = Type.getDescriptor(type);

        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)) {
          methodVisitor.visitVarInsn(ALOAD, 1);
          methodVisitor.visitTypeInsn(CHECKCAST, targetClassName);
          methodVisitor.visitFieldInsn(GETFIELD, targetClassName, field.getName(), fieldDescriptor);

          boxer.box(Type.getType(type));
        } else {
          injectReflectiveGetter(methodVisitor);
        }

        methodVisitor.visitInsn(ARETURN);
      }

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

    injectException(methodVisitor, IllegalAccessException.class);
    methodVisitor.visitMaxs(maxStack, 3);
    methodVisitor.visitEnd();
  }
  @Override
  public FieldAccessor build(ClassLoader classLoader) {
    String targetClassType = getInternalName(this.getTarget().getName());

    ClassWriter classWriter = this.getClassWriter();

    injectFieldTable(classWriter);
    injectConstructor(classWriter);
    injectGetTargetClass(classWriter, this.getTarget());

    List<Field> fields = new ArrayList<>();
    Class<?> current = this.getTarget();
    while (current != Object.class) {
      for (Field field : current.getDeclaredFields()) {
        fields.add(field);
      }
      current = current.getSuperclass();
    }

    injectGetByIndex(classWriter, targetClassType, fields);
    injectSetByIndex(classWriter, targetClassType, fields);

    injectGetByName(classWriter);
    injectSetByName(classWriter);

    injectGetFieldTable(classWriter);
    injectGetIndex(classWriter);

    classWriter.visitEnd();

    Class<?> result =
        CompilerService.create(classLoader)
            .defineClass(
                getExternalName(
                    getAccessorNameInternal(
                        this.getTarget(),
                        this
                            .getAccessorType())), // this is somewhat redundant but maybe in the
                                                  // future the class-name-format changes
                this.getClassWriter().toByteArray());

    return (FieldAccessor) instantiate(result);
  }
 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();
 }
 protected void injectGetIndex(ClassWriter classWriter) {
   MethodVisitor mv =
       classWriter.visitMethod(ACC_PRIVATE, "getIndex", "(Ljava/lang/String;)I", null, null);
   mv.visitCode();
   mv.visitInsn(ICONST_0);
   mv.visitVarInsn(ISTORE, 2);
   Label forLoopLabel = new Label();
   mv.visitLabel(forLoopLabel);
   mv.visitFrame(Opcodes.F_APPEND, 1, new Object[] {Opcodes.INTEGER}, 0, null);
   mv.visitVarInsn(ILOAD, 2);
   mv.visitVarInsn(ALOAD, 0);
   mv.visitFieldInsn(
       GETFIELD,
       getAccessorNameInternal(this.getTarget(), this.getAccessorType()),
       "fieldTable",
       "[Ljava/lang/reflect/Field;");
   mv.visitInsn(ARRAYLENGTH);
   Label forLoopIncrementLabel = new Label();
   mv.visitJumpInsn(IF_ICMPGE, forLoopIncrementLabel);
   mv.visitVarInsn(ALOAD, 0);
   mv.visitFieldInsn(
       GETFIELD,
       getAccessorNameInternal(this.getTarget(), this.getAccessorType()),
       "fieldTable",
       "[Ljava/lang/reflect/Field;");
   mv.visitVarInsn(ILOAD, 2);
   mv.visitInsn(AALOAD);
   mv.visitMethodInsn(
       INVOKEVIRTUAL, "java/lang/reflect/Field", "getName", "()Ljava/lang/String;", false);
   mv.visitVarInsn(ALOAD, 1);
   mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z", false);
   Label equalsLabel = new Label();
   mv.visitJumpInsn(IFEQ, equalsLabel);
   mv.visitVarInsn(ILOAD, 2);
   mv.visitInsn(IRETURN);
   mv.visitLabel(equalsLabel);
   mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
   mv.visitIincInsn(2, 1);
   mv.visitJumpInsn(GOTO, forLoopLabel);
   mv.visitLabel(forLoopIncrementLabel);
   mv.visitFrame(Opcodes.F_CHOP, 1, null, 0, null);
   mv.visitInsn(ICONST_M1);
   mv.visitInsn(IRETURN);
   mv.visitMaxs(2, 3);
   mv.visitEnd();
 }
    @Override
    public void leafDispatch(String suffix, int start, int end) {
      MethodVisitor mv;
      mv = cw.visitMethod(ACC_PUBLIC, name() + suffix, signature(), null, null);
      mv.visitCode();

      if (end - start > 0) {
        mv.visitVarInsn(ILOAD, argIndex());
        appendLeafSwitch(mv, start, end);
      }
      if (isVoid()) {
        mv.visitInsn(RETURN);
      } else {
        mv.visitInsn(ACONST_NULL);
        mv.visitInsn(ARETURN);
      }
      mv.visitMaxs(1, 2);
      mv.visitEnd();
    }
  public static byte[] createTargetSetterFactory(String factoryName, String className, Type target)
      throws Exception {

    ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
    MethodVisitor mv;

    String factoryType = AsmUtils.toType(factoryName);
    String classType = AsmUtils.toType(className);
    String targetType = AsmUtils.toType(target);

    cw.visit(
        V1_6,
        ACC_FINAL + ACC_PUBLIC + ACC_SUPER,
        factoryType,
        "L" + CELL_HANDLER_FACTORY_TYPE + "<L" + targetType + ";>;",
        CELL_HANDLER_FACTORY_TYPE,
        null);

    {
      mv =
          cw.visitMethod(
              ACC_PUBLIC,
              "<init>",
              "("
                  + AsmUtils.toDeclaredLType(Instantiator.class)
                  + AsmUtils.toDeclaredLType(CsvColumnKey[].class)
                  + AsmUtils.toDeclaredLType(ParsingContextFactory.class)
                  + AsmUtils.toDeclaredLType(FieldMapperErrorHandler.class)
                  + ")V",
              "("
                  + "L"
                  + AsmUtils.toType(Instantiator.class)
                  + "<L"
                  + AsmUtils.toType(CsvMapperCellHandler.class)
                  + "<L"
                  + targetType
                  + ";>;L"
                  + targetType
                  + ";>;"
                  + AsmUtils.toDeclaredLType(CsvColumnKey[].class)
                  + AsmUtils.toDeclaredLType(ParsingContextFactory.class)
                  + "L"
                  + AsmUtils.toType(FieldMapperErrorHandler.class)
                  + "<L"
                  + AsmUtils.toType(CsvColumnKey.class)
                  + ";>;"
                  + ")V",
              null);

      mv.visitCode();
      mv.visitVarInsn(ALOAD, 0);
      mv.visitVarInsn(ALOAD, 1);
      mv.visitVarInsn(ALOAD, 2);
      mv.visitVarInsn(ALOAD, 3);
      mv.visitVarInsn(ALOAD, 4);
      mv.visitMethodInsn(
          INVOKESPECIAL,
          CELL_HANDLER_FACTORY_TYPE,
          "<init>",
          "("
              + AsmUtils.toDeclaredLType(Instantiator.class)
              + AsmUtils.toDeclaredLType(CsvColumnKey[].class)
              + AsmUtils.toDeclaredLType(ParsingContextFactory.class)
              + AsmUtils.toDeclaredLType(FieldMapperErrorHandler.class)
              + ")V",
          false);
      mv.visitInsn(RETURN);
      mv.visitMaxs(5, 5);
      mv.visitEnd();
    }
    {
      mv =
          cw.visitMethod(
              ACC_PUBLIC,
              "newInstance",
              "("
                  + AsmUtils.toDeclaredLType(DelayedCellSetter[].class)
                  + AsmUtils.toDeclaredLType(CellSetter[].class)
                  + ")"
                  + AsmUtils.toDeclaredLType(CsvMapperCellHandler.class),
              "("
                  + "[L"
                  + DELAYED_CELL_SETTER_TYPE
                  + "<L"
                  + targetType
                  + ";*>;"
                  + "[L"
                  + CELL_SETTER_TYPE
                  + "<L"
                  + targetType
                  + ";>;"
                  + ")"
                  + "L"
                  + AsmUtils.toType(CsvMapperCellHandler.class)
                  + "<L"
                  + targetType
                  + ";>;",
              null);
      mv.visitCode();
      mv.visitTypeInsn(NEW, classType);
      mv.visitInsn(DUP);
      mv.visitVarInsn(ALOAD, 0);
      mv.visitFieldInsn(
          GETFIELD, factoryType, "instantiator", AsmUtils.toDeclaredLType(Instantiator.class));
      mv.visitVarInsn(ALOAD, 1);
      mv.visitVarInsn(ALOAD, 2);
      mv.visitVarInsn(ALOAD, 0);
      mv.visitFieldInsn(
          GETFIELD, factoryType, "keys", AsmUtils.toDeclaredLType(CsvColumnKey[].class));
      mv.visitVarInsn(ALOAD, 0);
      mv.visitFieldInsn(
          GETFIELD,
          factoryType,
          "parsingContextFactory",
          AsmUtils.toDeclaredLType(ParsingContextFactory.class));
      mv.visitMethodInsn(
          INVOKEVIRTUAL,
          AsmUtils.toType(ParsingContextFactory.class),
          "newContext",
          "()" + AsmUtils.toDeclaredLType(ParsingContext.class),
          false);
      mv.visitVarInsn(ALOAD, 0);
      mv.visitFieldInsn(
          GETFIELD,
          factoryType,
          "fieldErrorHandler",
          AsmUtils.toDeclaredLType(FieldMapperErrorHandler.class));
      mv.visitMethodInsn(
          INVOKESPECIAL,
          classType,
          "<init>",
          "("
              + AsmUtils.toDeclaredLType(Instantiator.class)
              + AsmUtils.toDeclaredLType(DelayedCellSetter[].class)
              + AsmUtils.toDeclaredLType(CellSetter[].class)
              + AsmUtils.toDeclaredLType(CsvColumnKey[].class)
              + AsmUtils.toDeclaredLType(ParsingContext.class)
              + AsmUtils.toDeclaredLType(FieldMapperErrorHandler.class)
              + ")V",
          false);
      mv.visitInsn(ARETURN);
      mv.visitMaxs(8, 3);
      mv.visitEnd();
    }
    cw.visitEnd();

    return cw.toByteArray();
  }
  private static <T> void appendInit(
      DelayedCellSetterFactory<T, ?>[] delayedCellSetters,
      CellSetter<T>[] setters,
      ClassWriter cw,
      String targetType,
      String classType,
      int maxSize) {
    MethodVisitor mv; // constructor
    {
      mv =
          cw.visitMethod(
              ACC_PUBLIC,
              "<init>",
              "("
                  + AsmUtils.toDeclaredLType(Instantiator.class)
                  + AsmUtils.toDeclaredLType(DelayedCellSetter[].class)
                  + AsmUtils.toDeclaredLType(CellSetter[].class)
                  + AsmUtils.toDeclaredLType(CsvColumnKey[].class)
                  + AsmUtils.toDeclaredLType(ParsingContext.class)
                  + AsmUtils.toDeclaredLType(FieldMapperErrorHandler.class)
                  + ")V",
              "("
                  + "L"
                  + AsmUtils.toType(Instantiator.class)
                  + "<L"
                  + AsmUtils.toType(CsvMapperCellHandler.class)
                  + "<L"
                  + targetType
                  + ";>;L"
                  + targetType
                  + ";>;"
                  + "[L"
                  + DELAYED_CELL_SETTER_TYPE
                  + "<L"
                  + targetType
                  + ";*>;"
                  + "[L"
                  + CELL_SETTER_TYPE
                  + "<L"
                  + targetType
                  + ";>;"
                  + AsmUtils.toDeclaredLType(CsvColumnKey[].class)
                  + AsmUtils.toDeclaredLType(ParsingContext.class)
                  + "L"
                  + AsmUtils.toType(FieldMapperErrorHandler.class)
                  + "<L"
                  + AsmUtils.toType(CsvColumnKey.class)
                  + ";>;"
                  + ")V",
              null);
      mv.visitCode();
      mv.visitVarInsn(ALOAD, 0);
      mv.visitVarInsn(ALOAD, 1);
      mv.visitVarInsn(ALOAD, 4);
      mv.visitVarInsn(ALOAD, 2);
      mv.visitInsn(ARRAYLENGTH);
      mv.visitVarInsn(ALOAD, 3);
      mv.visitInsn(ARRAYLENGTH);
      mv.visitVarInsn(ALOAD, 5);
      mv.visitVarInsn(ALOAD, 6);
      mv.visitMethodInsn(
          INVOKESPECIAL,
          CSV_CELL_MAPPER_TYPE,
          "<init>",
          "("
              + AsmUtils.toDeclaredLType(Instantiator.class)
              + AsmUtils.toDeclaredLType(CsvColumnKey[].class)
              + "I"
              + "I"
              + AsmUtils.toDeclaredLType(ParsingContext.class)
              + AsmUtils.toDeclaredLType(FieldMapperErrorHandler.class)
              + ")V",
          false);

      ShardingHelper.shard(
          delayedCellSetters.length,
          maxSize,
          new ShardingHelper.ShardCallBack() {
            @Override
            public void leafDispatch(String suffix, int start, int end) {}

            @Override
            public void nodeDispatch(String suffix, int divide, int start, int end) {}
          });
      for (int i = 0; i < delayedCellSetters.length; i++) {
        if (delayedCellSetters[i] != null) {
          mv.visitVarInsn(ALOAD, 0);
          mv.visitVarInsn(ALOAD, 2);
          AsmUtils.addIndex(mv, i);
          mv.visitInsn(AALOAD);
          mv.visitFieldInsn(
              PUTFIELD,
              classType,
              "delayedCellSetter" + i,
              AsmUtils.toDeclaredLType(DELAYED_CELL_SETTER_TYPE));
        }
      }

      for (int i = 0; i < setters.length; i++) {
        if (setters[i] != null) {
          mv.visitVarInsn(ALOAD, 0);
          mv.visitVarInsn(ALOAD, 3);
          AsmUtils.addIndex(mv, i);
          mv.visitInsn(AALOAD);
          mv.visitFieldInsn(
              PUTFIELD, classType, "setter" + i, AsmUtils.toDeclaredLType(CELL_SETTER_TYPE));
        }
      }

      mv.visitInsn(RETURN);
      mv.visitMaxs(7, 7);
      mv.visitEnd();
    }
  }
  public static <T> byte[] createTargetSetterClass(
      String className,
      DelayedCellSetterFactory<T, ?>[] delayedCellSetters,
      CellSetter<T>[] setters,
      Type type,
      boolean ignoreException,
      int maxMethodSize)
      throws Exception {

    ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);

    FieldVisitor fv;
    MethodVisitor mv;

    final String targetType = AsmUtils.toType(type);
    final String classType = AsmUtils.toType(className);

    cw.visit(
        V1_6,
        ACC_FINAL + ACC_PUBLIC + ACC_SUPER,
        classType,
        "L" + CSV_CELL_MAPPER_TYPE + "<L" + targetType + ";>;",
        CSV_CELL_MAPPER_TYPE,
        null);

    // declare fields
    for (int i = 0; i < delayedCellSetters.length; i++) {
      if (delayedCellSetters[i] != null) {
        fv =
            cw.visitField(
                ACC_PROTECTED + ACC_FINAL,
                "delayedCellSetter" + i,
                AsmUtils.toDeclaredLType(DELAYED_CELL_SETTER_TYPE),
                "L" + DELAYED_CELL_SETTER_TYPE + "<L" + targetType + ";*>;",
                null);
        fv.visitEnd();
      }
    }

    for (int i = 0; i < setters.length; i++) {
      if (setters[i] != null) {
        fv =
            cw.visitField(
                ACC_PROTECTED + ACC_FINAL,
                "setter" + i,
                AsmUtils.toDeclaredLType(CELL_SETTER_TYPE),
                "L" + CELL_SETTER_TYPE + "<L" + targetType + ";>;",
                null);
        fv.visitEnd();
      }
    }
    appendInit(delayedCellSetters, setters, cw, targetType, classType, maxMethodSize);

    appendDelayedCellValue(delayedCellSetters, ignoreException, cw, classType);
    append_delayedCellValue(delayedCellSetters, cw, classType, maxMethodSize);

    appendCellValue(setters, ignoreException, cw, classType);
    append_cellValue(delayedCellSetters, setters, cw, classType, maxMethodSize);

    appendApplyDelayedSetter(delayedCellSetters, ignoreException, cw, classType, maxMethodSize);
    appendApplyDelayedCellSetterN(delayedCellSetters, cw, classType);

    appendGetDelayedCellSetter(delayedCellSetters, cw, targetType, classType, maxMethodSize);
    appendPeekDelayedCellSetterValue(delayedCellSetters, cw, classType, maxMethodSize);

    cw.visitEnd();

    return AsmUtils.writeClassToFile(className, cw.toByteArray());
  }
  protected void injectConstructor(ClassWriter classWriter) {
    String resultName = getAccessorNameInternal(this.getTarget(), this.getAccessorType());

    MethodVisitor methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
    methodVisitor.visitCode();
    methodVisitor.visitVarInsn(ALOAD, 0);
    methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
    ;
    methodVisitor.visitTypeInsn(NEW, "java/util/ArrayList");
    methodVisitor.visitInsn(DUP);
    methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/util/ArrayList", "<init>", "()V", false);
    methodVisitor.visitVarInsn(ASTORE, 1);
    methodVisitor.visitVarInsn(ALOAD, 0);
    methodVisitor.visitMethodInsn(
        INVOKEVIRTUAL, resultName, "getTargetClass", "()Ljava/lang/Class;", false);
    methodVisitor.visitVarInsn(ASTORE, 2);
    Label whileLoop = new Label(); // enter the "while (currentClass != Object.class)" loop
    methodVisitor.visitLabel(whileLoop);
    methodVisitor.visitFrame(
        Opcodes.F_FULL,
        3,
        new Object[] {resultName, "java/util/List", "java/lang/Class"},
        0,
        new Object[] {});
    methodVisitor.visitVarInsn(ALOAD, 2);
    methodVisitor.visitLdcInsn(Type.getType("Ljava/lang/Object;"));
    Label classNotObjectComparer =
        new Label(); // compares the current class to Object.class aka currentClass != Object.class
    methodVisitor.visitJumpInsn(IF_ACMPEQ, classNotObjectComparer);
    methodVisitor.visitVarInsn(ALOAD, 2);
    methodVisitor.visitMethodInsn(
        INVOKEVIRTUAL,
        "java/lang/Class",
        "getDeclaredFields",
        "()[Ljava/lang/reflect/Field;",
        false);
    methodVisitor.visitVarInsn(ASTORE, 3);
    methodVisitor.visitVarInsn(ALOAD, 3);
    methodVisitor.visitInsn(ARRAYLENGTH);
    methodVisitor.visitVarInsn(ISTORE, 4);
    methodVisitor.visitInsn(ICONST_0);
    methodVisitor.visitVarInsn(ISTORE, 5);
    Label cachingForLoop = new Label(); // this loop handles the storing of the fields (to the list)
    methodVisitor.visitLabel(cachingForLoop);
    methodVisitor.visitFrame(
        Opcodes.F_APPEND,
        3,
        new Object[] {"[Ljava/lang/reflect/Field;", Opcodes.INTEGER, Opcodes.INTEGER},
        0,
        null);
    methodVisitor.visitVarInsn(ILOAD, 5);
    methodVisitor.visitVarInsn(ILOAD, 4);
    Label cachingForLoopIncrementLabel = new Label(); // handles the looping of the fields
    methodVisitor.visitJumpInsn(IF_ICMPGE, cachingForLoopIncrementLabel);
    methodVisitor.visitVarInsn(ALOAD, 3);
    methodVisitor.visitVarInsn(ILOAD, 5);
    methodVisitor.visitInsn(AALOAD);
    methodVisitor.visitVarInsn(ASTORE, 6);
    methodVisitor.visitVarInsn(ALOAD, 1);
    methodVisitor.visitVarInsn(ALOAD, 6);
    methodVisitor.visitMethodInsn(
        INVOKEINTERFACE, "java/util/List", "add", "(Ljava/lang/Object;)Z", true);
    methodVisitor.visitInsn(POP);
    methodVisitor.visitIincInsn(5, 1);
    methodVisitor.visitJumpInsn(GOTO, cachingForLoop);
    methodVisitor.visitLabel(cachingForLoopIncrementLabel);
    methodVisitor.visitFrame(Opcodes.F_CHOP, 3, null, 0, null);
    methodVisitor.visitVarInsn(ALOAD, 2);
    methodVisitor.visitMethodInsn(
        INVOKEVIRTUAL, "java/lang/Class", "getSuperclass", "()Ljava/lang/Class;", false);
    methodVisitor.visitVarInsn(ASTORE, 2);
    methodVisitor.visitJumpInsn(GOTO, whileLoop);
    methodVisitor.visitLabel(classNotObjectComparer);
    methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
    methodVisitor.visitVarInsn(ALOAD, 0);
    methodVisitor.visitVarInsn(ALOAD, 1);
    methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "size", "()I", true);
    methodVisitor.visitTypeInsn(ANEWARRAY, "java/lang/reflect/Field");
    methodVisitor.visitFieldInsn(PUTFIELD, resultName, "fieldTable", "[Ljava/lang/reflect/Field;");
    methodVisitor.visitInsn(ICONST_0);
    methodVisitor.visitVarInsn(ISTORE, 3);
    Label storingForLoop =
        new Label(); // this for loop goes about storing the fields in the array defined in the
                     // class
    methodVisitor.visitLabel(storingForLoop);
    methodVisitor.visitFrame(Opcodes.F_APPEND, 1, new Object[] {Opcodes.INTEGER}, 0, null);
    methodVisitor.visitVarInsn(ILOAD, 3);
    methodVisitor.visitVarInsn(ALOAD, 0);
    methodVisitor.visitFieldInsn(GETFIELD, resultName, "fieldTable", "[Ljava/lang/reflect/Field;");
    methodVisitor.visitInsn(ARRAYLENGTH);
    Label storage =
        new Label(); // gets the field at position x and stores it in the cache (fieldTable)
    methodVisitor.visitJumpInsn(IF_ICMPGE, storage);
    methodVisitor.visitVarInsn(ALOAD, 0);
    methodVisitor.visitFieldInsn(GETFIELD, resultName, "fieldTable", "[Ljava/lang/reflect/Field;");
    methodVisitor.visitVarInsn(ILOAD, 3);
    methodVisitor.visitVarInsn(ALOAD, 1);
    methodVisitor.visitVarInsn(ILOAD, 3);
    methodVisitor.visitMethodInsn(
        INVOKEINTERFACE, "java/util/List", "get", "(I)Ljava/lang/Object;", true);
    methodVisitor.visitTypeInsn(CHECKCAST, "java/lang/reflect/Field");
    methodVisitor.visitInsn(AASTORE);
    methodVisitor.visitIincInsn(3, 1);
    methodVisitor.visitJumpInsn(GOTO, storingForLoop);
    methodVisitor.visitLabel(storage);
    methodVisitor.visitFrame(Opcodes.F_CHOP, 1, null, 0, null);
    methodVisitor.visitInsn(RETURN);
    methodVisitor.visitMaxs(4, 7);
    methodVisitor.visitEnd();
  }
 protected void injectFieldTable(ClassWriter classWriter) {
   FieldVisitor fieldVisitor =
       classWriter.visitField(
           ACC_PRIVATE + ACC_FINAL, "fieldTable", "[Ljava/lang/reflect/Field;", null, null);
   fieldVisitor.visitEnd();
 }
    @Override
    public void nodeDispatch(String suffix, int divide, int start, int end) {
      MethodVisitor mv;
      mv = cw.visitMethod(ACC_PRIVATE, name() + suffix, signature(), null, null);
      mv.visitCode();
      Label startLabel = new Label();
      mv.visitLabel(startLabel);
      final int powerOfTwo = Integer.numberOfTrailingZeros(divide);

      int sStart = start >> powerOfTwo;
      int sEnd = end >> powerOfTwo;

      int left = end - (sEnd << powerOfTwo);
      if (left > 0) {
        sEnd++;
      }

      Label[] labels = newLabels(sEnd - sStart);
      Label defaultLabel = new Label();

      mv.visitVarInsn(ILOAD, argIndex());

      int sub = leafStart();
      if (sub != 0) {
        AsmUtils.addIndex(mv, sub);
        mv.visitInsn(ISUB);
      }

      AsmUtils.addIndex(mv, powerOfTwo);
      mv.visitInsn(ISHR);

      mv.visitVarInsn(ISTORE, maxArgIndex() + 1);
      mv.visitVarInsn(ILOAD, maxArgIndex() + 1);
      mv.visitTableSwitchInsn(sStart, sEnd - 1, defaultLabel, labels);

      for (int i = sStart; i < sEnd; i++) {

        int estart = i << powerOfTwo;
        int eend = Math.min(end, (i + 1) << powerOfTwo);

        mv.visitLabel(labels[i - sStart]);
        if (i == start) {
          mv.visitFrame(Opcodes.F_APPEND, 1, new Object[] {Opcodes.INTEGER}, 0, null);
        } else {
          mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
        }

        loadArguments(mv);
        mv.visitMethodInsn(
            INVOKESPECIAL,
            classType,
            name() + (divide / maxMethodSize) + "n" + estart + "t" + eend,
            signature(),
            false);

        if (isVoid()) {
          if (i < (sEnd - 1)) {
            mv.visitJumpInsn(GOTO, defaultLabel);
          }
        } else {
          mv.visitInsn(ARETURN);
        }
      }

      mv.visitLabel(defaultLabel);
      mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);

      if (isVoid()) {
        mv.visitInsn(RETURN);
      } else {
        mv.visitInsn(ACONST_NULL);
        mv.visitInsn(ARETURN);
      }

      Label endLabel = new Label();
      mv.visitLabel(endLabel);
      appendDebugInfo(mv, startLabel, endLabel);
      mv.visitMaxs(6, 5);
      mv.visitEnd();
    }
  private static <T> void appendPeekDelayedCellSetterValue(
      final DelayedCellSetterFactory<T, ?>[] delayedCellSetters,
      final ClassWriter cw,
      final String classType,
      final int maxMethodSize) {

    ShardingHelper.shard(
        delayedCellSetters.length,
        maxMethodSize,
        new AbstractMethodDispatchShardCallBack<T>(cw, classType, maxMethodSize) {
          @Override
          protected void appendLeafSwitch(MethodVisitor mv, int start, int end) {
            Label defaultLabel = new Label();
            Label[] labels = newLabels(end - start);
            mv.visitTableSwitchInsn(start, end - 1, defaultLabel, labels);

            for (int i = start; i < end; i++) {
              mv.visitLabel(labels[i - start]);
              mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
              if (delayedCellSetters[i] != null) {
                mv.visitVarInsn(ALOAD, 0);
                mv.visitFieldInsn(
                    GETFIELD,
                    classType,
                    "delayedCellSetter" + i,
                    "L" + DELAYED_CELL_SETTER_TYPE + ";");
                mv.visitMethodInsn(
                    INVOKEINTERFACE,
                    DELAYED_CELL_SETTER_TYPE,
                    "peekValue",
                    "()Ljava/lang/Object;",
                    true);
                mv.visitInsn(ARETURN);
              } else if (i < (delayedCellSetters.length - 1)) {
                mv.visitInsn(ACONST_NULL);
                mv.visitInsn(ARETURN);
              }
            }
            mv.visitLabel(defaultLabel);
            mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
          }

          @Override
          protected int maxArgIndex() {
            return 1;
          }

          @Override
          protected void loadArguments(MethodVisitor mv) {
            mv.visitVarInsn(ALOAD, 0);
            mv.visitVarInsn(ILOAD, 1);
          }

          @Override
          protected int argIndex() {
            return 1;
          }

          @Override
          protected String name() {
            return "_peekDelayedCellSetterValue";
          }

          @Override
          protected String signature() {
            return "(I)Ljava/lang/Object;";
          }
        });

    MethodVisitor mv;
    mv =
        cw.visitMethod(
            ACC_PUBLIC,
            "peekDelayedCellSetterValue",
            "(L" + AsmUtils.toType(CsvColumnKey.class) + ";)Ljava/lang/Object;",
            null,
            null);
    mv.visitCode();

    mv.visitVarInsn(ALOAD, 1);
    mv.visitMethodInsn(
        INVOKEVIRTUAL, AsmUtils.toType(CsvColumnKey.class), "getIndex", "()I", false);
    mv.visitVarInsn(ISTORE, 2);
    mv.visitVarInsn(ALOAD, 0);
    mv.visitVarInsn(ILOAD, 2);
    mv.visitMethodInsn(
        INVOKESPECIAL, classType, "_peekDelayedCellSetterValue", "(I)Ljava/lang/Object;", false);

    mv.visitInsn(ARETURN);
    mv.visitMaxs(1, 2);
    mv.visitEnd();
  }