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.ASM7, 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 (C_MODEL.equals(owner) && INIT.equals(name)) { 128 addConstructorInit(owner); 129 return; 130 } 131 132 if (opcode == INVOKESPECIAL && name.equals(INIT) && desc.equals(NOARG_VOID)) { 133 if (meta.isSuperClassEntity()) { 134 if (meta.isLog(3)) { 135 meta.log("... skipping intercept <init> ... handled by super class... CONSTRUCTOR: owner:" + owner + " " + constructorDesc); 136 } 137 } else if (owner.equals(meta.getClassName())) { 138 if (meta.isLog(3)) { 139 meta.log("... skipping intercept <init> ... handled by other constructor... CONSTRUCTOR: owner:" + owner + " " + constructorDesc); 140 } 141 } else if (owner.equals(meta.getSuperClassName())){ 142 addConstructorInit(owner); 143 } else { 144 if (meta.isLog(3)) { 145 meta.log("... skipping intercept <init> ... incorrect type "+owner); 146 } 147 } 148 } 149 } 150 151 private void addConstructorInit(String owner) { 152 153 if (meta.isLog(2)) { 154 meta.log("... adding intercept <init> in CONSTRUCTOR:" + constructorDesc + " OWNER/SUPER:" + owner); 155 } 156 157 if (constructorInitializationDone) { 158 // hopefully this is never called but put it in here to be on the safe side. 159 String msg = "Error in Enhancement. Only expecting to add <init> of intercept object" 160 + " once but it is trying to add it twice for " + meta.getClassName() + " CONSTRUCTOR:" 161 + constructorDesc+ " OWNER:" + owner; 162 System.err.println(msg); 163 164 } else { 165 // add the initialisation of the intercept object 166 super.visitVarInsn(ALOAD, 0); 167 super.visitTypeInsn(NEW, C_INTERCEPT); 168 super.visitInsn(DUP); 169 super.visitVarInsn(ALOAD, 0); 170 171 super.visitMethodInsn(INVOKESPECIAL, C_INTERCEPT, INIT, "(Ljava/lang/Object;)V", false); 172 super.visitFieldInsn(PUTFIELD, className, INTERCEPT_FIELD, EnhanceConstants.L_INTERCEPT); 173 174 if (meta.isLog(8)) { 175 meta.log("... constructorInitializationDone " + owner); 176 } 177 constructorInitializationDone = true; 178 } 179 } 180 181 182 @Override 183 public void visitParameter(String name, int access) { 184 deferredCode.flush(); 185 super.visitParameter(name, access); 186 } 187 188 @Override 189 public AnnotationVisitor visitAnnotationDefault() { 190 deferredCode.flush(); 191 return super.visitAnnotationDefault(); 192 } 193 194 @Override 195 public AnnotationVisitor visitAnnotation(String desc, boolean visible) { 196 deferredCode.flush(); 197 return super.visitAnnotation(desc, visible); 198 } 199 200 @Override 201 public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { 202 deferredCode.flush(); 203 return super.visitTypeAnnotation(typeRef, typePath, desc, visible); 204 } 205 206 @Override 207 public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) { 208 deferredCode.flush(); 209 return super.visitParameterAnnotation(parameter, desc, visible); 210 } 211 212 @Override 213 public void visitAttribute(Attribute attr) { 214 deferredCode.flush(); 215 super.visitAttribute(attr); 216 } 217 218 @Override 219 public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) { 220 deferredCode.flush(); 221 super.visitFrame(type, nLocal, local, nStack, stack); 222 } 223 224 @Override 225 public void visitIntInsn(int opcode, int operand) { 226 deferredCode.flush(); 227 super.visitIntInsn(opcode, operand); 228 } 229 230 @Override 231 public void visitMethodInsn(int opcode, String owner, String name, String desc) { 232 deferredCode.flush(); 233 super.visitMethodInsn(opcode, owner, name, desc); 234 } 235 236 @Override 237 public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) { 238 deferredCode.flush(); 239 super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); 240 } 241 242 @Override 243 public void visitJumpInsn(int opcode, Label label) { 244 deferredCode.flush(); 245 super.visitJumpInsn(opcode, label); 246 } 247 248 @Override 249 public void visitLabel(Label label) { 250 deferredCode.flush(); 251 super.visitLabel(label); 252 } 253 254 @Override 255 public void visitLdcInsn(Object cst) { 256 deferredCode.flush(); 257 super.visitLdcInsn(cst); 258 } 259 260 @Override 261 public void visitIincInsn(int var, int increment) { 262 deferredCode.flush(); 263 super.visitIincInsn(var, increment); 264 } 265 266 @Override 267 public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) { 268 deferredCode.flush(); 269 super.visitTableSwitchInsn(min, max, dflt, labels); 270 } 271 272 @Override 273 public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { 274 deferredCode.flush(); 275 super.visitLookupSwitchInsn(dflt, keys, labels); 276 } 277 278 @Override 279 public void visitMultiANewArrayInsn(String desc, int dims) { 280 deferredCode.flush(); 281 super.visitMultiANewArrayInsn(desc, dims); 282 } 283 284 @Override 285 public AnnotationVisitor visitInsnAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { 286 deferredCode.flush(); 287 return super.visitInsnAnnotation(typeRef, typePath, desc, visible); 288 } 289 290 @Override 291 public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { 292 deferredCode.flush(); 293 super.visitTryCatchBlock(start, end, handler, type); 294 } 295 296 @Override 297 public AnnotationVisitor visitTryCatchAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { 298 deferredCode.flush(); 299 return super.visitTryCatchAnnotation(typeRef, typePath, desc, visible); 300 } 301 302 @Override 303 public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) { 304 deferredCode.flush(); 305 super.visitLocalVariable(name, desc, signature, start, end, index); 306 } 307 308 @Override 309 public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, TypePath typePath, Label[] start, Label[] end, int[] index, String desc, boolean visible) { 310 deferredCode.flush(); 311 return super.visitLocalVariableAnnotation(typeRef, typePath, start, end, index, desc, visible); 312 } 313 314 @Override 315 public void visitLineNumber(int line, Label start) { 316 deferredCode.flush(); 317 super.visitLineNumber(line, start); 318 } 319 320 @Override 321 public void visitMaxs(int maxStack, int maxLocals) { 322 deferredCode.flush(); 323 super.visitMaxs(maxStack, maxLocals); 324 } 325 326 @Override 327 public void visitEnd() { 328 deferredCode.flush(); 329 super.visitEnd(); 330 } 331}