package com.insightfullogic.slab.implementation;
import static org.objectweb.asm.Type.LONG_TYPE;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.util.CheckClassAdapter;
import sun.misc.Unsafe;
import com.insightfullogic.slab.Cursor;
import com.insightfullogic.slab.ConcreteCursor;
import com.insightfullogic.slab.SlabOptions;
@SuppressWarnings("restriction")
public class BytecodeGenerator<T extends Cursor> implements Opcodes {
private static final String GENERATED_CONSTRUCTOR = "(ILcom/insightfullogic/slab/implementation/AllocationHandler;Lcom/insightfullogic/slab/SlabOptions;)V";
private static final String UNSAFE_NAME = Type.getInternalName(Unsafe.class);
private static final String UNSAFE_DESCRIPTOR = Type.getType(Unsafe.class).getDescriptor();
private static final String DIRECT_CLASS_NAME = Type.getInternalName(ConcreteCursor.class);
private static final String DIRECT_CLASS_CONSTRUCTOR;
static {
Constructor<?> constructor = ConcreteCursor.class.getConstructors()[0];
DIRECT_CLASS_CONSTRUCTOR = Type.getConstructorDescriptor(constructor);
}
private final TypeInspector inspector;
private final String classExtended;
private final String constructorExtended;
private final String implementationName;
private final String[] interfacesImplemented;
private final SlabOptions options;
public BytecodeGenerator(TypeInspector inspector, Class<T> representingKlass, SlabOptions options) {
this.inspector = inspector;
this.options = options;
implementationName = "DirectMemory" + representingKlass.getSimpleName();
if (representingKlass.isInterface()) {
classExtended = DIRECT_CLASS_NAME;
constructorExtended = DIRECT_CLASS_CONSTRUCTOR;
interfacesImplemented = new String[] { Type.getInternalName(representingKlass) };
} else {
classExtended = Type.getInternalName(representingKlass);
constructorExtended = Type.getConstructorDescriptor(representingKlass.getConstructors()[0]);
interfacesImplemented = null;
}
}
@SuppressWarnings("unchecked")
public Class<T> generate() {
ClassWriter out = new ClassWriter(ClassWriter.COMPUTE_MAXS);
CheckClassAdapter writer = new CheckClassAdapter(out);
int offset = 0;
declareClass(writer);
declareConstructor(writer);
for (Method getter : inspector.getters) {
offset = declareField(getter, writer, offset);
}
writer.visitEnd();
return (Class<T>) new GeneratedClassLoader(options).defineClass(implementationName, out);
}
private void declareClass(ClassVisitor writer) {
writer.visit(V1_6, ACC_PUBLIC + ACC_SUPER, implementationName, null, classExtended, interfacesImplemented);
}
private void declareConstructor(CheckClassAdapter writer) {
MethodVisitor method = writer.visitMethod(ACC_PUBLIC, "<init>", GENERATED_CONSTRUCTOR, null, null);
method.visitCode();
method.visitVarInsn(ALOAD, 0);
method.visitVarInsn(ILOAD, 1);
method.visitLdcInsn(inspector.getSizeInBytes());
method.visitVarInsn(ALOAD, 2);
method.visitVarInsn(ALOAD, 3);
method.visitMethodInsn(INVOKESPECIAL,
classExtended,
"<init>",
constructorExtended);
method.visitInsn(RETURN);
method.visitMaxs(5, 5);
method.visitEnd();
}
private int declareField(Method getter, ClassVisitor writer, int fieldOffset) {
Primitive type = inspector.getReturn(getter);
MethodVisitor implementingGetter = declareMethod(getter, writer);
declareGetterBody(fieldOffset, type, implementingGetter);
Method setter = inspector.setterFor(getter);
MethodVisitor implementingSetter = declareMethod(setter, writer);
declareSetterBody(fieldOffset, type, implementingSetter);
return fieldOffset + type.sizeInBytes;
}
private MethodVisitor declareMethod(Method method, ClassVisitor writer) {
String name = method.getName();
String descriptor = Type.getMethodDescriptor(method);
return writer.visitMethod(ACC_PUBLIC, name, descriptor, null, null);
}
private void declareGetterBody(int fieldOffset, Primitive type, MethodVisitor method) {
method.visitCode();
declareUnsafe(fieldOffset, method);
// unsafe.getLong
String unsafeGetter = "get" + type.unsafeMethodSuffix();
String unsafeDescriptor = getUnsafeMethodDescriptor(unsafeGetter, Long.TYPE);
method.visitMethodInsn(INVOKEVIRTUAL, UNSAFE_NAME, unsafeGetter, unsafeDescriptor);
method.visitInsn(type.returnOpcode);
method.visitMaxs(4, 4);
method.visitEnd();
}
private void declareSetterBody(int fieldOffset, Primitive type, MethodVisitor method) {
method.visitCode();
Label start = new Label();
method.visitLabel(start);
declareUnsafe(fieldOffset, method);
// load parameter 1
method.visitVarInsn(type.loadOpcode, 1);
// unsafe.putLong
String unsafeSetter = "put" + type.unsafeMethodSuffix();
String unsafeDescriptor = getUnsafeMethodDescriptor(unsafeSetter, Long.TYPE, type.javaEquivalent);
method.visitMethodInsn(INVOKEVIRTUAL, UNSAFE_NAME, unsafeSetter, unsafeDescriptor);
Label end = new Label();
method.visitLabel(end);
method.visitInsn(RETURN);
method.visitLocalVariable("value", Type.getDescriptor(type.javaEquivalent), null, start, end, 0);
method.visitMaxs(4, 4);
method.visitEnd();
}
private void declareUnsafe(int fieldOffset, MethodVisitor method) {
// DirectMemoryCursor.unsafe
method.visitFieldInsn(GETSTATIC, DIRECT_CLASS_NAME, "unsafe", UNSAFE_DESCRIPTOR);
// this.pointer + fieldOffset
method.visitVarInsn(ALOAD, 0);
method.visitFieldInsn(GETFIELD, implementationName, "pointer", LONG_TYPE.getDescriptor());
method.visitLdcInsn((long)fieldOffset);
method.visitInsn(LADD);
}
private String getUnsafeMethodDescriptor(String methodName, Class<?> ... types) {
try {
Method method = Unsafe.class.getMethod(methodName, types);
return Type.getMethodDescriptor(method);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}