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.ASM6, 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}