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}