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