blob: 7d9ca7ca8fa20408dcd8be02e300b176000865a3 [file] [log] [blame]
/*-
* Copyright (C) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
*
* This file was distributed by Oracle as part of a version of Oracle Berkeley
* DB Java Edition made available at:
*
* http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html
*
* Please see the LICENSE file included in the top-level directory of the
* appropriate version of Oracle Berkeley DB Java Edition for a copy of the
* license and additional information.
*/
package com.sleepycat.persist.model;
import static com.sleepycat.asm.Opcodes.AALOAD;
import static com.sleepycat.asm.Opcodes.ACC_ABSTRACT;
import static com.sleepycat.asm.Opcodes.ACC_PRIVATE;
import static com.sleepycat.asm.Opcodes.ACC_PUBLIC;
import static com.sleepycat.asm.Opcodes.ACC_STATIC;
import static com.sleepycat.asm.Opcodes.ACC_TRANSIENT;
import static com.sleepycat.asm.Opcodes.ACONST_NULL;
import static com.sleepycat.asm.Opcodes.ALOAD;
import static com.sleepycat.asm.Opcodes.ANEWARRAY;
import static com.sleepycat.asm.Opcodes.ARETURN;
import static com.sleepycat.asm.Opcodes.ASM4;
import static com.sleepycat.asm.Opcodes.BIPUSH;
import static com.sleepycat.asm.Opcodes.CHECKCAST;
import static com.sleepycat.asm.Opcodes.DCMPL;
import static com.sleepycat.asm.Opcodes.DCONST_0;
import static com.sleepycat.asm.Opcodes.DUP;
import static com.sleepycat.asm.Opcodes.FCMPL;
import static com.sleepycat.asm.Opcodes.FCONST_0;
import static com.sleepycat.asm.Opcodes.GETFIELD;
import static com.sleepycat.asm.Opcodes.GOTO;
import static com.sleepycat.asm.Opcodes.ICONST_0;
import static com.sleepycat.asm.Opcodes.ICONST_1;
import static com.sleepycat.asm.Opcodes.ICONST_2;
import static com.sleepycat.asm.Opcodes.ICONST_3;
import static com.sleepycat.asm.Opcodes.ICONST_4;
import static com.sleepycat.asm.Opcodes.ICONST_5;
import static com.sleepycat.asm.Opcodes.IFEQ;
import static com.sleepycat.asm.Opcodes.IFGT;
import static com.sleepycat.asm.Opcodes.IFLE;
import static com.sleepycat.asm.Opcodes.IFNE;
import static com.sleepycat.asm.Opcodes.IFNONNULL;
import static com.sleepycat.asm.Opcodes.IF_ICMPNE;
import static com.sleepycat.asm.Opcodes.ILOAD;
import static com.sleepycat.asm.Opcodes.INVOKEINTERFACE;
import static com.sleepycat.asm.Opcodes.INVOKESPECIAL;
import static com.sleepycat.asm.Opcodes.INVOKESTATIC;
import static com.sleepycat.asm.Opcodes.INVOKEVIRTUAL;
import static com.sleepycat.asm.Opcodes.IRETURN;
import static com.sleepycat.asm.Opcodes.ISUB;
import static com.sleepycat.asm.Opcodes.LCMP;
import static com.sleepycat.asm.Opcodes.LCONST_0;
import static com.sleepycat.asm.Opcodes.NEW;
import static com.sleepycat.asm.Opcodes.POP;
import static com.sleepycat.asm.Opcodes.PUTFIELD;
import static com.sleepycat.asm.Opcodes.RETURN;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.sleepycat.asm.AnnotationVisitor;
import com.sleepycat.asm.Attribute;
import com.sleepycat.asm.ClassVisitor;
import com.sleepycat.asm.FieldVisitor;
import com.sleepycat.asm.Label;
import com.sleepycat.asm.MethodVisitor;
import com.sleepycat.asm.Type;
import com.sleepycat.compat.DbCompat;
/**
* An ASM ClassVisitor that examines a class, throws NotPersistentException if
* it is not persistent, or enhances it if it is persistent. A class is
* persistent if it contains the @Entity or @Persistent annotations. A
* resulting enhanced class implements the com.sleepycat.persist.impl.Enhanced
* interface.
*
* <p>NotPersistentException is thrown to abort the transformation in order to
* avoid making two passes over the class file (one to look for the annotations
* and another to enhance the bytecode) or outputing a class that isn't
* enhanced. By aborting the transformation as soon as we detect that the
* annotations are missing, we make only one partial pass for a non-persistent
* class.</p>
*
* @author Mark Hayes
*/
class BytecodeEnhancer extends ClassVisitor {
/** Thrown when we determine that a class is not persistent. */
@SuppressWarnings("serial")
static class NotPersistentException extends RuntimeException {}
/** A static instance is used to avoid fillInStaceTrace overhead. */
private static final NotPersistentException NOT_PERSISTENT =
new NotPersistentException();
private static final Map<String, Integer> PRIMITIVE_WRAPPERS =
new HashMap<String, Integer>();
static {
PRIMITIVE_WRAPPERS.put(Boolean.class.getName(), Type.BOOLEAN);
PRIMITIVE_WRAPPERS.put(Character.class.getName(), Type.CHAR);
PRIMITIVE_WRAPPERS.put(Byte.class.getName(), Type.BYTE);
PRIMITIVE_WRAPPERS.put(Short.class.getName(), Type.SHORT);
PRIMITIVE_WRAPPERS.put(Integer.class.getName(), Type.INT);
PRIMITIVE_WRAPPERS.put(Long.class.getName(), Type.LONG);
PRIMITIVE_WRAPPERS.put(Float.class.getName(), Type.FLOAT);
PRIMITIVE_WRAPPERS.put(Double.class.getName(), Type.DOUBLE);
}
private String className;
private String superclassName;
private boolean isPersistent;
private boolean isAbstract;
private boolean hasDefaultConstructor;
private boolean hasPersistentSuperclass;
private boolean isCompositeKey;
private FieldInfo priKeyField;
private List<FieldInfo> secKeyFields;
private List<FieldInfo> nonKeyFields;
private String staticBlockMethod;
BytecodeEnhancer(ClassVisitor parentVisitor) {
super(ASM4, parentVisitor);
secKeyFields = new ArrayList<FieldInfo>();
nonKeyFields = new ArrayList<FieldInfo>();
}
@Override
public void visit(int version,
int access,
String name,
String sig,
String superName,
String[] interfaces) {
className = name;
superclassName = superName;
final String ENHANCED = "com/sleepycat/persist/impl/Enhanced";
if (containsString(interfaces, ENHANCED)) {
throw abort();
}
interfaces = appendString(interfaces, ENHANCED);
isAbstract = ((access & ACC_ABSTRACT) != 0);
hasPersistentSuperclass =
(superName != null && !superName.equals("java/lang/Object"));
super.visit(version, access, name, sig, superName, interfaces);
}
@Override
public void visitSource(String source, String debug) {
super.visitSource(source, debug);
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
if (desc.equals("Lcom/sleepycat/persist/model/Entity;") ||
desc.equals("Lcom/sleepycat/persist/model/Persistent;")) {
isPersistent = true;
}
return super.visitAnnotation(desc, visible);
}
@Override
public FieldVisitor visitField(int access,
String name,
String desc,
String sig,
Object value) {
if (!isPersistent) {
throw abort();
}
FieldVisitor ret = super.visitField(access, name, desc, sig, value);
if ((access & ACC_STATIC) == 0) {
FieldInfo info = new FieldInfo(ret, name, desc,
(access & ACC_TRANSIENT) != 0);
nonKeyFields.add(info);
ret = info;
}
return ret;
}
@Override
public MethodVisitor visitMethod(int access,
String name,
String desc,
String sig,
String[] exceptions) {
if (!isPersistent) {
throw abort();
}
if ("<init>".equals(name) && "()V".equals(desc)) {
hasDefaultConstructor = true;
}
if ("<clinit>".equals(name)) {
if (staticBlockMethod != null) {
throw DbCompat.unexpectedState();
}
staticBlockMethod = "bdbExistingStaticBlock";
return cv.visitMethod
(ACC_PRIVATE + ACC_STATIC, staticBlockMethod, "()V", null,
null);
}
return super.visitMethod(access, name, desc, sig, exceptions);
}
@Override
public void visitEnd() {
if (!isPersistent || !hasDefaultConstructor) {
throw abort();
}
/* Generate new code at the end of the class. */
sortFields();
genBdbNewInstance();
genBdbNewArray();
genBdbIsPriKeyFieldNullOrZero();
genBdbWritePriKeyField();
genBdbReadPriKeyField();
genBdbWriteSecKeyFields();
genBdbReadSecKeyFields();
genBdbWriteNonKeyFields();
genBdbReadNonKeyFields();
genBdbWriteCompositeKeyFields();
genBdbReadCompositeKeyFields();
genBdbGetField();
genBdbSetField();
genBdbSetPriField();
genStaticBlock();
super.visitEnd();
}
private void sortFields() {
/*
System.out.println("AllFields: " + nonKeyFields);
//*/
if (nonKeyFields.size() == 0) {
return;
}
isCompositeKey = true;
for (FieldInfo field : nonKeyFields) {
if (field.order == null) {
isCompositeKey = false;
}
}
if (isCompositeKey) {
Collections.sort(nonKeyFields, new Comparator<FieldInfo>() {
public int compare(FieldInfo f1, FieldInfo f2) {
return f1.order.value - f2.order.value;
}
});
} else {
for (int i = 0; i < nonKeyFields.size();) {
FieldInfo field = nonKeyFields.get(i);
if (field.isTransient) {
nonKeyFields.remove(i);
} else if (field.isPriKey) {
if (priKeyField == null) {
priKeyField = field;
nonKeyFields.remove(i);
}
} else if (field.isSecKey) {
secKeyFields.add(field);
nonKeyFields.remove(i);
} else {
i += 1;
}
}
Comparator<FieldInfo> cmp = new Comparator<FieldInfo>() {
public int compare(FieldInfo f1, FieldInfo f2) {
return f1.name.compareTo(f2.name);
}
};
Collections.sort(secKeyFields, cmp);
Collections.sort(nonKeyFields, cmp);
}
/*
System.out.println("PriKey: " + priKeyField);
System.out.println("SecKeys: " + secKeyFields);
System.out.println("NonKeys: " + nonKeyFields);
//*/
}
/**
* Outputs code in a static block to register the prototype instance:
*
* static {
* EnhancedAccessor.registerClass(TheClassName, new TheClass());
* // or for an abstract class:
* EnhancedAccessor.registerClass(TheClassName, null);
* }
*/
private void genStaticBlock() {
MethodVisitor mv =
cv.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
mv.visitCode();
if (staticBlockMethod != null) {
mv.visitMethodInsn
(INVOKESTATIC, className, staticBlockMethod, "()V");
}
mv.visitLdcInsn(className.replace('/', '.'));
if (isAbstract) {
mv.visitInsn(ACONST_NULL);
} else {
mv.visitTypeInsn(NEW, className);
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "()V");
}
mv.visitMethodInsn
(INVOKESTATIC, "com/sleepycat/persist/impl/EnhancedAccessor",
"registerClass",
"(Ljava/lang/String;Lcom/sleepycat/persist/impl/Enhanced;)V");
mv.visitInsn(RETURN);
mv.visitMaxs(3, 0);
mv.visitEnd();
}
/**
* public Object bdbNewInstance() {
* return new TheClass();
* // or if abstract:
* return null;
* }
*/
private void genBdbNewInstance() {
MethodVisitor mv = cv.visitMethod
(ACC_PUBLIC, "bdbNewInstance", "()Ljava/lang/Object;", null, null);
mv.visitCode();
if (isAbstract) {
mv.visitInsn(ACONST_NULL);
mv.visitInsn(ARETURN);
mv.visitMaxs(1, 1);
} else {
mv.visitTypeInsn(NEW, className);
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "()V");
mv.visitInsn(ARETURN);
mv.visitMaxs(2, 1);
}
mv.visitEnd();
}
/**
* public Object bdbNewArray(int len) {
* return new TheClass[len];
* // or if abstract:
* return null;
* }
*/
private void genBdbNewArray() {
MethodVisitor mv = cv.visitMethod
(ACC_PUBLIC, "bdbNewArray", "(I)Ljava/lang/Object;", null, null);
mv.visitCode();
if (isAbstract) {
mv.visitInsn(ACONST_NULL);
mv.visitInsn(ARETURN);
mv.visitMaxs(1, 2);
} else {
mv.visitVarInsn(ILOAD, 1);
mv.visitTypeInsn(ANEWARRAY, className);
mv.visitInsn(ARETURN);
mv.visitMaxs(1, 2);
mv.visitEnd();
}
}
/**
* public boolean bdbIsPriKeyFieldNullOrZero() {
* return theField == null; // or zero or false, as appropriate
* // or if no primary key but has superclass:
* return super.bdbIsPriKeyFieldNullOrZero();
* }
*/
private void genBdbIsPriKeyFieldNullOrZero() {
MethodVisitor mv = cv.visitMethod
(ACC_PUBLIC, "bdbIsPriKeyFieldNullOrZero", "()Z", null, null);
mv.visitCode();
if (priKeyField != null) {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn
(GETFIELD, className, priKeyField.name,
priKeyField.type.getDescriptor());
Label l0 = new Label();
if (isRefType(priKeyField.type)) {
mv.visitJumpInsn(IFNONNULL, l0);
} else {
genBeforeCompareToZero(mv, priKeyField.type);
mv.visitJumpInsn(IFNE, l0);
}
mv.visitInsn(ICONST_1);
Label l1 = new Label();
mv.visitJumpInsn(GOTO, l1);
mv.visitLabel(l0);
mv.visitInsn(ICONST_0);
mv.visitLabel(l1);
} else if (hasPersistentSuperclass) {
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn
(INVOKESPECIAL, superclassName, "bdbIsPriKeyFieldNullOrZero",
"()Z");
} else {
mv.visitInsn(ICONST_0);
}
mv.visitInsn(IRETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
/**
* public void bdbWritePriKeyField(EntityOutput output, Format format) {
* output.writeKeyObject(theField, format);
* // or
* output.writeInt(theField); // and other simple types
* // or if no primary key but has superclass:
* return super.bdbWritePriKeyField(output, format);
* }
*/
private void genBdbWritePriKeyField() {
MethodVisitor mv = cv.visitMethod
(ACC_PUBLIC, "bdbWritePriKeyField",
"(Lcom/sleepycat/persist/impl/EntityOutput;" +
"Lcom/sleepycat/persist/impl/Format;)V",
null, null);
mv.visitCode();
if (priKeyField != null) {
if (!genWriteSimpleKeyField(mv, priKeyField)) {
/* For a non-simple type, call EntityOutput.writeKeyObject. */
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn
(GETFIELD, className, priKeyField.name,
priKeyField.type.getDescriptor());
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn
(INVOKEINTERFACE,
"com/sleepycat/persist/impl/EntityOutput",
"writeKeyObject",
"(Ljava/lang/Object;" +
"Lcom/sleepycat/persist/impl/Format;)V");
}
} else if (hasPersistentSuperclass) {
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn
(INVOKESPECIAL, superclassName, "bdbWritePriKeyField",
"(Lcom/sleepycat/persist/impl/EntityOutput;" +
"Lcom/sleepycat/persist/impl/Format;)V");
}
mv.visitInsn(RETURN);
mv.visitMaxs(3, 3);
mv.visitEnd();
}
/**
* public void bdbReadPriKeyField(EntityInput input, Format format) {
* theField = (TheFieldClass) input.readKeyObject(format);
* // or
* theField = input.readInt(); // and other simple types
* // or if no primary key but has superclass:
* super.bdbReadPriKeyField(input, format);
* }
*/
private void genBdbReadPriKeyField() {
MethodVisitor mv = cv.visitMethod
(ACC_PUBLIC, "bdbReadPriKeyField",
"(Lcom/sleepycat/persist/impl/EntityInput;" +
"Lcom/sleepycat/persist/impl/Format;)V",
null, null);
mv.visitCode();
if (priKeyField != null) {
if (!genReadSimpleKeyField(mv, priKeyField)) {
/* For a non-simple type, call EntityInput.readKeyObject. */
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn
(INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityInput",
"readKeyObject",
"(Lcom/sleepycat/persist/impl/Format;)" +
"Ljava/lang/Object;");
mv.visitTypeInsn(CHECKCAST, getTypeInstName(priKeyField.type));
mv.visitFieldInsn
(PUTFIELD, className, priKeyField.name,
priKeyField.type.getDescriptor());
}
} else if (hasPersistentSuperclass) {
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn
(INVOKESPECIAL, superclassName, "bdbReadPriKeyField",
"(Lcom/sleepycat/persist/impl/EntityInput;" +
"Lcom/sleepycat/persist/impl/Format;)V");
}
mv.visitInsn(RETURN);
mv.visitMaxs(3, 3);
mv.visitEnd();
}
/**
* public void bdbWriteSecKeyFields(EntityOutput output) {
* output.registerPriKeyObject(priKeyField); // if an object
* super.bdbWriteSecKeyFields(EntityOutput output); // if has super
* output.writeInt(secKeyField1);
* output.writeObject(secKeyField2, null);
* // etc
* }
*/
private void genBdbWriteSecKeyFields() {
MethodVisitor mv = cv.visitMethod
(ACC_PUBLIC, "bdbWriteSecKeyFields",
"(Lcom/sleepycat/persist/impl/EntityOutput;)V", null, null);
mv.visitCode();
/*
* In JE 5.0, String is treated as primitive type, so String does
* not need to be registered. [#19247]
*/
if (priKeyField != null &&
isRefType(priKeyField.type) &&
!priKeyField.isString) {
genRegisterPrimaryKey(mv, false);
}
if (hasPersistentSuperclass) {
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn
(INVOKESPECIAL, superclassName, "bdbWriteSecKeyFields",
"(Lcom/sleepycat/persist/impl/EntityOutput;)V");
}
for (FieldInfo field : secKeyFields) {
genWriteField(mv, field);
}
mv.visitInsn(RETURN);
mv.visitMaxs(2, 2);
mv.visitEnd();
}
/**
* public void bdbReadSecKeyFields(EntityInput input,
* int startField,
* int endField,
* int superLevel) {
* input.registerPriKeyObject(priKeyField); // if an object
* // if has super:
* if (superLevel != 0) {
* super.bdbReadSecKeyFields(..., superLevel - 1);
* }
* if (superLevel <= 0) {
* switch (startField) {
* case 0:
* secKeyField1 = input.readInt();
* if (endField == 0) break;
* case 1:
* secKeyField2 = (String) input.readObject();
* if (endField == 1) break;
* case 2:
* secKeyField3 = input.readInt();
* }
* }
* }
*/
private void genBdbReadSecKeyFields() {
MethodVisitor mv = cv.visitMethod
(ACC_PUBLIC, "bdbReadSecKeyFields",
"(Lcom/sleepycat/persist/impl/EntityInput;III)V", null, null);
mv.visitCode();
if (priKeyField != null &&
isRefType(priKeyField.type) &&
!priKeyField.isString) {
genRegisterPrimaryKey(mv, true);
} else if (priKeyField != null && priKeyField.isString) {
genRegisterPrimaryStringKey(mv);
}
genReadSuperKeyFields(mv, true);
genReadFieldSwitch(mv, secKeyFields);
mv.visitInsn(RETURN);
mv.visitMaxs(5, 5);
mv.visitEnd();
}
/**
* output.registerPriKeyObject(priKeyField);
* // or
* input.registerPriKeyObject(priKeyField);
*/
private void genRegisterPrimaryKey(MethodVisitor mv, boolean input) {
String entityInputOrOutputClass =
input ? "com/sleepycat/persist/impl/EntityInput"
: "com/sleepycat/persist/impl/EntityOutput";
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn
(GETFIELD, className, priKeyField.name,
priKeyField.type.getDescriptor());
mv.visitMethodInsn
(INVOKEINTERFACE, entityInputOrOutputClass, "registerPriKeyObject",
"(Ljava/lang/Object;)V");
}
/**
* input.registerPriStringKeyObject(priKeyField);
*/
private void genRegisterPrimaryStringKey(MethodVisitor mv) {
String entityInputOrOutputClass =
"com/sleepycat/persist/impl/EntityInput";
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn
(GETFIELD, className, priKeyField.name,
priKeyField.type.getDescriptor());
mv.visitMethodInsn
(INVOKEINTERFACE, entityInputOrOutputClass,
"registerPriStringKeyObject", "(Ljava/lang/Object;)V");
}
/**
* public void bdbWriteNonKeyFields(EntityOutput output) {
* // like bdbWriteSecKeyFields but does not call registerPriKeyObject
* }
*/
private void genBdbWriteNonKeyFields() {
MethodVisitor mv = cv.visitMethod
(ACC_PUBLIC, "bdbWriteNonKeyFields",
"(Lcom/sleepycat/persist/impl/EntityOutput;)V", null, null);
mv.visitCode();
if (!isCompositeKey) {
if (hasPersistentSuperclass) {
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn
(INVOKESPECIAL, superclassName, "bdbWriteNonKeyFields",
"(Lcom/sleepycat/persist/impl/EntityOutput;)V");
}
for (FieldInfo field : nonKeyFields) {
genWriteField(mv, field);
}
}
mv.visitInsn(RETURN);
mv.visitMaxs(2, 2);
mv.visitEnd();
}
/**
* public void bdbReadNonKeyFields(EntityInput input,
* int startField,
* int endField,
* int superLevel) {
* // like bdbReadSecKeyFields but does not call registerPriKeyObject
* }
*/
private void genBdbReadNonKeyFields() {
MethodVisitor mv = cv.visitMethod
(ACC_PUBLIC, "bdbReadNonKeyFields",
"(Lcom/sleepycat/persist/impl/EntityInput;III)V", null, null);
mv.visitCode();
if (!isCompositeKey) {
genReadSuperKeyFields(mv, false);
genReadFieldSwitch(mv, nonKeyFields);
}
mv.visitInsn(RETURN);
mv.visitMaxs(5, 5);
mv.visitEnd();
}
/**
* public void bdbWriteCompositeKeyFields(EntityOutput output,
* Format[] formats) {
* output.writeInt(compositeKeyField1);
* output.writeKeyObject(compositeKeyField2, formats[1]);
* // etc
* }
*/
private void genBdbWriteCompositeKeyFields() {
MethodVisitor mv = cv.visitMethod
(ACC_PUBLIC, "bdbWriteCompositeKeyFields",
"(Lcom/sleepycat/persist/impl/EntityOutput;" +
"[Lcom/sleepycat/persist/impl/Format;)V",
null, null);
mv.visitCode();
if (isCompositeKey) {
for (int i = 0; i < nonKeyFields.size(); i += 1) {
FieldInfo field = nonKeyFields.get(i);
if (!genWriteSimpleKeyField(mv, field)) {
/* For a non-simple type, call writeKeyObject. */
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn
(GETFIELD, className, field.name,
field.type.getDescriptor());
mv.visitVarInsn(ALOAD, 2);
if (i <= Byte.MAX_VALUE) {
mv.visitIntInsn(BIPUSH, i);
} else {
mv.visitLdcInsn(new Integer(i));
}
mv.visitInsn(AALOAD);
mv.visitMethodInsn
(INVOKEINTERFACE,
"com/sleepycat/persist/impl/EntityOutput",
"writeKeyObject",
"(Ljava/lang/Object;" +
"Lcom/sleepycat/persist/impl/Format;)V");
}
}
}
mv.visitInsn(RETURN);
mv.visitMaxs(3, 3);
mv.visitEnd();
}
/**
* public void bdbReadCompositeKeyFields(EntityInput input,
* Format[] formats) {
* compositeKeyField1 = input.readInt();
* compositeKeyField2 = input.readKeyObject(formats[1]);
* }
*/
private void genBdbReadCompositeKeyFields() {
MethodVisitor mv = cv.visitMethod
(ACC_PUBLIC, "bdbReadCompositeKeyFields",
"(Lcom/sleepycat/persist/impl/EntityInput;" +
"[Lcom/sleepycat/persist/impl/Format;)V",
null, null);
mv.visitCode();
if (isCompositeKey) {
for (int i = 0; i < nonKeyFields.size(); i += 1) {
FieldInfo field = nonKeyFields.get(i);
/* Ignore non-simple (illegal) types for composite key. */
if (!genReadSimpleKeyField(mv, field)) {
/* For a non-simple type, call readKeyObject. */
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
if (i <= Byte.MAX_VALUE) {
mv.visitIntInsn(BIPUSH, i);
} else {
mv.visitLdcInsn(new Integer(i));
}
mv.visitInsn(AALOAD);
mv.visitMethodInsn
(INVOKEINTERFACE,
"com/sleepycat/persist/impl/EntityInput",
"readKeyObject",
"(Lcom/sleepycat/persist/impl/Format;)" +
"Ljava/lang/Object;");
mv.visitTypeInsn(CHECKCAST, getTypeInstName(field.type));
mv.visitFieldInsn
(PUTFIELD, className, field.name,
field.type.getDescriptor());
}
}
}
mv.visitInsn(RETURN);
mv.visitMaxs(5, 5);
mv.visitEnd();
}
/**
* output.writeInt(field); // and other primitives
* // or
* output.writeObject(field, null);
*/
private void genWriteField(MethodVisitor mv, FieldInfo field) {
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn
(GETFIELD, className, field.name, field.type.getDescriptor());
int sort = field.type.getSort();
if (field.isString) {
/*
* In JE 5.0, we treat String as primitive, and will not store
* format ID for String data. [#19247]
*/
mv.visitMethodInsn
(INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityOutput",
"writeString",
"(Ljava/lang/String;)Lcom/sleepycat/bind/tuple/TupleOutput;");
mv.visitInsn(POP);
} else if (sort == Type.OBJECT || sort == Type.ARRAY) {
mv.visitInsn(ACONST_NULL);
mv.visitMethodInsn
(INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityOutput",
"writeObject",
"(Ljava/lang/Object;Lcom/sleepycat/persist/impl/Format;)V");
} else {
genWritePrimitive(mv, sort);
}
}
/**
* Generates writing of a simple type key field, or returns false if the
* key field is not a simple type (i.e., it is a composite key type).
*
* output.writeInt(theField); // and other primitives
* // or
* output.writeInt(theField.intValue()); // and other simple types
* // or returns false
*/
private boolean genWriteSimpleKeyField(MethodVisitor mv, FieldInfo field) {
if (genWritePrimitiveField(mv, field)) {
return true;
}
String fieldClassName = field.type.getClassName();
if (!isSimpleRefType(fieldClassName)) {
return false;
}
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn
(GETFIELD, className, field.name, field.type.getDescriptor());
Integer sort = PRIMITIVE_WRAPPERS.get(fieldClassName);
if (sort != null) {
genUnwrapPrimitive(mv, sort);
genWritePrimitive(mv, sort);
} else if (fieldClassName.equals(Date.class.getName())) {
mv.visitMethodInsn
(INVOKEVIRTUAL, "java/util/Date", "getTime", "()J");
genWritePrimitive(mv, Type.LONG);
} else if (fieldClassName.equals(String.class.getName())) {
mv.visitMethodInsn
(INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityOutput",
"writeString",
"(Ljava/lang/String;)Lcom/sleepycat/bind/tuple/TupleOutput;");
mv.visitInsn(POP);
} else if (fieldClassName.equals(BigInteger.class.getName())) {
mv.visitMethodInsn
(INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityOutput",
"writeBigInteger",
"(Ljava/math/BigInteger;)Lcom/sleepycat/bind/tuple/TupleOutput;");
mv.visitInsn(POP);
} else if (fieldClassName.equals(BigDecimal.class.getName())) {
mv.visitMethodInsn
(INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityOutput",
"writeSortedBigDecimal",
"(Ljava/math/BigDecimal;)Lcom/sleepycat/bind/tuple/TupleOutput;");
mv.visitInsn(POP);
} else {
throw DbCompat.unexpectedState(fieldClassName);
}
return true;
}
private boolean genWritePrimitiveField(MethodVisitor mv, FieldInfo field) {
int sort = field.type.getSort();
if (sort == Type.OBJECT || sort == Type.ARRAY) {
return false;
}
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn
(GETFIELD, className, field.name, field.type.getDescriptor());
genWritePrimitive(mv, sort);
return true;
}
/**
* // if has super:
* if (superLevel != 0) {
* super.bdbReadXxxKeyFields(..., superLevel - 1);
* }
*/
private void genReadSuperKeyFields(MethodVisitor mv,
boolean areSecKeyFields) {
if (hasPersistentSuperclass) {
Label next = new Label();
mv.visitVarInsn(ILOAD, 4);
mv.visitJumpInsn(IFEQ, next);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ILOAD, 2);
mv.visitVarInsn(ILOAD, 3);
mv.visitVarInsn(ILOAD, 4);
mv.visitInsn(ICONST_1);
mv.visitInsn(ISUB);
String name = areSecKeyFields ? "bdbReadSecKeyFields"
: "bdbReadNonKeyFields";
mv.visitMethodInsn
(INVOKESPECIAL, superclassName, name,
"(Lcom/sleepycat/persist/impl/EntityInput;III)V");
mv.visitLabel(next);
}
}
/**
* public void bdbReadXxxKeyFields(EntityInput input,
* int startField,
* int endField,
* int superLevel) {
* // ...
* if (superLevel <= 0) {
* switch (startField) {
* case 0:
* keyField1 = input.readInt();
* if (endField == 0) break;
* case 1:
* keyField2 = (String) input.readObject();
* if (endField == 1) break;
* case 2:
* keyField3 = input.readInt();
* }
* }
*/
private void genReadFieldSwitch(MethodVisitor mv, List<FieldInfo> fields) {
int nFields = fields.size();
if (nFields > 0) {
mv.visitVarInsn(ILOAD, 4);
Label pastSwitch = new Label();
mv.visitJumpInsn(IFGT, pastSwitch);
Label[] labels = new Label[nFields];
for (int i = 0; i < nFields; i += 1) {
labels[i] = new Label();
}
mv.visitVarInsn(ILOAD, 2);
mv.visitTableSwitchInsn(0, nFields - 1, pastSwitch, labels);
for (int i = 0; i < nFields; i += 1) {
FieldInfo field = fields.get(i);
mv.visitLabel(labels[i]);
genReadField(mv, field);
if (i < nFields - 1) {
Label nextCase = labels[i + 1];
mv.visitVarInsn(ILOAD, 3);
if (i == 0) {
mv.visitJumpInsn(IFNE, nextCase);
} else {
switch (i) {
case 1:
mv.visitInsn(ICONST_1);
break;
case 2:
mv.visitInsn(ICONST_2);
break;
case 3:
mv.visitInsn(ICONST_3);
break;
case 4:
mv.visitInsn(ICONST_4);
break;
case 5:
mv.visitInsn(ICONST_5);
break;
default:
mv.visitIntInsn(BIPUSH, i);
}
mv.visitJumpInsn(IF_ICMPNE, nextCase);
}
mv.visitJumpInsn(GOTO, pastSwitch);
}
}
mv.visitLabel(pastSwitch);
}
}
/**
* field = input.readInt(); // and other primitives
* // or
* field = (FieldClass) input.readObject();
*/
private void genReadField(MethodVisitor mv, FieldInfo field) {
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
if (field.isString) {
/*
* In JE 5.0, we treat String as primitive, and will not store
* format ID for String data. [#19247]
*/
mv.visitMethodInsn
(INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityInput",
"readStringObject", "()Ljava/lang/Object;");
mv.visitTypeInsn(CHECKCAST, getTypeInstName(field.type));
} else if (isRefType(field.type)) {
mv.visitMethodInsn
(INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityInput",
"readObject", "()Ljava/lang/Object;");
mv.visitTypeInsn(CHECKCAST, getTypeInstName(field.type));
} else {
genReadPrimitive(mv, field.type.getSort());
}
mv.visitFieldInsn
(PUTFIELD, className, field.name, field.type.getDescriptor());
}
/**
* Generates reading of a simple type key field, or returns false if the
* key field is not a simple type (i.e., it is a composite key type).
*
* field = input.readInt(); // and other primitives
* // or
* field = Integer.valueOf(input.readInt()); // and other simple types
* // or returns false
*/
private boolean genReadSimpleKeyField(MethodVisitor mv, FieldInfo field) {
if (genReadPrimitiveField(mv, field)) {
return true;
}
String fieldClassName = field.type.getClassName();
if (!isSimpleRefType(fieldClassName)) {
return false;
}
Integer sort = PRIMITIVE_WRAPPERS.get(fieldClassName);
if (sort != null) {
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
genReadPrimitive(mv, sort);
genWrapPrimitive(mv, sort);
} else if (fieldClassName.equals(Date.class.getName())) {
/* Date is a special case because we use NEW instead of valueOf. */
mv.visitVarInsn(ALOAD, 0);
mv.visitTypeInsn(NEW, "java/util/Date");
mv.visitInsn(DUP);
mv.visitVarInsn(ALOAD, 1);
genReadPrimitive(mv, Type.LONG);
mv.visitMethodInsn
(INVOKESPECIAL, "java/util/Date", "<init>", "(J)V");
} else if (fieldClassName.equals(String.class.getName())) {
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn
(INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityInput",
"readString", "()Ljava/lang/String;");
} else if (fieldClassName.equals(BigInteger.class.getName())) {
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn
(INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityInput",
"readBigInteger", "()Ljava/math/BigInteger;");
} else if (fieldClassName.equals(BigDecimal.class.getName())) {
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn
(INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityInput",
"readSortedBigDecimal", "()Ljava/math/BigDecimal;");
} else {
throw DbCompat.unexpectedState(fieldClassName);
}
mv.visitFieldInsn
(PUTFIELD, className, field.name, field.type.getDescriptor());
return true;
}
private boolean genReadPrimitiveField(MethodVisitor mv, FieldInfo field) {
int sort = field.type.getSort();
if (sort == Type.OBJECT || sort == Type.ARRAY) {
return false;
}
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
genReadPrimitive(mv, sort);
mv.visitFieldInsn
(PUTFIELD, className, field.name, field.type.getDescriptor());
return true;
}
/**
* public Object bdbGetField(Object o,
* int field,
* int superLevel,
* boolean isSecField) {
* if (superLevel > 0) {
* // if has superclass:
* return super.bdbGetField
* (o, field, superLevel - 1, isSecField);
* } else if (isSecField) {
* switch (field) {
* case 0:
* return Integer.valueOf(f2);
* case 1:
* return f3;
* case 2:
* return f4;
* }
* } else {
* switch (field) {
* case 0:
* return Integer.valueOf(f5);
* case 1:
* return f6;
* case 2:
* return f7;
* }
* }
* return null;
* }
*/
private void genBdbGetField() {
MethodVisitor mv = cv.visitMethod
(ACC_PUBLIC, "bdbGetField",
"(Ljava/lang/Object;IIZ)Ljava/lang/Object;", null, null);
mv.visitCode();
mv.visitVarInsn(ILOAD, 3);
Label l0 = new Label();
mv.visitJumpInsn(IFLE, l0);
Label l1 = new Label();
if (hasPersistentSuperclass) {
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ILOAD, 2);
mv.visitVarInsn(ILOAD, 3);
mv.visitInsn(ICONST_1);
mv.visitInsn(ISUB);
mv.visitVarInsn(ILOAD, 4);
mv.visitMethodInsn
(INVOKESPECIAL, className, "bdbGetField",
"(Ljava/lang/Object;IIZ)Ljava/lang/Object;");
mv.visitInsn(ARETURN);
} else {
mv.visitJumpInsn(GOTO, l1);
}
mv.visitLabel(l0);
mv.visitVarInsn(ILOAD, 4);
Label l2 = new Label();
mv.visitJumpInsn(IFEQ, l2);
genGetFieldSwitch(mv, secKeyFields, l1);
mv.visitLabel(l2);
genGetFieldSwitch(mv, nonKeyFields, l1);
mv.visitLabel(l1);
mv.visitInsn(ACONST_NULL);
mv.visitInsn(ARETURN);
mv.visitMaxs(1, 5);
mv.visitEnd();
}
/**
* mv.visitVarInsn(ILOAD, 2);
* Label l0 = new Label();
* Label l1 = new Label();
* Label l2 = new Label();
* mv.visitTableSwitchInsn(0, 2, TheDefLabel, new Label[] { l0, l1, l2 });
* mv.visitLabel(l0);
* mv.visitVarInsn(ALOAD, 0);
* mv.visitFieldInsn(GETFIELD, TheClassName, "f2", "I");
* mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf",
* "(I)Ljava/lang/Integer;");
* mv.visitInsn(ARETURN);
* mv.visitLabel(l1);
* mv.visitVarInsn(ALOAD, 0);
* mv.visitFieldInsn(GETFIELD, TheClassName, "f3", "Ljava/lang/String;");
* mv.visitInsn(ARETURN);
* mv.visitLabel(l2);
* mv.visitVarInsn(ALOAD, 0);
* mv.visitFieldInsn(GETFIELD, TheClassName, "f4", "Ljava/lang/String;");
* mv.visitInsn(ARETURN);
*/
private void genGetFieldSwitch(MethodVisitor mv,
List<FieldInfo> fields,
Label defaultLabel) {
int nFields = fields.size();
if (nFields == 0) {
mv.visitJumpInsn(GOTO, defaultLabel);
return;
}
Label[] labels = new Label[nFields];
for (int i = 0; i < nFields; i += 1) {
labels[i] = new Label();
}
mv.visitVarInsn(ILOAD, 2);
mv.visitTableSwitchInsn(0, nFields - 1, defaultLabel, labels);
for (int i = 0; i < nFields; i += 1) {
FieldInfo field = fields.get(i);
mv.visitLabel(labels[i]);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn
(GETFIELD, className, field.name, field.type.getDescriptor());
if (!isRefType(field.type)) {
genWrapPrimitive(mv, field.type.getSort());
}
mv.visitInsn(ARETURN);
}
}
/**
* public void bdbSetField(Object o,
* int field,
* int superLevel,
* boolean isSecField,
* Object value) {
* if (superLevel > 0) {
* // if has superclass:
* super.bdbSetField
* (o, field, superLevel - 1, isSecField, value);
* } else if (isSecField) {
* switch (field) {
* case 0:
* f2 = ((Integer) value).intValue();
* case 1:
* f3 = (String) value;
* case 2:
* f4 = (String) value;
* }
* } else {
* switch (field) {
* case 0:
* f5 = ((Integer) value).intValue();
* case 1:
* f6 = (String) value;
* case 2:
* f7 = (String) value;
* }
* }
* }
*/
private void genBdbSetField() {
MethodVisitor mv = cv.visitMethod
(ACC_PUBLIC, "bdbSetField",
"(Ljava/lang/Object;IIZLjava/lang/Object;)V", null, null);
mv.visitCode();
mv.visitVarInsn(ILOAD, 3);
Label l0 = new Label();
mv.visitJumpInsn(IFLE, l0);
if (hasPersistentSuperclass) {
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ILOAD, 2);
mv.visitVarInsn(ILOAD, 3);
mv.visitInsn(ICONST_1);
mv.visitInsn(ISUB);
mv.visitVarInsn(ILOAD, 4);
mv.visitVarInsn(ALOAD, 5);
mv.visitMethodInsn
(INVOKESPECIAL, className, "bdbSetField",
"(Ljava/lang/Object;IIZLjava/lang/Object;)V");
}
mv.visitInsn(RETURN);
mv.visitLabel(l0);
mv.visitVarInsn(ILOAD, 4);
Label l2 = new Label();
mv.visitJumpInsn(IFEQ, l2);
Label l1 = new Label();
genSetFieldSwitch(mv, secKeyFields, l1);
mv.visitLabel(l2);
genSetFieldSwitch(mv, nonKeyFields, l1);
mv.visitLabel(l1);
mv.visitInsn(RETURN);
mv.visitMaxs(2, 6);
mv.visitEnd();
}
/**
* public void bdbSetPriField(Object o, Object value) {
* if (priKeyField != null) {
* thisField = (TheFieldClass) value;
* } else if (super != null) {
* // if has superclass:
* super.bdbSetPriField(o, value)
* }
* }
*/
private void genBdbSetPriField() {
MethodVisitor mv = cv.visitMethod
(ACC_PUBLIC, "bdbSetPriField",
"(Ljava/lang/Object;Ljava/lang/Object;)V", null, null);
mv.visitCode();
if (priKeyField != null) {
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 2);
if (isRefType(priKeyField.type)) {
mv.visitTypeInsn(CHECKCAST, getTypeInstName(priKeyField.type));
} else {
int sort = priKeyField.type.getSort();
mv.visitTypeInsn
(CHECKCAST,
getPrimitiveWrapperClass(sort).replace('.', '/'));
genUnwrapPrimitive(mv, sort);
}
mv.visitFieldInsn
(PUTFIELD, className, priKeyField.name,
priKeyField.type.getDescriptor());
} else if (hasPersistentSuperclass) {
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn
(INVOKESPECIAL, superclassName, "bdbSetPriField",
"(Ljava/lang/Object;Ljava/lang/Object;)V");
}
mv.visitInsn(RETURN);
mv.visitMaxs(3, 3);
mv.visitEnd();
}
/**
* mv.visitVarInsn(ILOAD, 2);
* Label l0 = new Label();
* Label l1 = new Label();
* Label l2 = new Label();
* mv.visitTableSwitchInsn(0, 2, TheDefLabel, new Label[] { l0, l1, l2 });
* mv.visitLabel(l0);
* mv.visitVarInsn(ALOAD, 0);
* mv.visitVarInsn(ALOAD, 5);
* mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
* mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue",
* "()I");
* mv.visitFieldInsn(PUTFIELD, TheClassName, "f2", "I");
* mv.visitLabel(l1);
* mv.visitVarInsn(ALOAD, 0);
* mv.visitVarInsn(ALOAD, 5);
* mv.visitTypeInsn(CHECKCAST, "java/lang/String");
* mv.visitFieldInsn(PUTFIELD, TheClassName, "f3", "Ljava/lang/String;");
* mv.visitLabel(l2);
* mv.visitVarInsn(ALOAD, 0);
* mv.visitVarInsn(ALOAD, 5);
* mv.visitTypeInsn(CHECKCAST, "java/lang/String");
* mv.visitFieldInsn(PUTFIELD, TheClassName, "f4", "Ljava/lang/String;");
*/
private void genSetFieldSwitch(MethodVisitor mv,
List<FieldInfo> fields,
Label defaultLabel) {
int nFields = fields.size();
if (nFields == 0) {
mv.visitJumpInsn(GOTO, defaultLabel);
return;
}
Label[] labels = new Label[nFields];
for (int i = 0; i < nFields; i += 1) {
labels[i] = new Label();
}
mv.visitVarInsn(ILOAD, 2);
mv.visitTableSwitchInsn(0, nFields - 1, defaultLabel, labels);
for (int i = 0; i < nFields; i += 1) {
FieldInfo field = fields.get(i);
mv.visitLabel(labels[i]);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 5);
if (isRefType(field.type)) {
mv.visitTypeInsn(CHECKCAST, getTypeInstName(field.type));
} else {
int sort = field.type.getSort();
mv.visitTypeInsn
(CHECKCAST,
getPrimitiveWrapperClass(sort).replace('.', '/'));
genUnwrapPrimitive(mv, sort);
}
mv.visitFieldInsn
(PUTFIELD, className, field.name, field.type.getDescriptor());
mv.visitInsn(RETURN);
}
}
private void genWritePrimitive(MethodVisitor mv, int sort) {
switch (sort) {
case Type.BOOLEAN:
mv.visitMethodInsn
(INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityOutput",
"writeBoolean", "(Z)Lcom/sleepycat/bind/tuple/TupleOutput;");
break;
case Type.CHAR:
mv.visitMethodInsn
(INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityOutput",
"writeChar", "(I)Lcom/sleepycat/bind/tuple/TupleOutput;");
break;
case Type.BYTE:
mv.visitMethodInsn
(INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityOutput",
"writeByte", "(I)Lcom/sleepycat/bind/tuple/TupleOutput;");
break;
case Type.SHORT:
mv.visitMethodInsn
(INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityOutput",
"writeShort", "(I)Lcom/sleepycat/bind/tuple/TupleOutput;");
break;
case Type.INT:
mv.visitMethodInsn
(INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityOutput",
"writeInt", "(I)Lcom/sleepycat/bind/tuple/TupleOutput;");
break;
case Type.LONG:
mv.visitMethodInsn
(INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityOutput",
"writeLong", "(J)Lcom/sleepycat/bind/tuple/TupleOutput;");
break;
case Type.FLOAT:
mv.visitMethodInsn
(INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityOutput",
"writeSortedFloat",
"(F)Lcom/sleepycat/bind/tuple/TupleOutput;");
break;
case Type.DOUBLE:
mv.visitMethodInsn
(INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityOutput",
"writeSortedDouble",
"(D)Lcom/sleepycat/bind/tuple/TupleOutput;");
break;
default:
throw DbCompat.unexpectedState(String.valueOf(sort));
}
/* The write methods always return 'this' and we always discard it. */
mv.visitInsn(POP);
}
private void genReadPrimitive(MethodVisitor mv, int sort) {
switch (sort) {
case Type.BOOLEAN:
mv.visitMethodInsn
(INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityInput",
"readBoolean", "()Z");
break;
case Type.CHAR:
mv.visitMethodInsn
(INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityInput",
"readChar", "()C");
break;
case Type.BYTE:
mv.visitMethodInsn
(INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityInput",
"readByte", "()B");
break;
case Type.SHORT:
mv.visitMethodInsn
(INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityInput",
"readShort", "()S");
break;
case Type.INT:
mv.visitMethodInsn
(INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityInput",
"readInt", "()I");
break;
case Type.LONG:
mv.visitMethodInsn
(INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityInput",
"readLong", "()J");
break;
case Type.FLOAT:
mv.visitMethodInsn
(INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityInput",
"readSortedFloat", "()F");
break;
case Type.DOUBLE:
mv.visitMethodInsn
(INVOKEINTERFACE, "com/sleepycat/persist/impl/EntityInput",
"readSortedDouble", "()D");
break;
default:
throw DbCompat.unexpectedState(String.valueOf(sort));
}
}
private void genWrapPrimitive(MethodVisitor mv, int sort) {
switch (sort) {
case Type.BOOLEAN:
mv.visitMethodInsn
(INVOKESTATIC, "java/lang/Boolean", "valueOf",
"(Z)Ljava/lang/Boolean;");
break;
case Type.CHAR:
mv.visitMethodInsn
(INVOKESTATIC, "java/lang/Character", "valueOf",
"(C)Ljava/lang/Character;");
break;
case Type.BYTE:
mv.visitMethodInsn
(INVOKESTATIC, "java/lang/Byte", "valueOf",
"(B)Ljava/lang/Byte;");
break;
case Type.SHORT:
mv.visitMethodInsn
(INVOKESTATIC, "java/lang/Short", "valueOf",
"(S)Ljava/lang/Short;");
break;
case Type.INT:
mv.visitMethodInsn
(INVOKESTATIC, "java/lang/Integer", "valueOf",
"(I)Ljava/lang/Integer;");
break;
case Type.LONG:
mv.visitMethodInsn
(INVOKESTATIC, "java/lang/Long", "valueOf",
"(J)Ljava/lang/Long;");
break;
case Type.FLOAT:
mv.visitMethodInsn
(INVOKESTATIC, "java/lang/Float", "valueOf",
"(F)Ljava/lang/Float;");
break;
case Type.DOUBLE:
mv.visitMethodInsn
(INVOKESTATIC, "java/lang/Double", "valueOf",
"(D)Ljava/lang/Double;");
break;
default:
throw DbCompat.unexpectedState(String.valueOf(sort));
}
}
private void genUnwrapPrimitive(MethodVisitor mv, int sort) {
switch (sort) {
case Type.BOOLEAN:
mv.visitMethodInsn
(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z");
break;
case Type.CHAR:
mv.visitMethodInsn
(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C");
break;
case Type.BYTE:
mv.visitMethodInsn
(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B");
break;
case Type.SHORT:
mv.visitMethodInsn
(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S");
break;
case Type.INT:
mv.visitMethodInsn
(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I");
break;
case Type.LONG:
mv.visitMethodInsn
(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J");
break;
case Type.FLOAT:
mv.visitMethodInsn
(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F");
break;
case Type.DOUBLE:
mv.visitMethodInsn
(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D");
break;
default:
throw DbCompat.unexpectedState(String.valueOf(sort));
}
}
/**
* Returns the type name for a visitTypeInsn operand, which is the internal
* name for an object type and the descriptor for an array type. Must not
* be called for a non-reference type.
*/
private static String getTypeInstName(Type type) {
if (type.getSort() == Type.OBJECT) {
return type.getInternalName();
} else if (type.getSort() == Type.ARRAY) {
return type.getDescriptor();
} else {
throw DbCompat.unexpectedState();
}
}
/**
* Call this method before comparing a non-reference operand to zero as an
* int, for example, with IFNE, IFEQ, IFLT, etc. If the operand is a long,
* float or double, this method will compare it to zero and leave the
* result as an int operand.
*/
private static void genBeforeCompareToZero(MethodVisitor mv, Type type) {
switch (type.getSort()) {
case Type.LONG:
mv.visitInsn(LCONST_0);
mv.visitInsn(LCMP);
break;
case Type.FLOAT:
mv.visitInsn(FCONST_0);
mv.visitInsn(FCMPL);
break;
case Type.DOUBLE:
mv.visitInsn(DCONST_0);
mv.visitInsn(DCMPL);
break;
}
}
/**
* Returns true if the given class is a primitive wrapper, Date or String.
*/
static boolean isSimpleRefType(String className) {
return (PRIMITIVE_WRAPPERS.containsKey(className) ||
className.equals(BigInteger.class.getName()) ||
className.equals(BigDecimal.class.getName()) ||
className.equals(Date.class.getName()) ||
className.equals(String.class.getName()));
}
/**
* Returns the wrapper class for a primitive.
*/
static String getPrimitiveWrapperClass(int primitiveSort) {
for (Map.Entry<String, Integer> entry :
PRIMITIVE_WRAPPERS.entrySet()) {
if (entry.getValue() == primitiveSort) {
return entry.getKey();
}
}
throw DbCompat.unexpectedState(String.valueOf(primitiveSort));
}
/**
* Returns true if the given type is an object or array.
*/
private static boolean isRefType(Type type) {
int sort = type.getSort();
return (sort == Type.OBJECT || sort == Type.ARRAY);
}
/**
* Returns whether a string array contains a given string.
*/
private static boolean containsString(String[] a, String s) {
if (a != null) {
for (String t : a) {
if (s.equals(t)) {
return true;
}
}
}
return false;
}
/**
* Appends a string to a string array.
*/
private static String[] appendString(String[] a, String s) {
if (a != null) {
int len = a.length;
String[] a2 = new String[len + 1];
System.arraycopy(a, 0, a2, 0, len);
a2[len] = s;
return a2;
} else {
return new String[] { s };
}
}
/**
* Aborts the enhancement process when we determine that enhancement is
* unnecessary or not possible.
*/
private NotPersistentException abort() {
return NOT_PERSISTENT;
}
private static class FieldInfo extends FieldVisitor {
FieldVisitor parent;
String name;
Type type;
OrderInfo order;
boolean isPriKey;
boolean isSecKey;
boolean isTransient;
boolean isString = false;
FieldInfo(FieldVisitor parent,
String name,
String desc,
boolean isTransient) {
super(ASM4);
this.parent = parent;
this.name = name;
this.isTransient = isTransient;
type = Type.getType(desc);
if (type.getClassName().equals(String.class.getName())) {
isString = true;
}
}
public AnnotationVisitor visitAnnotation(String desc,
boolean visible) {
AnnotationVisitor ret = parent.visitAnnotation(desc, visible);
if (desc.equals
("Lcom/sleepycat/persist/model/KeyField;")) {
order = new OrderInfo(ret);
ret = order;
} else if (desc.equals
("Lcom/sleepycat/persist/model/PrimaryKey;")) {
isPriKey = true;
} else if (desc.equals
("Lcom/sleepycat/persist/model/SecondaryKey;")) {
isSecKey = true;
} else if (desc.equals
("Lcom/sleepycat/persist/model/NotPersistent;")) {
isTransient = true;
} else if (desc.equals
("Lcom/sleepycat/persist/model/NotTransient;")) {
isTransient = false;
}
return ret;
}
public void visitAttribute(Attribute attr) {
parent.visitAttribute(attr);
}
public void visitEnd() {
parent.visitEnd();
}
@Override
public String toString() {
String label;
if (isPriKey) {
label = "PrimaryKey";
} else if (isSecKey) {
label = "SecondaryKey";
} else if (order != null) {
label = "CompositeKeyField " + order.value;
} else {
label = "NonKeyField";
}
return "[" + label + ' ' + name + ' ' + type + ']';
}
}
private static class OrderInfo extends AnnotationInfo {
int value;
OrderInfo(AnnotationVisitor parent) {
super(parent);
}
@Override
public void visit(String name, Object value) {
if (name.equals("value")) {
this.value = (Integer) value;
}
parent.visit(name, value);
}
}
private static abstract class AnnotationInfo extends AnnotationVisitor {
AnnotationVisitor parent;
AnnotationInfo(AnnotationVisitor parent) {
super(ASM4);
this.parent = parent;
}
public void visit(String name, Object value) {
parent.visit(name, value);
}
public AnnotationVisitor visitAnnotation(String name, String desc) {
return parent.visitAnnotation(name, desc);
}
public AnnotationVisitor visitArray(String name) {
return parent.visitArray(name);
}
public void visitEnum(String name, String desc, String value) {
parent.visitEnum(name, desc, value);
}
public void visitEnd() {
parent.visitEnd();
}
}
}