001package io.ebean.enhance.entity; 002 003import io.ebean.enhance.asm.AnnotationVisitor; 004import io.ebean.enhance.asm.Attribute; 005import io.ebean.enhance.asm.Handle; 006import io.ebean.enhance.asm.Label; 007import io.ebean.enhance.asm.MethodVisitor; 008import io.ebean.enhance.asm.Opcodes; 009import io.ebean.enhance.asm.TypePath; 010import io.ebean.enhance.common.ClassMeta; 011import io.ebean.enhance.common.EnhanceConstants; 012 013/** 014 * Modify the constructor to additionally initialise the entityBeanIntercept 015 * field. 016 * 017 * <pre class="code"> 018 * // added into constructor 019 * _ebean_intercept = new EntityBeanIntercept(this); 020 * </pre> 021 */ 022public class ConstructorAdapter extends MethodVisitor implements EnhanceConstants, Opcodes { 023 024 private final ClassMeta meta; 025 026 private final String className; 027 028 private final String constructorDesc; 029 030 private boolean constructorInitializationDone; 031 032 /** 033 * Holds an init instructions to see if it is an init of a OneToMany or ManyToMany. 034 */ 035 private final ConstructorDeferredCode deferredCode; 036 037 public ConstructorAdapter(MethodVisitor mv, ClassMeta meta, String constructorDesc) { 038 super(Opcodes.ASM6, mv); 039 this.meta = meta; 040 this.className = meta.getClassName(); 041 this.constructorDesc = constructorDesc; 042 this.deferredCode = new ConstructorDeferredCode(meta, mv); 043 } 044 045 @Override 046 public void visitVarInsn(int opcode, int var) { 047 if (!deferredCode.deferVisitVarInsn(opcode, var)) { 048 super.visitVarInsn(opcode, var); 049 } 050 } 051 052 @Override 053 public void visitTypeInsn(int opcode, String type) { 054 if (!deferredCode.deferVisitTypeInsn(opcode, type)) { 055 super.visitTypeInsn(opcode, type); 056 } 057 } 058 059 @Override 060 public void visitInsn(int opcode) { 061 if (!deferredCode.deferVisitInsn(opcode)) { 062 super.visitInsn(opcode); 063 } 064 } 065 066 @Override 067 public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { 068 if (!deferredCode.deferVisitMethodInsn(opcode, owner, name, desc, itf)) { 069 super.visitMethodInsn(opcode, owner, name, desc, itf); 070 addInitialisationIfRequired(opcode, owner, name, desc); 071 } 072 } 073 074 @Override 075 public void visitFieldInsn(int opcode, String owner, String name, String desc) { 076 077 if (deferredCode.consumeVisitFieldInsn(opcode, owner, name, desc)) { 078 // we have removed all the instructions initialising the many property 079 return; 080 } 081 082 FieldMeta fieldMeta = meta.getFieldPersistent(name); 083 if (fieldMeta == null || !fieldMeta.isPersistent()) { 084 // leave transient fields in constructor alone 085 if (meta.isLog(4)) { 086 meta.log("... visitFieldInsn (in constructor but non-persistent)- " + opcode + " owner:" + owner + ":" + name + ":" + desc); 087 } 088 super.visitFieldInsn(opcode, owner, name, desc); 089 090 } else { 091 if (opcode == PUTFIELD) { 092 // intercept persistent PUTFIELD in the constructor 093 String methodName = "_ebean_set_" + name; 094 String methodDesc = "(" + desc + ")V"; 095 if (meta.isLog(3)) { 096 meta.log("... Constructor PUTFIELD replaced with:" + methodName + methodDesc); 097 } 098 super.visitMethodInsn(INVOKEVIRTUAL, className, methodName, methodDesc, false); 099 100 } else if (opcode == GETFIELD && fieldMeta.isMany()) { 101 // intercept persistent many GETFIELD in the constructor to initialise the collection 102 String methodName = "_ebean_get_" + name; 103 String methodDesc = "()" + desc; 104 if (meta.isLog(3)) { 105 meta.log("... Constructor GETFIELD:" + name + " replaced with: " + methodName + methodDesc); 106 } 107 super.visitMethodInsn(INVOKEVIRTUAL, className, methodName, methodDesc, false); 108 109 } else { 110 if (meta.isLog(3)) { 111 meta.log("... visitFieldInsn (unaltered in constructor)- " + opcode + " owner:" + owner + ":" + name + ":" + desc); 112 } 113 super.visitFieldInsn(opcode, owner, name, desc); 114 } 115 } 116 } 117 118 /** 119 * Add initialisation of EntityBeanIntercept to constructor. 120 * 121 * <pre> 122 * _ebean_intercept = new EntityBeanIntercept(this); 123 * </pre> 124 */ 125 public void addInitialisationIfRequired(int opcode, String owner, String name, String desc) { 126 127 if (opcode == INVOKESPECIAL && name.equals("<init>") && desc.equals("()V")) { 128 if (meta.isSuperClassEntity()) { 129 if (meta.isLog(3)) { 130 meta.log("... skipping intercept <init> ... handled by super class... CONSTRUCTOR: owner:" + owner + " " + constructorDesc); 131 } 132 } else if (owner.equals(meta.getClassName())) { 133 if (meta.isLog(3)) { 134 meta.log("... skipping intercept <init> ... handled by other constructor... CONSTRUCTOR: owner:" + owner + " " + constructorDesc); 135 } 136 } else if (owner.equals(meta.getSuperClassName())){ 137 if (meta.isLog(2)) { 138 meta.log("... adding intercept <init> in CONSTRUCTOR:" + constructorDesc + " OWNER/SUPER:" + owner); 139 } 140 141 if (constructorInitializationDone) { 142 // hopefully this is never called but put it in here to be 143 // on the safe side. 144 String msg = "Error in Enhancement. Only expecting to add <init> of intercept object" 145 + " once but it is trying to add it twice for " + meta.getClassName() + " CONSTRUCTOR:" 146 + constructorDesc+ " OWNER:" + owner; 147 System.err.println(msg); 148 149 } else { 150 // add the initialisation of the intercept object 151 super.visitVarInsn(ALOAD, 0); 152 super.visitTypeInsn(NEW, C_INTERCEPT); 153 super.visitInsn(DUP); 154 super.visitVarInsn(ALOAD, 0); 155 156 super.visitMethodInsn(INVOKESPECIAL, C_INTERCEPT, "<init>", "(Ljava/lang/Object;)V", false); 157 super.visitFieldInsn(PUTFIELD, className, INTERCEPT_FIELD, EnhanceConstants.L_INTERCEPT); 158 159 if (meta.isLog(8)) { 160 meta.log("... constructorInitializationDone " + owner); 161 } 162 constructorInitializationDone = true; 163 } 164 } else { 165 if (meta.isLog(3)) { 166 meta.log("... skipping intercept <init> ... incorrect type "+owner); 167 } 168 } 169 } 170 } 171 172 173 @Override 174 public void visitParameter(String name, int access) { 175 deferredCode.flush(); 176 super.visitParameter(name, access); 177 } 178 179 @Override 180 public AnnotationVisitor visitAnnotationDefault() { 181 deferredCode.flush(); 182 return super.visitAnnotationDefault(); 183 } 184 185 @Override 186 public AnnotationVisitor visitAnnotation(String desc, boolean visible) { 187 deferredCode.flush(); 188 return super.visitAnnotation(desc, visible); 189 } 190 191 @Override 192 public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { 193 deferredCode.flush(); 194 return super.visitTypeAnnotation(typeRef, typePath, desc, visible); 195 } 196 197 @Override 198 public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) { 199 deferredCode.flush(); 200 return super.visitParameterAnnotation(parameter, desc, visible); 201 } 202 203 @Override 204 public void visitAttribute(Attribute attr) { 205 deferredCode.flush(); 206 super.visitAttribute(attr); 207 } 208 209 @Override 210 public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) { 211 deferredCode.flush(); 212 super.visitFrame(type, nLocal, local, nStack, stack); 213 } 214 215 @Override 216 public void visitIntInsn(int opcode, int operand) { 217 deferredCode.flush(); 218 super.visitIntInsn(opcode, operand); 219 } 220 221 @Override 222 public void visitMethodInsn(int opcode, String owner, String name, String desc) { 223 deferredCode.flush(); 224 super.visitMethodInsn(opcode, owner, name, desc); 225 } 226 227 @Override 228 public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) { 229 deferredCode.flush(); 230 super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); 231 } 232 233 @Override 234 public void visitJumpInsn(int opcode, Label label) { 235 deferredCode.flush(); 236 super.visitJumpInsn(opcode, label); 237 } 238 239 @Override 240 public void visitLabel(Label label) { 241 deferredCode.flush(); 242 super.visitLabel(label); 243 } 244 245 @Override 246 public void visitLdcInsn(Object cst) { 247 deferredCode.flush(); 248 super.visitLdcInsn(cst); 249 } 250 251 @Override 252 public void visitIincInsn(int var, int increment) { 253 deferredCode.flush(); 254 super.visitIincInsn(var, increment); 255 } 256 257 @Override 258 public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) { 259 deferredCode.flush(); 260 super.visitTableSwitchInsn(min, max, dflt, labels); 261 } 262 263 @Override 264 public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { 265 deferredCode.flush(); 266 super.visitLookupSwitchInsn(dflt, keys, labels); 267 } 268 269 @Override 270 public void visitMultiANewArrayInsn(String desc, int dims) { 271 deferredCode.flush(); 272 super.visitMultiANewArrayInsn(desc, dims); 273 } 274 275 @Override 276 public AnnotationVisitor visitInsnAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { 277 deferredCode.flush(); 278 return super.visitInsnAnnotation(typeRef, typePath, desc, visible); 279 } 280 281 @Override 282 public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { 283 deferredCode.flush(); 284 super.visitTryCatchBlock(start, end, handler, type); 285 } 286 287 @Override 288 public AnnotationVisitor visitTryCatchAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { 289 deferredCode.flush(); 290 return super.visitTryCatchAnnotation(typeRef, typePath, desc, visible); 291 } 292 293 @Override 294 public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) { 295 deferredCode.flush(); 296 super.visitLocalVariable(name, desc, signature, start, end, index); 297 } 298 299 @Override 300 public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, TypePath typePath, Label[] start, Label[] end, int[] index, String desc, boolean visible) { 301 deferredCode.flush(); 302 return super.visitLocalVariableAnnotation(typeRef, typePath, start, end, index, desc, visible); 303 } 304 305 @Override 306 public void visitLineNumber(int line, Label start) { 307 deferredCode.flush(); 308 super.visitLineNumber(line, start); 309 } 310 311 @Override 312 public void visitMaxs(int maxStack, int maxLocals) { 313 deferredCode.flush(); 314 super.visitMaxs(maxStack, maxLocals); 315 } 316 317 @Override 318 public void visitEnd() { 319 deferredCode.flush(); 320 super.visitEnd(); 321 } 322}