private void addInitializationLogicToConstructors() {
    if (!classMetadata.hasManagedFieldsWithFieldGranularity()) {
      return;
    }

    for (MethodNode methodNode : (List<MethodNode>) classNode.methods) {
      MethodMetadata methodMetadata =
          classMetadata.getMethodMetadata(methodNode.name, methodNode.desc);

      if (methodMetadata.isTransactional() && methodMetadata.isConstructor()) {
        int firstAfterSuper = firstIndexAfterSuper(methodNode, classNode.superName);

        if (firstAfterSuper >= 0) {
          InsnList extraInstructions = new InsnList();
          for (FieldNode fieldNode : (List<FieldNode>) classNode.fields) {
            FieldMetadata fieldMetadata = classMetadata.getFieldMetadata(fieldNode.name);

            if (fieldMetadata.hasFieldGranularity()) {
              extraInstructions.add(new VarInsnNode(ALOAD, 0));

              String referenceDesc = findReferenceDesc(fieldMetadata.getDesc());
              String referenceName = Type.getType(referenceDesc).getInternalName();

              extraInstructions.add(new TypeInsnNode(NEW, referenceName));
              extraInstructions.add(new InsnNode(DUP));

              extraInstructions.add(
                  new MethodInsnNode(INVOKESPECIAL, referenceName, "<init>", "()V"));

              extraInstructions.add(
                  new FieldInsnNode(PUTFIELD, classNode.name, fieldNode.name, referenceDesc));
            }

            AbstractInsnNode first = methodNode.instructions.get(firstAfterSuper);
            methodNode.instructions.insert(first, extraInstructions);
          }
        }
      }
    }
  }
  private void fixFieldAccessInMethods() {
    List<MethodNode> methods = new LinkedList<MethodNode>();

    for (MethodNode methodNode : (List<MethodNode>) classNode.methods) {
      MethodMetadata methodMetadata =
          classMetadata.getMethodMetadata(methodNode.name, methodNode.desc);

      if (methodMetadata.isTransactional()) {
        methodNode = fixMethod(methodNode);
      }

      methods.add(methodNode);
    }

    classNode.methods = methods;
  }
  private void fixFields() {
    List<FieldNode> fields = new LinkedList<FieldNode>();
    for (FieldNode fieldNode : (List<FieldNode>) classNode.fields) {
      FieldMetadata fieldMetadata = classMetadata.getFieldMetadata(fieldNode.name);
      if (fieldMetadata.hasFieldGranularity()) {
        String referenceDesc = findReferenceDesc(fieldNode.desc);

        FieldNode fixedFieldNode =
            new FieldNode(
                ACC_SYNTHETIC + ACC_FINAL + ACC_PUBLIC,
                fieldNode.name,
                referenceDesc,
                null, // signature
                null // value
                );

        fields.add(fixedFieldNode);
      } else {
        fields.add(fieldNode);
      }
    }

    classNode.fields = fields;
  }
  private InsnList fixInstructions(MethodNode originalMethod, CloneMap cloneMap) {
    InsnList instructions = new InsnList();

    for (int k = 0; k < originalMethod.instructions.size(); k++) {
      AbstractInsnNode originalInsn = originalMethod.instructions.get(k);
      switch (originalInsn.getOpcode()) {
          // the put on the field granular field is transformed to a fieldref.set
        case PUTFIELD:
          {
            FieldInsnNode fieldInsn = (FieldInsnNode) originalInsn;
            ClassMetadata ownerMetadata =
                metadataRepository.loadClassMetadata(classLoader, fieldInsn.owner);
            FieldMetadata fieldMetadata = ownerMetadata.getFieldMetadata(fieldInsn.name);
            Type originalFieldType = Type.getType(fieldMetadata.getDesc());

            if (fieldMetadata.hasFieldGranularity()) {

              boolean fieldIsCategory2 = isCategory2(fieldMetadata.getDesc());

              if (fieldIsCategory2) {
                // value(category2), owner,..

                instructions.add(new InsnNode(DUP2_X1));
                // [value(category2), owner, value(category2),...]

                instructions.add(new InsnNode(POP2));
                // [owner, value(category2), ...]
              } else {
                // [value(category1), owner,
                instructions.add(new InsnNode(SWAP));
                // [owner, value(category1),..
              }

              String referenceDesc = findReferenceDesc(fieldMetadata.getDesc());
              String referenceName = Type.getType(referenceDesc).getInternalName();

              instructions.add(
                  new FieldInsnNode(GETFIELD, fieldInsn.owner, fieldInsn.name, referenceDesc));

              if (fieldIsCategory2) {
                // [owner, value(category2),..

                instructions.add(new InsnNode(DUP_X2));
                // [owner, value(category2), owner

                instructions.add(new InsnNode(POP));
                // [value(category2), owner
              } else {
                // [owner, value(category1)
                instructions.add(new InsnNode(SWAP));
                // [value(category1), owner..
              }

              // call the set
              if (originalFieldType.getSort() == Type.ARRAY
                  || originalFieldType.getSort() == Type.OBJECT) {
                String objectDesc = Type.getDescriptor(Object.class);
                MethodInsnNode methodInsn =
                    new MethodInsnNode(
                        INVOKEVIRTUAL,
                        referenceName,
                        "set",
                        format("(%s)%s", objectDesc, objectDesc));
                instructions.add(methodInsn);
              } else {
                MethodInsnNode methodInsn =
                    new MethodInsnNode(
                        INVOKEVIRTUAL,
                        referenceName,
                        "set",
                        format("(%s)%s", fieldMetadata.getDesc(), fieldMetadata.getDesc()));
                instructions.add(methodInsn);
              }

              // pop the unused return value of the set.
              if (fieldIsCategory2) {
                instructions.add(new InsnNode(POP2));
              } else {
                instructions.add(new InsnNode(POP));
              }
            } else {
              instructions.add(originalInsn.clone(cloneMap));
            }
          }
          break;
          // the get on the field granular field is transformed to a fieldref.get
        case GETFIELD:
          {
            FieldInsnNode fieldInsn = (FieldInsnNode) originalInsn;
            FieldMetadata fieldMetadata =
                metadataRepository
                    .loadClassMetadata(classLoader, fieldInsn.owner)
                    .getFieldMetadata(fieldInsn.name);
            if (!fieldMetadata.hasFieldGranularity()) {
              // if it is not getter on a field granular field
              instructions.add(originalInsn.clone(cloneMap));
            } else {
              // it is a getter on a field granular field.
              String referenceDesc = findReferenceDesc(fieldMetadata.getDesc());
              String referenceName = Type.getType(referenceDesc).getInternalName();

              // place the fieldref on the stack.
              instructions.add(
                  new FieldInsnNode(GETFIELD, fieldInsn.owner, fieldInsn.name, referenceDesc));

              Type originalFieldType = Type.getType(fieldMetadata.getDesc());
              if (originalFieldType.getSort() == Type.ARRAY
                  || originalFieldType.getSort() == Type.OBJECT) {
                instructions.add(
                    new MethodInsnNode(
                        INVOKEVIRTUAL,
                        referenceName,
                        "get",
                        format("()%s", getDescriptor(Object.class))));

                if (!originalFieldType.equals(Type.getType(Object.class))) {
                  instructions.add(
                      new TypeInsnNode(CHECKCAST, originalFieldType.getInternalName()));
                }
              } else {
                instructions.add(
                    new MethodInsnNode(
                        INVOKEVIRTUAL,
                        referenceName,
                        "get",
                        format("()%s", fieldMetadata.getDesc())));
              }
            }
          }
          break;
        default:
          instructions.add(originalInsn.clone(cloneMap));
          break;
      }
    }

    return instructions;
  }