package org.deuce.transaction.global;
import org.deuce.Atomic;
import org.deuce.objectweb.asm.AnnotationVisitor;
import org.deuce.objectweb.asm.Label;
import org.deuce.objectweb.asm.MethodAdapter;
import org.deuce.objectweb.asm.MethodVisitor;
import org.deuce.objectweb.asm.Opcodes;
import org.deuce.objectweb.asm.Type;
public class MethodTransformer extends MethodAdapter{
final static private String ATOMIC_METHOD_POST = "__atomic__";
private final ClassTransformer classTransformer;
private final int access;
private final String name;
private final String desc;
private final String signature;
private final String[] exceptions;
private MethodVisitor atomicVisitor = null; // visitor of the wrapping method if there's one
public MethodTransformer( MethodVisitor visitor, int access, String name, String desc,
String signature, String[] exceptions, ClassTransformer classTransformer) {
super(visitor);
this.access = access;
this.name = name;
this.desc = desc;
this.signature = signature;
this.exceptions = exceptions;
this.classTransformer = classTransformer;
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
boolean atomic = Type.getDescriptor(Atomic.class).equals(desc);
if( atomic) {
atomicVisitor = mv;
// FIXME handle native methods
buildAtomic();
// replace with the logic method
mv = classTransformer.createMethod(
(access & ~Opcodes.ACC_PUBLIC & ~Opcodes.ACC_PROTECTED) | Opcodes.ACC_PRIVATE,
name + ATOMIC_METHOD_POST, this.desc, this.signature, this.exceptions);
}
if( atomicVisitor != null){
return atomicVisitor.visitAnnotation(desc, visible); // annotate the wrapping method
}
return super.visitAnnotation(desc, visible);
}
private void buildAtomic() {
final Type[] types = Type.getArgumentTypes(desc);
final boolean isNonStatic = (access & Opcodes.ACC_STATIC) == 0;
final int lockLocal = locals(types, isNonStatic);
atomicVisitor.visitCode();
// enter synchronized block
Label l0 = new Label();
Label l1 = new Label();
Label l2 = new Label();
atomicVisitor.visitTryCatchBlock(l0, l1, l2, null);
Label l3 = new Label();
atomicVisitor.visitTryCatchBlock(l2, l3, l2, null);
atomicVisitor.visitFieldInsn(Opcodes.GETSTATIC, "org/deuce/transaction/global/Lock",
"lock", "Ljava/lang/Object;");
atomicVisitor.visitInsn(Opcodes.DUP);
atomicVisitor.visitVarInsn(Opcodes.ASTORE, lockLocal);
atomicVisitor.visitInsn(Opcodes.MONITORENTER);
atomicVisitor.visitLabel(l0);
callMethod( atomicVisitor, types, isNonStatic); // Delegates call
// exit synchronized block
atomicVisitor.visitVarInsn(Opcodes.ALOAD, lockLocal);
atomicVisitor.visitInsn(Opcodes.MONITOREXIT);
atomicVisitor.visitLabel(l1);
returnMethod( atomicVisitor); // Returns result
// exit synchronized block
atomicVisitor.visitLabel(l2);
atomicVisitor.visitVarInsn(Opcodes.ALOAD, lockLocal);
atomicVisitor.visitInsn(Opcodes.MONITOREXIT);
atomicVisitor.visitLabel(l3);
atomicVisitor.visitInsn(Opcodes.ATHROW);
atomicVisitor.visitMaxs(0, 0); // compute MAX is set
atomicVisitor.visitEnd();
}
private void callMethod( MethodVisitor methodVisitor, Type[] types, boolean isNonStatic) {
int offset = 0;
if( isNonStatic){
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); // load this
offset = 1;
}
for( int i=0 ; i<types.length ;++i) {
switch( types[i].getSort()) {
case Type.BOOLEAN:
case Type.BYTE:
case Type.CHAR:
case Type.SHORT:
case Type.INT:
methodVisitor.visitVarInsn(Opcodes.ILOAD, i + offset);
break;
case Type.LONG:
methodVisitor.visitVarInsn(Opcodes.LLOAD, i + offset);
break;
case Type.FLOAT:
methodVisitor.visitVarInsn(Opcodes.FLOAD, i + offset);
break;
case Type.DOUBLE:
methodVisitor.visitVarInsn(Opcodes.DLOAD, i + offset);
break;
default:
methodVisitor.visitVarInsn(Opcodes.ALOAD, i + offset);
break;
}
}
if( isNonStatic) {
methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, classTransformer.getClassName(),
name + ATOMIC_METHOD_POST, desc);
}
else {
methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, classTransformer.getClassName(),
name + ATOMIC_METHOD_POST, desc);
}
}
private void returnMethod( MethodVisitor methodVisitor) {
Type type = Type.getReturnType(desc);
switch( type.getSort()) {
case Type.VOID:
mv.visitInsn(Opcodes.RETURN);
break;
case Type.BOOLEAN:
case Type.BYTE:
case Type.CHAR:
case Type.SHORT:
case Type.INT:
mv.visitInsn(Opcodes.IRETURN);
break;
case Type.LONG:
mv.visitInsn(Opcodes.LRETURN);
break;
case Type.FLOAT:
mv.visitInsn(Opcodes.FRETURN);
break;
case Type.DOUBLE:
mv.visitInsn(Opcodes.DRETURN);
break;
default:
mv.visitInsn(Opcodes.ARETURN);
break;
}
}
private int locals( Type[] types, boolean isNonStatic) {
int locals = 0;
if( isNonStatic)
locals = 1;
for( int i=0 ; i<types.length ;++i) {
switch( types[i].getSort()) {
case Type.LONG:
case Type.DOUBLE:
locals += 2;
break;
default:
++locals;
}
}
return locals;
}
}