1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2002,2008 Oracle.  All rights reserved.
5 *
6 * $Id: BytecodeEnhancer.java,v 1.1 2008/02/07 17:12:28 mark Exp $
7 */
8
9package com.sleepycat.persist.model;
10
11import static com.sleepycat.asm.Opcodes.ACC_ABSTRACT;
12import static com.sleepycat.asm.Opcodes.ACC_PRIVATE;
13import static com.sleepycat.asm.Opcodes.ACC_PUBLIC;
14import static com.sleepycat.asm.Opcodes.ACC_STATIC;
15import static com.sleepycat.asm.Opcodes.ACC_TRANSIENT;
16import static com.sleepycat.asm.Opcodes.ACONST_NULL;
17import static com.sleepycat.asm.Opcodes.ALOAD;
18import static com.sleepycat.asm.Opcodes.ANEWARRAY;
19import static com.sleepycat.asm.Opcodes.ARETURN;
20import static com.sleepycat.asm.Opcodes.BIPUSH;
21import static com.sleepycat.asm.Opcodes.CHECKCAST;
22import static com.sleepycat.asm.Opcodes.DCMPL;
23import static com.sleepycat.asm.Opcodes.DCONST_0;
24import static com.sleepycat.asm.Opcodes.DUP;
25import static com.sleepycat.asm.Opcodes.FCMPL;
26import static com.sleepycat.asm.Opcodes.FCONST_0;
27import static com.sleepycat.asm.Opcodes.GETFIELD;
28import static com.sleepycat.asm.Opcodes.GOTO;
29import static com.sleepycat.asm.Opcodes.ICONST_0;
30import static com.sleepycat.asm.Opcodes.ICONST_1;
31import static com.sleepycat.asm.Opcodes.ICONST_2;
32import static com.sleepycat.asm.Opcodes.ICONST_3;
33import static com.sleepycat.asm.Opcodes.ICONST_4;
34import static com.sleepycat.asm.Opcodes.ICONST_5;
35import static com.sleepycat.asm.Opcodes.IFEQ;
36import static com.sleepycat.asm.Opcodes.IFGT;
37import static com.sleepycat.asm.Opcodes.IFLE;
38import static com.sleepycat.asm.Opcodes.IFNE;
39import static com.sleepycat.asm.Opcodes.IFNONNULL;
40import static com.sleepycat.asm.Opcodes.IF_ICMPNE;
41import static com.sleepycat.asm.Opcodes.ILOAD;
42import static com.sleepycat.asm.Opcodes.INVOKEINTERFACE;
43import static com.sleepycat.asm.Opcodes.INVOKESPECIAL;
44import static com.sleepycat.asm.Opcodes.INVOKESTATIC;
45import static com.sleepycat.asm.Opcodes.INVOKEVIRTUAL;
46import static com.sleepycat.asm.Opcodes.IRETURN;
47import static com.sleepycat.asm.Opcodes.ISUB;
48import static com.sleepycat.asm.Opcodes.LCMP;
49import static com.sleepycat.asm.Opcodes.LCONST_0;
50import static com.sleepycat.asm.Opcodes.NEW;
51import static com.sleepycat.asm.Opcodes.POP;
52import static com.sleepycat.asm.Opcodes.PUTFIELD;
53import static com.sleepycat.asm.Opcodes.RETURN;
54
55import java.math.BigInteger;
56import java.util.ArrayList;
57import java.util.Collections;
58import java.util.Comparator;
59import java.util.Date;
60import java.util.HashMap;
61import java.util.List;
62import java.util.Map;
63
64import com.sleepycat.asm.AnnotationVisitor;
65import com.sleepycat.asm.Attribute;
66import com.sleepycat.asm.ClassAdapter;
67import com.sleepycat.asm.ClassVisitor;
68import com.sleepycat.asm.FieldVisitor;
69import com.sleepycat.asm.Label;
70import com.sleepycat.asm.MethodVisitor;
71import com.sleepycat.asm.Type;
72
73/**
74 * An ASM ClassVisitor that examines a class, throws NotPersistentException if
75 * it is not persistent, or enhances it if it is persistent.  A class is
76 * persistent if it contains the @Entity or @Persistent annotations.  A
77 * resulting enhanced class implements the com.sleepycat.persist.impl.Enhanced
78 * interface.
79 *
80 * <p>NotPersistentException is thrown to abort the transformation in order to
81 * avoid making two passes over the class file (one to look for the annotations
82 * and another to enhance the bytecode) or outputing a class that isn't
83 * enhanced.  By aborting the transformation as soon as we detect that the
84 * annotations are missing, we make only one partial pass for a non-persistent
85 * class.</p>
86 *
87 * @author Mark Hayes
88 */
89class BytecodeEnhancer extends ClassAdapter {
90
91    /** Thrown when we determine that a class is not persistent. */
92    static class NotPersistentException extends RuntimeException {}
93
94    /** A static instance is used to avoid fillInStaceTrace overhead. */
95    private static final NotPersistentException NOT_PERSISTENT =
96        new NotPersistentException();
97
98    private static final Map<String,Integer> PRIMITIVE_WRAPPERS =
99        new HashMap<String,Integer>();
100    static {
101        PRIMITIVE_WRAPPERS.put(Boolean.class.getName(), Type.BOOLEAN);
102        PRIMITIVE_WRAPPERS.put(Character.class.getName(), Type.CHAR);
103        PRIMITIVE_WRAPPERS.put(Byte.class.getName(), Type.BYTE);
104        PRIMITIVE_WRAPPERS.put(Short.class.getName(), Type.SHORT);
105        PRIMITIVE_WRAPPERS.put(Integer.class.getName(), Type.INT);
106        PRIMITIVE_WRAPPERS.put(Long.class.getName(), Type.LONG);
107        PRIMITIVE_WRAPPERS.put(Float.class.getName(), Type.FLOAT);
108        PRIMITIVE_WRAPPERS.put(Double.class.getName(), Type.DOUBLE);
109    }
110
111    private String className;
112    private String superclassName;
113    private boolean isPersistent;
114    private boolean isAbstract;
115    private boolean hasDefaultConstructor;
116    private boolean hasPersistentSuperclass;
117    private boolean isCompositeKey;
118    private FieldInfo priKeyField;
119    private List<FieldInfo> secKeyFields;
120    private List<FieldInfo> nonKeyFields;
121    private String staticBlockMethod;
122
123    BytecodeEnhancer(ClassVisitor parentVisitor) {
124        super(parentVisitor);
125        secKeyFields = new ArrayList<FieldInfo>();
126        nonKeyFields = new ArrayList<FieldInfo>();
127    }
128
129    @Override
130    public void visit(int version,
131                      int access,
132                      String name,
133                      String sig,
134                      String superName,
135                      String[] interfaces) {
136        className = name;
137        superclassName = superName;
138        final String ENHANCED = "com/sleepycat/persist/impl/Enhanced";
139        if (containsString(interfaces, ENHANCED)) {
140            throw abort();
141        }
142        interfaces = appendString(interfaces, ENHANCED);
143        isAbstract = ((access & ACC_ABSTRACT) != 0);
144        hasPersistentSuperclass =
145            (superName != null && !superName.equals("java/lang/Object"));
146        super.visit(version, access, name, sig, superName, interfaces);
147    }
148
149    @Override
150    public void visitSource(String source, String debug) {
151        super.visitSource(source, debug);
152    }
153
154    @Override
155    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
156        if (desc.equals("Lcom/sleepycat/persist/model/Entity;") ||
157            desc.equals("Lcom/sleepycat/persist/model/Persistent;")) {
158            isPersistent = true;
159        }
160        return super.visitAnnotation(desc, visible);
161    }
162
163    @Override
164    public FieldVisitor visitField(int access,
165                                   String name,
166                                   String desc,
167                                   String sig,
168                                   Object value) {
169        if (!isPersistent) {
170            throw abort();
171        }
172        FieldVisitor ret = super.visitField(access, name, desc, sig, value);
173        if ((access & (ACC_STATIC | ACC_TRANSIENT)) == 0) {
174            FieldInfo info = new FieldInfo(ret, name, desc);
175            nonKeyFields.add(info);
176            ret = info;
177        }
178        return ret;
179    }
180
181    @Override
182    public MethodVisitor visitMethod(int access,
183                                     String name,
184                                     String desc,
185                                     String sig,
186                                     String[] exceptions) {
187        if (!isPersistent) {
188            throw abort();
189        }
190        if ("<init>".equals(name) && "()V".equals(desc)) {
191            hasDefaultConstructor = true;
192        }
193        if ("<clinit>".equals(name)) {
194            if (staticBlockMethod != null) {
195                throw new IllegalStateException();
196            }
197            staticBlockMethod = "bdbExistingStaticBlock";
198            return cv.visitMethod
199                (ACC_PRIVATE + ACC_STATIC, staticBlockMethod, "()V", null,
200                 null);
201        }
202        return super.visitMethod(access, name, desc, sig, exceptions);
203    }
204
205    @Override
206    public void visitEnd() {
207        if (!isPersistent || !hasDefaultConstructor) {
208            throw abort();
209        }
210        /* Generate new code at the end of the class. */
211        sortFields();
212        genBdbNewInstance();
213        genBdbNewArray();
214        genBdbIsPriKeyFieldNullOrZero();
215        genBdbWritePriKeyField();
216        genBdbReadPriKeyField();
217        genBdbWriteSecKeyFields();
218        genBdbReadSecKeyFields();
219        genBdbWriteNonKeyFields();
220        genBdbReadNonKeyFields();
221        genBdbGetField();
222        genBdbSetField();
223        genStaticBlock();
224        super.visitEnd();
225    }
226
227    private void sortFields() {
228        /*
229        System.out.println("AllFields: " + nonKeyFields);
230        //*/
231        if (nonKeyFields.size() == 0) {
232            return;
233        }
234        isCompositeKey = true;
235        for (FieldInfo field : nonKeyFields) {
236            if (field.order == null) {
237                isCompositeKey = false;
238            }
239        }
240        if (isCompositeKey) {
241            Collections.sort(nonKeyFields, new Comparator<FieldInfo>() {
242                public int compare(FieldInfo f1, FieldInfo f2) {
243                    return f1.order.value - f2.order.value;
244                }
245            });
246        } else {
247            for (int i = 0; i < nonKeyFields.size();) {
248                FieldInfo field = nonKeyFields.get(i);
249                if (field.isPriKey) {
250                    if (priKeyField == null) {
251                        priKeyField = field;
252                        nonKeyFields.remove(i);
253                    }
254                } else if (field.isSecKey) {
255                    secKeyFields.add(field);
256                    nonKeyFields.remove(i);
257                } else {
258                    i += 1;
259                }
260            }
261            Comparator<FieldInfo> cmp = new Comparator<FieldInfo>() {
262                public int compare(FieldInfo f1, FieldInfo f2) {
263                    return f1.name.compareTo(f2.name);
264                }
265            };
266            Collections.sort(secKeyFields, cmp);
267            Collections.sort(nonKeyFields, cmp);
268        }
269        /*
270        System.out.println("PriKey: " + priKeyField);
271        System.out.println("SecKeys: " + secKeyFields);
272        System.out.println("NonKeys: " + nonKeyFields);
273        //*/
274    }
275
276    /**
277     * Outputs code in a static block to register the prototype instance:
278     *
279     *  static {
280     *      EnhancedAccessor.registerClass(TheClassName, new TheClass());
281     *      // or for an abstract class:
282     *      EnhancedAccessor.registerClass(TheClassName, null);
283     *  }
284     */
285    private void genStaticBlock() {
286        MethodVisitor mv =
287            cv.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
288        mv.visitCode();
289        if (staticBlockMethod != null) {
290            mv.visitMethodInsn
291                (INVOKESTATIC, className, staticBlockMethod, "()V");
292        }
293        mv.visitLdcInsn(className.replace('/', '.'));
294        if (isAbstract) {
295            mv.visitInsn(ACONST_NULL);
296        } else {
297            mv.visitTypeInsn(NEW, className);
298            mv.visitInsn(DUP);
299            mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "()V");
300        }
301        mv.visitMethodInsn
302            (INVOKESTATIC, "com/sleepycat/persist/impl/EnhancedAccessor",
303             "registerClass",
304             "(Ljava/lang/String;Lcom/sleepycat/persist/impl/Enhanced;)V");
305        mv.visitInsn(RETURN);
306        mv.visitMaxs(3, 0);
307        mv.visitEnd();
308    }
309
310    /**
311     *  public Object bdbNewInstance() {
312     *      return new TheClass();
313     *      // or if abstract:
314     *      return null;
315     *  }
316     */
317    private void genBdbNewInstance() {
318        MethodVisitor mv = cv.visitMethod
319            (ACC_PUBLIC, "bdbNewInstance", "()Ljava/lang/Object;", null, null);
320        mv.visitCode();
321        if (isAbstract) {
322            mv.visitInsn(ACONST_NULL);
323            mv.visitInsn(ARETURN);
324            mv.visitMaxs(1, 1);
325        } else {
326            mv.visitTypeInsn(NEW, className);
327            mv.visitInsn(DUP);
328            mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "()V");
329            mv.visitInsn(ARETURN);
330            mv.visitMaxs(2, 1);
331        }
332        mv.visitEnd();
333    }
334
335    /**
336     *  public Object bdbNewArray(int len) {
337     *      return new TheClass[len];
338     *      // or if abstract:
339     *      return null;
340     *  }
341     */
342    private void genBdbNewArray() {
343        MethodVisitor mv = cv.visitMethod
344            (ACC_PUBLIC, "bdbNewArray", "(I)Ljava/lang/Object;", null, null);
345        mv.visitCode();
346        if (isAbstract) {
347            mv.visitInsn(ACONST_NULL);
348            mv.visitInsn(ARETURN);
349            mv.visitMaxs(1, 2);
350        } else {
351            mv.visitVarInsn(ILOAD, 1);
352            mv.visitTypeInsn(ANEWARRAY, className);
353            mv.visitInsn(ARETURN);
354            mv.visitMaxs(1, 2);
355            mv.visitEnd();
356        }
357    }
358
359    /**
360     *  public boolean bdbIsPriKeyFieldNullOrZero() {
361     *      return theField == null; // or zero or false, as appropriate
362     *      // or if no primary key but has superclass:
363     *      return super.bdbIsPriKeyFieldNullOrZero();
364     *  }
365     */
366    private void genBdbIsPriKeyFieldNullOrZero() {
367        MethodVisitor mv = cv.visitMethod
368            (ACC_PUBLIC, "bdbIsPriKeyFieldNullOrZero", "()Z", null, null);
369        mv.visitCode();
370        if (priKeyField != null) {
371            mv.visitVarInsn(ALOAD, 0);
372            mv.visitFieldInsn
373                (GETFIELD, className, priKeyField.name,
374                 priKeyField.type.getDescriptor());
375            Label l0 = new Label();
376            if (isRefType(priKeyField.type)) {
377                mv.visitJumpInsn(IFNONNULL, l0);
378            } else {
379                genBeforeCompareToZero(mv, priKeyField.type);
380                mv.visitJumpInsn(IFNE, l0);
381            }
382            mv.visitInsn(ICONST_1);
383            Label l1 = new Label();
384            mv.visitJumpInsn(GOTO, l1);
385            mv.visitLabel(l0);
386            mv.visitInsn(ICONST_0);
387            mv.visitLabel(l1);
388        } else if (hasPersistentSuperclass) {
389            mv.visitVarInsn(ALOAD, 0);
390            mv.visitMethodInsn
391                (INVOKESPECIAL, superclassName, "bdbIsPriKeyFieldNullOrZero",
392                 "()Z");
393        } else {
394            mv.visitInsn(ICONST_0);
395        }
396        mv.visitInsn(IRETURN);
397        mv.visitMaxs(1, 1);
398        mv.visitEnd();
399    }
400
401    /**
402     *  public void bdbWritePriKeyField(EntityOutput output, Format format) {
403     *      output.writeKeyObject(theField, format);
404     *      // or
405     *      output.writeInt(theField); // and other simple types
406     *      // or if no primary key but has superclass:
407     *      return super.bdbWritePriKeyField(output, format);
408     *  }
409     */
410    private void genBdbWritePriKeyField() {
411        MethodVisitor mv = cv.visitMethod
412            (ACC_PUBLIC, "bdbWritePriKeyField",
413             "(Lcom/sleepycat/persist/impl/EntityOutput;" +
414              "Lcom/sleepycat/persist/impl/Format;)V",
415             null, null);
416        mv.visitCode();
417        if (priKeyField != null) {
418            if (!genWriteSimpleKeyField(mv, priKeyField)) {
419                /* For a non-simple type, call EntityOutput.writeKeyObject. */
420                mv.visitVarInsn(ALOAD, 1);
421                mv.visitVarInsn(ALOAD, 0);
422                mv.visitFieldInsn
423                    (GETFIELD, className, priKeyField.name,
424                     priKeyField.type.getDescriptor());
425                mv.visitVarInsn(ALOAD, 2);
426                mv.visitMethodInsn
427                    (INVOKEINTERFACE,
428                     "com/sleepycat/persist/impl/EntityOutput",
429                     "writeKeyObject",
430                     "(Ljava/lang/Object;" +
431                      "Lcom/sleepycat/persist/impl/Format;)V");
432            }
433        } else if (hasPersistentSuperclass) {
434            mv.visitVarInsn(ALOAD, 0);
435            mv.visitVarInsn(ALOAD, 1);
436            mv.visitVarInsn(ALOAD, 2);
437            mv.visitMethodInsn
438                (INVOKESPECIAL, superclassName, "bdbWritePriKeyField",
439                 "(Lcom/sleepycat/persist/impl/EntityOutput;" +
440                  "Lcom/sleepycat/persist/impl/Format;)V");
441        }
442        mv.visitInsn(RETURN);
443        mv.visitMaxs(3, 3);
444        mv.visitEnd();
445    }
446
447    /**
448     *  public void bdbReadPriKeyField(EntityInput input, Format format) {
449     *      theField = (TheFieldClass) input.readKeyObject(format);
450     *      // or
451     *      theField = input.readInt(); // and other simple types
452     *      // or if no primary key but has superclass:
453     *      super.bdbReadPriKeyField(input, format);
454     *  }
455     */
456    private void genBdbReadPriKeyField() {
457        MethodVisitor mv = cv.visitMethod
458            (ACC_PUBLIC, "bdbReadPriKeyField",
459             "(Lcom/sleepycat/persist/impl/EntityInput;" +
460              "Lcom/sleepycat/persist/impl/Format;)V",
461             null, null);
462        mv.visitCode();
463        if (priKeyField != null) {
464            if (!genReadSimpleKeyField(mv, priKeyField)) {
465                /* For a non-simple type, call EntityInput.readKeyObject. */
466                mv.visitVarInsn(ALOAD, 0);
467                mv.visitVarInsn(ALOAD, 1);
468                mv.visitVarInsn(ALOAD, 2);
469                mv.visitMethodInsn
470                    (INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityInput",
471                     "readKeyObject",
472                     "(Lcom/sleepycat/persist/impl/Format;)" +
473                     "Ljava/lang/Object;");
474                mv.visitTypeInsn(CHECKCAST, getTypeInstName(priKeyField.type));
475                mv.visitFieldInsn
476                    (PUTFIELD, className, priKeyField.name,
477                     priKeyField.type.getDescriptor());
478            }
479        } else if (hasPersistentSuperclass) {
480            mv.visitVarInsn(ALOAD, 0);
481            mv.visitVarInsn(ALOAD, 1);
482            mv.visitVarInsn(ALOAD, 2);
483            mv.visitMethodInsn
484                (INVOKESPECIAL, superclassName, "bdbReadPriKeyField",
485                 "(Lcom/sleepycat/persist/impl/EntityInput;" +
486                  "Lcom/sleepycat/persist/impl/Format;)V");
487        }
488        mv.visitInsn(RETURN);
489        mv.visitMaxs(3, 3);
490        mv.visitEnd();
491    }
492
493    /**
494     *  public void bdbWriteSecKeyFields(EntityOutput output) {
495     *      output.registerPriKeyObject(priKeyField); // if an object
496     *      super.bdbWriteSecKeyFields(EntityOutput output); // if has super
497     *      output.writeInt(secKeyField1);
498     *      output.writeObject(secKeyField2, null);
499     *      // etc
500     *  }
501     */
502    private void genBdbWriteSecKeyFields() {
503        MethodVisitor mv = cv.visitMethod
504            (ACC_PUBLIC, "bdbWriteSecKeyFields",
505             "(Lcom/sleepycat/persist/impl/EntityOutput;)V", null, null);
506        mv.visitCode();
507        if (priKeyField != null && isRefType(priKeyField.type)) {
508            genRegisterPrimaryKey(mv, false);
509        }
510        if (hasPersistentSuperclass) {
511            mv.visitVarInsn(ALOAD, 0);
512            mv.visitVarInsn(ALOAD, 1);
513            mv.visitMethodInsn
514                (INVOKESPECIAL, superclassName, "bdbWriteSecKeyFields",
515                 "(Lcom/sleepycat/persist/impl/EntityOutput;)V");
516        }
517        for (FieldInfo field : secKeyFields) {
518            genWriteField(mv, field);
519        }
520        mv.visitInsn(RETURN);
521        mv.visitMaxs(2, 2);
522        mv.visitEnd();
523    }
524
525    /**
526     *  public void bdbReadSecKeyFields(EntityInput input,
527     *                                  int startField,
528     *                                  int endField,
529     *                                  int superLevel) {
530     *      input.registerPriKeyObject(priKeyField); // if an object
531     *      // if has super:
532     *      if (superLevel != 0) {
533     *          super.bdbReadSecKeyFields(..., superLevel - 1);
534     *      }
535     *      if (superLevel <= 0) {
536     *          switch (startField) {
537     *          case 0:
538     *              secKeyField1 = input.readInt();
539     *              if (endField == 0) break;
540     *          case 1:
541     *              secKeyField2 = (String) input.readObject();
542     *              if (endField == 1) break;
543     *          case 2:
544     *              secKeyField3 = input.readInt();
545     *          }
546     *      }
547     *  }
548     */
549    private void genBdbReadSecKeyFields() {
550        MethodVisitor mv = cv.visitMethod
551            (ACC_PUBLIC, "bdbReadSecKeyFields",
552             "(Lcom/sleepycat/persist/impl/EntityInput;III)V", null, null);
553        mv.visitCode();
554        if (priKeyField != null && isRefType(priKeyField.type)) {
555            genRegisterPrimaryKey(mv, true);
556        }
557        genReadSuperKeyFields(mv, true);
558        genReadFieldSwitch(mv, secKeyFields);
559        mv.visitInsn(RETURN);
560        mv.visitMaxs(5, 5);
561        mv.visitEnd();
562    }
563
564    /**
565     *      output.registerPriKeyObject(priKeyField);
566     *      // or
567     *      input.registerPriKeyObject(priKeyField);
568     */
569    private void genRegisterPrimaryKey(MethodVisitor mv, boolean input) {
570        String entityInputOrOutputClass =
571            input ? "com/sleepycat/persist/impl/EntityInput"
572                  : "com/sleepycat/persist/impl/EntityOutput";
573        mv.visitVarInsn(ALOAD, 1);
574        mv.visitVarInsn(ALOAD, 0);
575        mv.visitFieldInsn
576            (GETFIELD, className, priKeyField.name,
577             priKeyField.type.getDescriptor());
578        mv.visitMethodInsn
579            (INVOKEINTERFACE, entityInputOrOutputClass, "registerPriKeyObject",
580             "(Ljava/lang/Object;)V");
581    }
582
583    /**
584     *  public void bdbWriteNonKeyFields(EntityOutput output) {
585     *      super.bdbWriteNonKeyFields(output); // if has super
586     *      output.writeInt(nonKeyField1);
587     *      output.writeObject(nonKeyField2, null);
588     *      // etc
589     *  }
590     */
591    private void genBdbWriteNonKeyFields() {
592        MethodVisitor mv = cv.visitMethod
593            (ACC_PUBLIC, "bdbWriteNonKeyFields",
594             "(Lcom/sleepycat/persist/impl/EntityOutput;)V", null, null);
595        mv.visitCode();
596        if (hasPersistentSuperclass) {
597            mv.visitVarInsn(ALOAD, 0);
598            mv.visitVarInsn(ALOAD, 1);
599            mv.visitMethodInsn
600                (INVOKESPECIAL, superclassName, "bdbWriteNonKeyFields",
601                 "(Lcom/sleepycat/persist/impl/EntityOutput;)V");
602        }
603        if (isCompositeKey) {
604            for (FieldInfo field : nonKeyFields) {
605                genWriteSimpleKeyField(mv, field);
606                /* Ignore non-simple (illegal) types for composite key. */
607            }
608        } else {
609            for (FieldInfo field : nonKeyFields) {
610                genWriteField(mv, field);
611            }
612        }
613        mv.visitInsn(RETURN);
614        mv.visitMaxs(2, 2);
615        mv.visitEnd();
616    }
617
618    /**
619     *  public void bdbReadNonKeyFields(EntityInput input,
620     *                                  int startField,
621     *                                  int endField,
622     *                                  int superLevel) {
623     *      // if has super:
624     *      if (superLevel != 0) {
625     *          super.bdbReadNonKeyFields(..., superLevel - 1);
626     *      }
627     *      nonKeyField1 = input.readInt();
628     *      nonKeyField2 = (String) input.readObject();
629     *      // etc
630     *      // or like bdbReadSecKeyFields if not a composite key class
631     *  }
632     */
633    private void genBdbReadNonKeyFields() {
634        MethodVisitor mv = cv.visitMethod
635            (ACC_PUBLIC, "bdbReadNonKeyFields",
636             "(Lcom/sleepycat/persist/impl/EntityInput;III)V", null, null);
637        mv.visitCode();
638        if (isCompositeKey) {
639            for (FieldInfo field : nonKeyFields) {
640                genReadSimpleKeyField(mv, field);
641                /* Ignore non-simple (illegal) types for composite key. */
642            }
643        } else {
644            genReadSuperKeyFields(mv, false);
645            genReadFieldSwitch(mv, nonKeyFields);
646        }
647        mv.visitInsn(RETURN);
648        mv.visitMaxs(5, 5);
649        mv.visitEnd();
650    }
651
652    /**
653     *      output.writeInt(field); // and other primitives
654     *      // or
655     *      output.writeObject(field, null);
656     */
657    private void genWriteField(MethodVisitor mv, FieldInfo field) {
658        mv.visitVarInsn(ALOAD, 1);
659        mv.visitVarInsn(ALOAD, 0);
660        mv.visitFieldInsn
661            (GETFIELD, className, field.name, field.type.getDescriptor());
662        int sort = field.type.getSort();
663        if (sort == Type.OBJECT || sort == Type.ARRAY) {
664            mv.visitInsn(ACONST_NULL);
665            mv.visitMethodInsn
666                (INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityOutput",
667                 "writeObject",
668                 "(Ljava/lang/Object;Lcom/sleepycat/persist/impl/Format;)V");
669        } else {
670            genWritePrimitive(mv, sort);
671        }
672    }
673
674    /**
675     * Generates writing of a simple type key field, or returns false if the
676     * key field is not a simple type (i.e., it is a composite key type).
677     *
678     *      output.writeInt(theField); // and other primitives
679     *      // or
680     *      output.writeInt(theField.intValue()); // and other simple types
681     *      // or returns false
682     */
683    private boolean genWriteSimpleKeyField(MethodVisitor mv, FieldInfo field) {
684        if (genWritePrimitiveField(mv, field)) {
685            return true;
686        }
687        String fieldClassName = field.type.getClassName();
688        if (!isSimpleRefType(fieldClassName)) {
689            return false;
690        }
691        mv.visitVarInsn(ALOAD, 1);
692        mv.visitVarInsn(ALOAD, 0);
693        mv.visitFieldInsn
694            (GETFIELD, className, field.name, field.type.getDescriptor());
695        Integer sort = PRIMITIVE_WRAPPERS.get(fieldClassName);
696        if (sort != null) {
697            genUnwrapPrimitive(mv, sort);
698            genWritePrimitive(mv, sort);
699        } else if (fieldClassName.equals(Date.class.getName())) {
700            mv.visitMethodInsn
701                (INVOKEVIRTUAL, "java/util/Date", "getTime", "()J");
702            genWritePrimitive(mv, Type.LONG);
703        } else if (fieldClassName.equals(String.class.getName())) {
704            mv.visitMethodInsn
705                (INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityOutput",
706                 "writeString",
707                 "(Ljava/lang/String;)Lcom/sleepycat/bind/tuple/TupleOutput;");
708            mv.visitInsn(POP);
709        } else if (fieldClassName.equals(BigInteger.class.getName())) {
710            mv.visitMethodInsn
711                (INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityOutput",
712                 "writeBigInteger",
713             "(Ljava/math/BigInteger;)Lcom/sleepycat/bind/tuple/TupleOutput;");
714            mv.visitInsn(POP);
715        } else {
716            throw new IllegalStateException(fieldClassName);
717        }
718        return true;
719    }
720
721    private boolean genWritePrimitiveField(MethodVisitor mv, FieldInfo field) {
722        int sort = field.type.getSort();
723        if (sort == Type.OBJECT || sort == Type.ARRAY) {
724            return false;
725        }
726        mv.visitVarInsn(ALOAD, 1);
727        mv.visitVarInsn(ALOAD, 0);
728        mv.visitFieldInsn
729            (GETFIELD, className, field.name, field.type.getDescriptor());
730        genWritePrimitive(mv, sort);
731        return true;
732    }
733
734    /**
735     *      // if has super:
736     *      if (superLevel != 0) {
737     *          super.bdbReadXxxKeyFields(..., superLevel - 1);
738     *      }
739     */
740    private void genReadSuperKeyFields(MethodVisitor mv,
741                                       boolean areSecKeyFields) {
742        if (hasPersistentSuperclass) {
743            Label next = new Label();
744            mv.visitVarInsn(ILOAD, 4);
745            mv.visitJumpInsn(IFEQ, next);
746            mv.visitVarInsn(ALOAD, 0);
747            mv.visitVarInsn(ALOAD, 1);
748            mv.visitVarInsn(ILOAD, 2);
749            mv.visitVarInsn(ILOAD, 3);
750            mv.visitVarInsn(ILOAD, 4);
751            mv.visitInsn(ICONST_1);
752            mv.visitInsn(ISUB);
753            String name = areSecKeyFields ? "bdbReadSecKeyFields"
754                                          : "bdbReadNonKeyFields";
755            mv.visitMethodInsn
756                (INVOKESPECIAL, superclassName, name,
757                 "(Lcom/sleepycat/persist/impl/EntityInput;III)V");
758            mv.visitLabel(next);
759        }
760    }
761
762    /**
763     *  public void bdbReadXxxKeyFields(EntityInput input,
764     *                                  int startField,
765     *                                  int endField,
766     *                                  int superLevel) {
767     *      // ...
768     *      if (superLevel <= 0) {
769     *          switch (startField) {
770     *          case 0:
771     *              keyField1 = input.readInt();
772     *              if (endField == 0) break;
773     *          case 1:
774     *              keyField2 = (String) input.readObject();
775     *              if (endField == 1) break;
776     *          case 2:
777     *              keyField3 = input.readInt();
778     *          }
779     *      }
780     */
781    private void genReadFieldSwitch(MethodVisitor mv, List<FieldInfo> fields) {
782        int nFields = fields.size();
783        if (nFields > 0) {
784            mv.visitVarInsn(ILOAD, 4);
785            Label pastSwitch = new Label();
786            mv.visitJumpInsn(IFGT, pastSwitch);
787            Label[] labels = new Label[nFields];
788            for (int i = 0; i < nFields; i += 1) {
789                labels[i] = new Label();
790            }
791            mv.visitVarInsn(ILOAD, 2);
792            mv.visitTableSwitchInsn(0, nFields - 1, pastSwitch, labels);
793            for (int i = 0; i < nFields; i += 1) {
794                FieldInfo field = fields.get(i);
795                mv.visitLabel(labels[i]);
796                genReadField(mv, field);
797                if (i < nFields - 1) {
798                    Label nextCase = labels[i + 1];
799                    mv.visitVarInsn(ILOAD, 3);
800                    if (i == 0) {
801                        mv.visitJumpInsn(IFNE, nextCase);
802                    } else {
803                        switch (i) {
804                        case 1:
805                            mv.visitInsn(ICONST_1);
806                            break;
807                        case 2:
808                            mv.visitInsn(ICONST_2);
809                            break;
810                        case 3:
811                            mv.visitInsn(ICONST_3);
812                            break;
813                        case 4:
814                            mv.visitInsn(ICONST_4);
815                            break;
816                        case 5:
817                            mv.visitInsn(ICONST_5);
818                            break;
819                        default:
820                            mv.visitIntInsn(BIPUSH, i);
821                        }
822                        mv.visitJumpInsn(IF_ICMPNE, nextCase);
823                    }
824                    mv.visitJumpInsn(GOTO, pastSwitch);
825                }
826            }
827            mv.visitLabel(pastSwitch);
828        }
829    }
830
831    /**
832     *      field = input.readInt(); // and other primitives
833     *      // or
834     *      field = (FieldClass) input.readObject();
835     */
836    private void genReadField(MethodVisitor mv, FieldInfo field) {
837        mv.visitVarInsn(ALOAD, 0);
838        mv.visitVarInsn(ALOAD, 1);
839        if (isRefType(field.type)) {
840            mv.visitMethodInsn
841                (INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityInput",
842                 "readObject", "()Ljava/lang/Object;");
843            mv.visitTypeInsn(CHECKCAST, getTypeInstName(field.type));
844        } else {
845            genReadPrimitive(mv, field.type.getSort());
846        }
847        mv.visitFieldInsn
848            (PUTFIELD, className, field.name, field.type.getDescriptor());
849    }
850
851    /**
852     * Generates reading of a simple type key field, or returns false if the
853     * key field is not a simple type (i.e., it is a composite key type).
854     *
855     *      field = input.readInt(); // and other primitives
856     *      // or
857     *      field = Integer.valueOf(input.readInt()); // and other simple types
858     *      // or returns false
859     */
860    private boolean genReadSimpleKeyField(MethodVisitor mv, FieldInfo field) {
861        if (genReadPrimitiveField(mv, field)) {
862            return true;
863        }
864        String fieldClassName = field.type.getClassName();
865        if (!isSimpleRefType(fieldClassName)) {
866            return false;
867        }
868        Integer sort = PRIMITIVE_WRAPPERS.get(fieldClassName);
869        if (sort != null) {
870            mv.visitVarInsn(ALOAD, 0);
871            mv.visitVarInsn(ALOAD, 1);
872            genReadPrimitive(mv, sort);
873            genWrapPrimitive(mv, sort);
874        } else if (fieldClassName.equals(Date.class.getName())) {
875            /* Date is a special case because we use NEW instead of valueOf. */
876            mv.visitVarInsn(ALOAD, 0);
877            mv.visitTypeInsn(NEW, "java/util/Date");
878            mv.visitInsn(DUP);
879            mv.visitVarInsn(ALOAD, 1);
880            genReadPrimitive(mv, Type.LONG);
881            mv.visitMethodInsn
882                (INVOKESPECIAL, "java/util/Date", "<init>", "(J)V");
883        } else if (fieldClassName.equals(String.class.getName())) {
884            mv.visitVarInsn(ALOAD, 0);
885            mv.visitVarInsn(ALOAD, 1);
886            mv.visitMethodInsn
887                (INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityInput",
888                 "readString", "()Ljava/lang/String;");
889        } else if (fieldClassName.equals(BigInteger.class.getName())) {
890            mv.visitVarInsn(ALOAD, 0);
891            mv.visitVarInsn(ALOAD, 1);
892            mv.visitMethodInsn
893                (INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityInput",
894                 "readBigInteger", "()Ljava/math/BigInteger;");
895        } else {
896            throw new IllegalStateException(fieldClassName);
897        }
898        mv.visitFieldInsn
899            (PUTFIELD, className, field.name, field.type.getDescriptor());
900        return true;
901    }
902
903    private boolean genReadPrimitiveField(MethodVisitor mv, FieldInfo field) {
904        int sort = field.type.getSort();
905        if (sort == Type.OBJECT || sort == Type.ARRAY) {
906            return false;
907        }
908        mv.visitVarInsn(ALOAD, 0);
909        mv.visitVarInsn(ALOAD, 1);
910        genReadPrimitive(mv, sort);
911        mv.visitFieldInsn
912            (PUTFIELD, className, field.name, field.type.getDescriptor());
913        return true;
914    }
915
916    /**
917     *  public Object bdbGetField(Object o,
918     *                            int field,
919     *                            int superLevel,
920     *                            boolean isSecField) {
921     *      if (superLevel > 0) {
922     *          // if has superclass:
923     *          return super.bdbGetField
924     *              (o, field, superLevel - 1, isSecField);
925     *      } else if (isSecField) {
926     *          switch (field) {
927     *          case 0:
928     *              return Integer.valueOf(f2);
929     *          case 1:
930     *              return f3;
931     *          case 2:
932     *              return f4;
933     *          }
934     *      } else {
935     *          switch (field) {
936     *          case 0:
937     *              return Integer.valueOf(f5);
938     *          case 1:
939     *              return f6;
940     *          case 2:
941     *              return f7;
942     *          }
943     *      }
944     *      return null;
945     *  }
946     */
947    private void genBdbGetField() {
948        MethodVisitor mv = cv.visitMethod
949            (ACC_PUBLIC, "bdbGetField",
950             "(Ljava/lang/Object;IIZ)Ljava/lang/Object;", null, null);
951        mv.visitCode();
952        mv.visitVarInsn(ILOAD, 3);
953        Label l0 = new Label();
954        mv.visitJumpInsn(IFLE, l0);
955        Label l1 = new Label();
956        if (hasPersistentSuperclass) {
957            mv.visitVarInsn(ALOAD, 0);
958            mv.visitVarInsn(ALOAD, 1);
959            mv.visitVarInsn(ILOAD, 2);
960            mv.visitVarInsn(ILOAD, 3);
961            mv.visitInsn(ICONST_1);
962            mv.visitInsn(ISUB);
963            mv.visitVarInsn(ILOAD, 4);
964            mv.visitMethodInsn
965                (INVOKESPECIAL, className, "bdbGetField",
966                 "(Ljava/lang/Object;IIZ)Ljava/lang/Object;");
967            mv.visitInsn(ARETURN);
968        } else {
969            mv.visitJumpInsn(GOTO, l1);
970        }
971        mv.visitLabel(l0);
972        mv.visitVarInsn(ILOAD, 4);
973        Label l2 = new Label();
974        mv.visitJumpInsn(IFEQ, l2);
975        genGetFieldSwitch(mv, secKeyFields, l1);
976        mv.visitLabel(l2);
977        genGetFieldSwitch(mv, nonKeyFields, l1);
978        mv.visitLabel(l1);
979        mv.visitInsn(ACONST_NULL);
980        mv.visitInsn(ARETURN);
981        mv.visitMaxs(1, 5);
982        mv.visitEnd();
983    }
984
985    /**
986     *  mv.visitVarInsn(ILOAD, 2);
987     *  Label l0 = new Label();
988     *  Label l1 = new Label();
989     *  Label l2 = new Label();
990     *  mv.visitTableSwitchInsn(0, 2, TheDefLabel, new Label[] { l0, l1, l2 });
991     *  mv.visitLabel(l0);
992     *  mv.visitVarInsn(ALOAD, 0);
993     *  mv.visitFieldInsn(GETFIELD, TheClassName, "f2", "I");
994     *  mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf",
995     *                     "(I)Ljava/lang/Integer;");
996     *  mv.visitInsn(ARETURN);
997     *  mv.visitLabel(l1);
998     *  mv.visitVarInsn(ALOAD, 0);
999     *  mv.visitFieldInsn(GETFIELD, TheClassName, "f3", "Ljava/lang/String;");
1000     *  mv.visitInsn(ARETURN);
1001     *  mv.visitLabel(l2);
1002     *  mv.visitVarInsn(ALOAD, 0);
1003     *  mv.visitFieldInsn(GETFIELD, TheClassName, "f4", "Ljava/lang/String;");
1004     *  mv.visitInsn(ARETURN);
1005     */
1006    private void genGetFieldSwitch(MethodVisitor mv,
1007                                   List<FieldInfo> fields,
1008                                   Label defaultLabel) {
1009        int nFields = fields.size();
1010        if (nFields == 0) {
1011            mv.visitJumpInsn(GOTO, defaultLabel);
1012            return;
1013        }
1014        Label[] labels = new Label[nFields];
1015        for (int i = 0; i < nFields; i += 1) {
1016            labels[i] = new Label();
1017        }
1018        mv.visitVarInsn(ILOAD, 2);
1019        mv.visitTableSwitchInsn(0, nFields - 1, defaultLabel, labels);
1020        for (int i = 0; i < nFields; i += 1) {
1021            FieldInfo field = fields.get(i);
1022            mv.visitLabel(labels[i]);
1023            mv.visitVarInsn(ALOAD, 0);
1024            mv.visitFieldInsn
1025                (GETFIELD, className, field.name, field.type.getDescriptor());
1026            if (!isRefType(field.type)) {
1027                genWrapPrimitive(mv, field.type.getSort());
1028            }
1029            mv.visitInsn(ARETURN);
1030        }
1031    }
1032
1033    /**
1034     *  public void bdbSetField(Object o,
1035     *                          int field,
1036     *                          int superLevel,
1037     *                          boolean isSecField,
1038     *                          Object value) {
1039     *      if (superLevel > 0) {
1040     *          // if has superclass:
1041     *          super.bdbSetField
1042     *              (o, field, superLevel - 1, isSecField, value);
1043     *      } else if (isSecField) {
1044     *          switch (field) {
1045     *          case 0:
1046     *              f2 = ((Integer) value).intValue();
1047     *          case 1:
1048     *              f3 = (String) value;
1049     *          case 2:
1050     *              f4 = (String) value;
1051     *          }
1052     *      } else {
1053     *          switch (field) {
1054     *          case 0:
1055     *              f5 = ((Integer) value).intValue();
1056     *          case 1:
1057     *              f6 = (String) value;
1058     *          case 2:
1059     *              f7 = (String) value;
1060     *          }
1061     *      }
1062     *  }
1063     */
1064    private void genBdbSetField() {
1065        MethodVisitor mv = cv.visitMethod
1066            (ACC_PUBLIC, "bdbSetField",
1067             "(Ljava/lang/Object;IIZLjava/lang/Object;)V", null, null);
1068        mv.visitCode();
1069        mv.visitVarInsn(ILOAD, 3);
1070        Label l0 = new Label();
1071        mv.visitJumpInsn(IFLE, l0);
1072        if (hasPersistentSuperclass) {
1073            mv.visitVarInsn(ALOAD, 0);
1074            mv.visitVarInsn(ALOAD, 1);
1075            mv.visitVarInsn(ILOAD, 2);
1076            mv.visitVarInsn(ILOAD, 3);
1077            mv.visitInsn(ICONST_1);
1078            mv.visitInsn(ISUB);
1079            mv.visitVarInsn(ILOAD, 4);
1080            mv.visitVarInsn(ALOAD, 5);
1081            mv.visitMethodInsn
1082                (INVOKESPECIAL, className, "bdbSetField",
1083                 "(Ljava/lang/Object;IIZLjava/lang/Object;)V");
1084        }
1085        mv.visitInsn(RETURN);
1086        mv.visitLabel(l0);
1087        mv.visitVarInsn(ILOAD, 4);
1088        Label l2 = new Label();
1089        mv.visitJumpInsn(IFEQ, l2);
1090        Label l1 = new Label();
1091        genSetFieldSwitch(mv, secKeyFields, l1);
1092        mv.visitLabel(l2);
1093        genSetFieldSwitch(mv, nonKeyFields, l1);
1094        mv.visitLabel(l1);
1095        mv.visitInsn(RETURN);
1096        mv.visitMaxs(2, 6);
1097        mv.visitEnd();
1098    }
1099
1100    /**
1101     *  mv.visitVarInsn(ILOAD, 2);
1102     *  Label l0 = new Label();
1103     *  Label l1 = new Label();
1104     *  Label l2 = new Label();
1105     *  mv.visitTableSwitchInsn(0, 2, TheDefLabel, new Label[] { l0, l1, l2 });
1106     *  mv.visitLabel(l0);
1107     *  mv.visitVarInsn(ALOAD, 0);
1108     *  mv.visitVarInsn(ALOAD, 5);
1109     *  mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
1110     *  mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue",
1111     *                     "()I");
1112     *  mv.visitFieldInsn(PUTFIELD, TheClassName, "f2", "I");
1113     *  mv.visitLabel(l1);
1114     *  mv.visitVarInsn(ALOAD, 0);
1115     *  mv.visitVarInsn(ALOAD, 5);
1116     *  mv.visitTypeInsn(CHECKCAST, "java/lang/String");
1117     *  mv.visitFieldInsn(PUTFIELD, TheClassName, "f3", "Ljava/lang/String;");
1118     *  mv.visitLabel(l2);
1119     *  mv.visitVarInsn(ALOAD, 0);
1120     *  mv.visitVarInsn(ALOAD, 5);
1121     *  mv.visitTypeInsn(CHECKCAST, "java/lang/String");
1122     *  mv.visitFieldInsn(PUTFIELD, TheClassName, "f4", "Ljava/lang/String;");
1123     */
1124    private void genSetFieldSwitch(MethodVisitor mv,
1125                                   List<FieldInfo> fields,
1126                                   Label defaultLabel) {
1127        int nFields = fields.size();
1128        if (nFields == 0) {
1129            mv.visitJumpInsn(GOTO, defaultLabel);
1130            return;
1131        }
1132        Label[] labels = new Label[nFields];
1133        for (int i = 0; i < nFields; i += 1) {
1134            labels[i] = new Label();
1135        }
1136        mv.visitVarInsn(ILOAD, 2);
1137        mv.visitTableSwitchInsn(0, nFields - 1, defaultLabel, labels);
1138        for (int i = 0; i < nFields; i += 1) {
1139            FieldInfo field = fields.get(i);
1140            mv.visitLabel(labels[i]);
1141            mv.visitVarInsn(ALOAD, 0);
1142            mv.visitVarInsn(ALOAD, 5);
1143            if (isRefType(field.type)) {
1144                mv.visitTypeInsn(CHECKCAST, getTypeInstName(field.type));
1145            } else {
1146                int sort = field.type.getSort();
1147                mv.visitTypeInsn
1148                    (CHECKCAST,
1149                     getPrimitiveWrapperClass(sort).replace('.', '/'));
1150                genUnwrapPrimitive(mv, sort);
1151            }
1152            mv.visitFieldInsn
1153                (PUTFIELD, className, field.name, field.type.getDescriptor());
1154            mv.visitInsn(RETURN);
1155        }
1156    }
1157
1158    private void genWritePrimitive(MethodVisitor mv, int sort) {
1159        switch (sort) {
1160        case Type.BOOLEAN:
1161            mv.visitMethodInsn
1162                (INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityOutput",
1163                 "writeBoolean", "(Z)Lcom/sleepycat/bind/tuple/TupleOutput;");
1164            break;
1165        case Type.CHAR:
1166            mv.visitMethodInsn
1167                (INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityOutput",
1168                 "writeChar", "(I)Lcom/sleepycat/bind/tuple/TupleOutput;");
1169            break;
1170        case Type.BYTE:
1171            mv.visitMethodInsn
1172                (INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityOutput",
1173                 "writeByte", "(I)Lcom/sleepycat/bind/tuple/TupleOutput;");
1174            break;
1175        case Type.SHORT:
1176            mv.visitMethodInsn
1177                (INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityOutput",
1178                 "writeShort", "(I)Lcom/sleepycat/bind/tuple/TupleOutput;");
1179            break;
1180        case Type.INT:
1181            mv.visitMethodInsn
1182                (INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityOutput",
1183                 "writeInt", "(I)Lcom/sleepycat/bind/tuple/TupleOutput;");
1184            break;
1185        case Type.LONG:
1186            mv.visitMethodInsn
1187                (INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityOutput",
1188                 "writeLong", "(J)Lcom/sleepycat/bind/tuple/TupleOutput;");
1189            break;
1190        case Type.FLOAT:
1191            mv.visitMethodInsn
1192                (INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityOutput",
1193                 "writeSortedFloat",
1194                 "(F)Lcom/sleepycat/bind/tuple/TupleOutput;");
1195            break;
1196        case Type.DOUBLE:
1197            mv.visitMethodInsn
1198                (INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityOutput",
1199                 "writeSortedDouble",
1200                 "(D)Lcom/sleepycat/bind/tuple/TupleOutput;");
1201            break;
1202        default:
1203            throw new IllegalStateException(String.valueOf(sort));
1204        }
1205        /* The write methods always return 'this' and we always discard it. */
1206        mv.visitInsn(POP);
1207    }
1208
1209    private void genReadPrimitive(MethodVisitor mv, int sort) {
1210        switch (sort) {
1211        case Type.BOOLEAN:
1212            mv.visitMethodInsn
1213                (INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityInput",
1214                 "readBoolean", "()Z");
1215            break;
1216        case Type.CHAR:
1217            mv.visitMethodInsn
1218                (INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityInput",
1219                 "readChar", "()C");
1220            break;
1221        case Type.BYTE:
1222            mv.visitMethodInsn
1223                (INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityInput",
1224                 "readByte", "()B");
1225            break;
1226        case Type.SHORT:
1227            mv.visitMethodInsn
1228                (INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityInput",
1229                 "readShort", "()S");
1230            break;
1231        case Type.INT:
1232            mv.visitMethodInsn
1233                (INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityInput",
1234                 "readInt", "()I");
1235            break;
1236        case Type.LONG:
1237            mv.visitMethodInsn
1238                (INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityInput",
1239                 "readLong", "()J");
1240            break;
1241        case Type.FLOAT:
1242            mv.visitMethodInsn
1243                (INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityInput",
1244                 "readSortedFloat", "()F");
1245            break;
1246        case Type.DOUBLE:
1247            mv.visitMethodInsn
1248                (INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityInput",
1249                 "readSortedDouble", "()D");
1250            break;
1251        default:
1252            throw new IllegalStateException(String.valueOf(sort));
1253        }
1254    }
1255
1256    private void genWrapPrimitive(MethodVisitor mv, int sort) {
1257        switch (sort) {
1258        case Type.BOOLEAN:
1259            mv.visitMethodInsn
1260                (INVOKESTATIC, "java/lang/Boolean", "valueOf",
1261                 "(Z)Ljava/lang/Boolean;");
1262            break;
1263        case Type.CHAR:
1264            mv.visitMethodInsn
1265                (INVOKESTATIC, "java/lang/Character", "valueOf",
1266                 "(C)Ljava/lang/Character;");
1267            break;
1268        case Type.BYTE:
1269            mv.visitMethodInsn
1270                (INVOKESTATIC, "java/lang/Byte", "valueOf",
1271                 "(B)Ljava/lang/Byte;");
1272            break;
1273        case Type.SHORT:
1274            mv.visitMethodInsn
1275                (INVOKESTATIC, "java/lang/Short", "valueOf",
1276                 "(S)Ljava/lang/Short;");
1277            break;
1278        case Type.INT:
1279            mv.visitMethodInsn
1280                (INVOKESTATIC, "java/lang/Integer", "valueOf",
1281                 "(I)Ljava/lang/Integer;");
1282            break;
1283        case Type.LONG:
1284            mv.visitMethodInsn
1285                (INVOKESTATIC, "java/lang/Long", "valueOf",
1286                 "(J)Ljava/lang/Long;");
1287            break;
1288        case Type.FLOAT:
1289            mv.visitMethodInsn
1290                (INVOKESTATIC, "java/lang/Float", "valueOf",
1291                 "(F)Ljava/lang/Float;");
1292            break;
1293        case Type.DOUBLE:
1294            mv.visitMethodInsn
1295                (INVOKESTATIC, "java/lang/Double", "valueOf",
1296                 "(D)Ljava/lang/Double;");
1297            break;
1298        default:
1299            throw new IllegalStateException(String.valueOf(sort));
1300        }
1301    }
1302
1303    private void genUnwrapPrimitive(MethodVisitor mv, int sort) {
1304        switch (sort) {
1305        case Type.BOOLEAN:
1306            mv.visitMethodInsn
1307                (INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z");
1308            break;
1309        case Type.CHAR:
1310            mv.visitMethodInsn
1311                (INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C");
1312            break;
1313        case Type.BYTE:
1314            mv.visitMethodInsn
1315                (INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B");
1316            break;
1317        case Type.SHORT:
1318            mv.visitMethodInsn
1319                (INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S");
1320            break;
1321        case Type.INT:
1322            mv.visitMethodInsn
1323                (INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I");
1324            break;
1325        case Type.LONG:
1326            mv.visitMethodInsn
1327                (INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J");
1328            break;
1329        case Type.FLOAT:
1330            mv.visitMethodInsn
1331                (INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F");
1332            break;
1333        case Type.DOUBLE:
1334            mv.visitMethodInsn
1335                (INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D");
1336            break;
1337        default:
1338            throw new IllegalStateException(String.valueOf(sort));
1339        }
1340    }
1341
1342    /**
1343     * Returns the type name for a visitTypeInsn operand, which is the internal
1344     * name for an object type and the descriptor for an array type.  Must not
1345     * be called for a non-reference type.
1346     */
1347    private static String getTypeInstName(Type type) {
1348        if (type.getSort() == Type.OBJECT) {
1349            return type.getInternalName();
1350        } else if (type.getSort() == Type.ARRAY) {
1351            return type.getDescriptor();
1352        } else {
1353            throw new IllegalStateException();
1354        }
1355    }
1356
1357    /**
1358     * Call this method before comparing a non-reference operand to zero as an
1359     * int, for example, with IFNE, IFEQ, IFLT, etc.  If the operand is a long,
1360     * float or double, this method will compare it to zero and leave the
1361     * result as an int operand.
1362     */
1363    private static void genBeforeCompareToZero(MethodVisitor mv, Type type) {
1364        switch (type.getSort()) {
1365        case Type.LONG:
1366            mv.visitInsn(LCONST_0);
1367            mv.visitInsn(LCMP);
1368            break;
1369        case Type.FLOAT:
1370            mv.visitInsn(FCONST_0);
1371            mv.visitInsn(FCMPL);
1372            break;
1373        case Type.DOUBLE:
1374            mv.visitInsn(DCONST_0);
1375            mv.visitInsn(DCMPL);
1376            break;
1377        }
1378    }
1379
1380    /**
1381     * Returns true if the given class is a primitive wrapper, Date or String.
1382     */
1383    static boolean isSimpleRefType(String className) {
1384        return (PRIMITIVE_WRAPPERS.containsKey(className) ||
1385                className.equals(BigInteger.class.getName()) ||
1386                className.equals(Date.class.getName()) ||
1387                className.equals(String.class.getName()));
1388    }
1389
1390    /**
1391     * Returns the wrapper class for a primitive.
1392     */
1393    static String getPrimitiveWrapperClass(int primitiveSort) {
1394        for (Map.Entry<String,Integer> entry : PRIMITIVE_WRAPPERS.entrySet()) {
1395            if (entry.getValue() == primitiveSort) {
1396                return entry.getKey();
1397            }
1398        }
1399        throw new IllegalStateException(String.valueOf(primitiveSort));
1400    }
1401
1402    /**
1403     * Returns true if the given type is an object or array.
1404     */
1405    private static boolean isRefType(Type type) {
1406        int sort = type.getSort();
1407        return (sort == Type.OBJECT || sort == Type.ARRAY);
1408    }
1409
1410    /**
1411     * Returns whether a string array contains a given string.
1412     */
1413    private static boolean containsString(String[] a, String s) {
1414        if (a != null) {
1415            for (String t : a) {
1416                if (s.equals(t)) {
1417                    return true;
1418                }
1419            }
1420        }
1421        return false;
1422    }
1423
1424    /**
1425     * Appends a string to a string array.
1426     */
1427    private static String[] appendString(String[] a, String s) {
1428        if (a != null) {
1429            int len = a.length;
1430            String[] a2 = new String[len + 1];
1431            System.arraycopy(a, 0, a2, 0, len);
1432            a2[len] = s;
1433            return a2;
1434        } else {
1435            return new String[] { s };
1436        }
1437    }
1438
1439    /**
1440     * Aborts the enhancement process when we determine that enhancement is
1441     * unnecessary or not possible.
1442     */
1443    private NotPersistentException abort() {
1444        return NOT_PERSISTENT;
1445    }
1446
1447    private static class FieldInfo implements FieldVisitor {
1448
1449        FieldVisitor parent;
1450        String name;
1451        Type type;
1452        OrderInfo order;
1453        boolean isPriKey;
1454        boolean isSecKey;
1455
1456        FieldInfo(FieldVisitor parent, String name, String desc) {
1457            this.parent = parent;
1458            this.name = name;
1459            type = Type.getType(desc);
1460        }
1461
1462        public AnnotationVisitor visitAnnotation(String desc,
1463                                                 boolean visible) {
1464            AnnotationVisitor ret = parent.visitAnnotation(desc, visible);
1465            if (desc.equals
1466                    ("Lcom/sleepycat/persist/model/KeyField;")) {
1467                order = new OrderInfo(ret);
1468                ret = order;
1469            } else if (desc.equals
1470                    ("Lcom/sleepycat/persist/model/PrimaryKey;")) {
1471                isPriKey = true;
1472            } else if (desc.equals
1473                    ("Lcom/sleepycat/persist/model/SecondaryKey;")) {
1474                isSecKey = true;
1475            }
1476            return ret;
1477        }
1478
1479        public void visitAttribute(Attribute attr) {
1480            parent.visitAttribute(attr);
1481        }
1482
1483        public void visitEnd() {
1484            parent.visitEnd();
1485        }
1486
1487        @Override
1488        public String toString() {
1489            String label;
1490            if (isPriKey) {
1491                label = "PrimaryKey";
1492            } else if (isSecKey) {
1493                label = "SecondaryKey";
1494            } else if (order != null) {
1495                label = "CompositeKeyField " + order.value;
1496            } else {
1497                label = "NonKeyField";
1498            }
1499            return "[" + label + ' ' + name + ' ' + type + ']';
1500        }
1501    }
1502
1503    private static class OrderInfo extends AnnotationInfo {
1504
1505        int value;
1506
1507        OrderInfo(AnnotationVisitor parent) {
1508            super(parent);
1509        }
1510
1511        @Override
1512        public void visit(String name, Object value) {
1513            if (name.equals("value")) {
1514                this.value = (Integer) value;
1515            }
1516            parent.visit(name, value);
1517        }
1518    }
1519
1520    private static abstract class AnnotationInfo implements AnnotationVisitor {
1521
1522        AnnotationVisitor parent;
1523
1524        AnnotationInfo(AnnotationVisitor parent) {
1525            this.parent = parent;
1526        }
1527
1528        public void visit(String name, Object value) {
1529            parent.visit(name, value);
1530        }
1531
1532        public AnnotationVisitor visitAnnotation(String name, String desc) {
1533            return parent.visitAnnotation(name, desc);
1534        }
1535
1536        public AnnotationVisitor visitArray(String name) {
1537            return parent.visitArray(name);
1538        }
1539
1540        public void visitEnum(String name, String desc, String value) {
1541            parent.visitEnum(name, desc, value);
1542        }
1543
1544        public void visitEnd() {
1545            parent.visitEnd();
1546        }
1547    }
1548}
1549