/** Rewrites a field reference to another field access. */
    MemberAdapter fieldToField(Field f) {
      final String newName = f.getName();
      final Type newType = Type.getType(f.getType());
      final String newTypeDescriptor = newType.getDescriptor();
      final String newTypeInternalName =
          isReferenceType(newType) ? newType.getInternalName() : null;

      return new MemberAdapter(f) {
        @Override
        boolean adapt(int opcode, String owner, String name, String desc, MethodVisitor delegate) {
          switch (opcode) {
            case GETFIELD:
            case GETSTATIC:
              // rewrite "x.y" to "(T)x.z".
              // we'll leave it up to HotSpot to optimize away casts
              delegate.visitFieldInsn(opcode, owner, newName, newTypeDescriptor);
              Type t = Type.getType(desc);
              if (isReferenceType(t)) delegate.visitTypeInsn(CHECKCAST, t.getInternalName());
              return true;
            case PUTFIELD:
            case PUTSTATIC:
              // rewrite "x.y=v" to "x.z=(T)v"
              if (isReferenceType(newType)) delegate.visitTypeInsn(CHECKCAST, newTypeInternalName);
              delegate.visitFieldInsn(opcode, owner, newName, newTypeDescriptor);
              return true;
          }
          return false;
        }
      };
    }
      @Override
      boolean adapt(int opcode, String owner, String name, String desc, MethodVisitor delegate) {
        if (opcode == fieldOpcode) {
          Type t = Type.getType(desc);
          boolean expectedReference = isReferenceType(t);

          if (actuallyPrimitive ^ expectedReference) {
            // rewrite "x.y" to "(T)x.z()".
            // we'll leave it up to HotSpot to optimize away casts
            delegate.visitMethodInsn(invokeOpcode, owner, methodName, methodDescriptor);
            if (expectedReference) delegate.visitTypeInsn(CHECKCAST, t.getInternalName());
            return true;
          }
        }
        return false;
      }
      public FieldToMethodAdapter(Method m, int fieldOpcode, int invokeOpcode) {
        super(m);

        methodName = m.getName();
        methodDescriptor = Type.getMethodDescriptor(m);

        Class<?>[] params = m.getParameterTypes();
        boolean isGetter = params.length == 0;

        actuallyPrimitive = isGetter ? m.getReturnType().isPrimitive() : params[0].isPrimitive();

        this.fieldOpcode = fieldOpcode;
        this.invokeOpcode = invokeOpcode;
      }
      @Override
      boolean adapt(int opcode, String owner, String name, String desc, MethodVisitor delegate) {
        if (opcode == fieldOpcode) {
          Type t = Type.getType(desc);
          boolean expectedReference = isReferenceType(t);

          if (actuallyPrimitive ^ expectedReference) {
            // rewrite "x.y=v" to "x.z(v)"
            // we expect the argument type to match
            delegate.visitMethodInsn(invokeOpcode, owner, methodName, methodDescriptor);
            return true;
          }
        }
        return false;
      }
 private static boolean isReferenceType(Type t) {
   return t.getSort() == ARRAY || t.getSort() == OBJECT;
 }