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}