001package io.ebean.enhance.entity;
002
003import io.ebean.enhance.asm.AnnotationVisitor;
004import io.ebean.enhance.asm.Label;
005import io.ebean.enhance.asm.MethodVisitor;
006import io.ebean.enhance.asm.Opcodes;
007import io.ebean.enhance.common.ClassMeta;
008
009/**
010 * Changes the method code from using PUTFIELD and GETFIELD to calling our
011 * special field interception methods.
012 * <p>
013 * This should only be performed on non-transient methods. If a method has the
014 * Transient annotation then it is not transformed in this way.
015 * </p>
016 */
017public class MethodFieldAdapter extends MethodVisitor implements Opcodes {
018
019  private final ClassMeta meta;
020
021  private final String className;
022
023  private final String methodDescription;
024
025  private boolean transientAnnotation;
026
027  public MethodFieldAdapter(MethodVisitor mv, ClassMeta meta, String methodDescription) {
028    super(Opcodes.ASM7, mv);
029    this.meta = meta;
030    this.className = meta.getClassName();
031    this.methodDescription = methodDescription;
032  }
033
034  /**
035  * Checks for the javax/persistence/Transient annotation.
036  * <p>
037  * If this annotation is on the method then field interception is not
038  * applied (Aka the method is not transformed).
039  * </p>
040  */
041  @Override
042  public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
043    if (desc.equals("Ljavax/persistence/Transient;")) {
044      transientAnnotation = true;
045    }
046    return super.visitAnnotation(desc, visible);
047  }
048
049  @Override
050  public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
051
052    super.visitLocalVariable(name, desc, signature, start, end, index);
053  }
054
055  @Override
056  public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
057
058    super.visitMethodInsn(opcode, owner, name, desc, itf);
059  }
060
061  @Override
062  public void visitFieldInsn(int opcode, String owner, String name, String desc) {
063
064    if (transientAnnotation) {
065      // The whole method is left as is
066      super.visitFieldInsn(opcode, owner, name, desc);
067      return;
068    }
069
070    if (opcode == Opcodes.GETSTATIC || opcode == Opcodes.PUTSTATIC){
071      if (meta.isLog(3)) {
072        meta.log(" ... info: skip static field "+owner+" "+name+" in "+methodDescription);
073      }
074      super.visitFieldInsn(opcode, owner, name, desc);
075      return;
076    }
077
078    if (isNonPersistentField(owner, name)) {
079      if (meta.isLog(3)) {
080        meta.log(" ... info: non-persistent field "+owner+" "+name+" in "+methodDescription);
081      }
082      super.visitFieldInsn(opcode, owner, name, desc);
083      return;
084    }
085
086    // every basicField has a special set of field interception methods
087    if (opcode == Opcodes.GETFIELD) {
088      String methodName = "_ebean_get_" + name;
089      String methodDesc = "()" + desc;
090      if (meta.isLog(4)) {
091        meta.log("GETFIELD method:" + methodDescription
092          + " field:" + name + " > " + methodName + " "+ methodDesc);
093      }
094      super.visitMethodInsn(INVOKEVIRTUAL, className, methodName, methodDesc, false);
095
096    } else if (opcode == Opcodes.PUTFIELD) {
097      String methodName = "_ebean_set_" + name;
098      String methodDesc = "(" + desc + ")V";
099      if (meta.isLog(4)) {
100        meta.log("PUTFIELD method:" + methodDescription
101          + " field:" + name + " > " + methodName + " "+ methodDesc);
102      }
103      super.visitMethodInsn(INVOKEVIRTUAL, className, methodName, methodDesc, false);
104
105    } else {
106      meta.log("Warning adapting method:" + methodDescription
107        + "; unexpected static access to a persistent field?? " + name
108        +" opCode not GETFIELD or PUTFIELD??  opCode:"+opcode+"");
109
110      super.visitFieldInsn(opcode, owner, name, desc);
111    }
112  }
113
114  /**
115  * Return true if the field is non-persistent and hence should not be intercepted.
116  */
117  private boolean isNonPersistentField(String owner, String name) {
118    return !isSameOwner(owner) || !meta.isFieldPersistent(name);
119  }
120
121  /**
122  * Return true if the owner type is the same as this class being enhanced.
123  */
124  private boolean isSameOwner(String owner) {
125    return className.equals(owner);
126  }
127}