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}