package org.deuce.transform.asm.method;
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;
import org.deuce.objectweb.asm.commons.AnalyzerAdapter;
import org.deuce.objectweb.asm.commons.Method;
import org.deuce.transaction.Context;
import org.deuce.transaction.ContextDelegator;
import org.deuce.transaction.strongiso.field.ArrayReadFieldAccess;
import org.deuce.transform.UseStrongIso;
import org.deuce.transform.asm.ClassTransformer;
import org.deuce.transform.asm.ExcludeIncludeStore;
import org.deuce.transform.asm.FieldsHolder;
import org.deuce.transform.util.Util;
public class DuplicateMethod extends MethodAdapter{
final static public String LOCAL_VARIBALE_NAME = "__transactionContext__";
private final int argumentsSize;
private final FieldsHolder fieldsHolder;
private Label firstLabel;
private Label lastLabel;
private boolean addContextToTable = false;
private AnalyzerAdapter analyzerAdapter;
public DuplicateMethod(MethodVisitor mv, boolean isstatic, Method newMethod, FieldsHolder fieldsHolder) {
super(mv);
this.fieldsHolder = fieldsHolder;
this.argumentsSize = calcArgumentsSize( isstatic, newMethod);
}
public void setAnalyzer(AnalyzerAdapter analyzerAdapter) {
this.analyzerAdapter = analyzerAdapter;
}
@Override
public void visitMethodInsn(int opcode, String owner, String name,
String desc)
{
if( ExcludeIncludeStore.exclude(owner))
{
super.visitMethodInsn(opcode, owner, name, desc); // ... = foo( ...
}
else
{
super.visitVarInsn(Opcodes.ALOAD, argumentsSize - 1); // load context
Method newMethod = ClassTransformer.createNewMethod(name, desc);
super.visitMethodInsn(opcode, owner, name, newMethod.getDescriptor()); // ... = foo( ...
}
}
/**
* Adds for each field visited a call to the context.
*/
@Override
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
if( ExcludeIncludeStore.exclude( owner) ||
name.contains("$")){ // Syntactic TODO remove this limitation
super.visitFieldInsn(opcode, owner, name, desc); // ... = foo( ...
return;
}
String fieldsHolderName = fieldsHolder.getFieldsHolderName(owner);
mv.visitFieldInsn(Opcodes.GETSTATIC, fieldsHolderName, Util.getAddressField(name), "J");
Label l1 = new Label();
mv.visitInsn(Opcodes.LCONST_0);
mv.visitInsn(Opcodes.LCMP);
mv.visitJumpInsn(Opcodes.IFGE, l1);
super.visitFieldInsn(opcode, owner, name, desc);
Label l2 = new Label();
mv.visitJumpInsn(Opcodes.GOTO, l2);
mv.visitLabel(l1);
final Type type = Type.getType(desc);
switch( opcode) {
case Opcodes.GETFIELD: // ALOAD 0: this (stack status)
addBeforeReadCall(fieldsHolderName, opcode, owner, name, desc);
super.visitInsn(Opcodes.DUP);
super.visitFieldInsn(opcode, owner, name, desc);
super.visitFieldInsn( Opcodes.GETSTATIC, fieldsHolderName, Util.getAddressField(name) , "J");
super.visitVarInsn(Opcodes.ALOAD, argumentsSize - 1); // load context
super.visitMethodInsn( Opcodes.INVOKESTATIC, ContextDelegator.CONTEXT_DELEGATOR_INTERNAL,
ContextDelegator.READ_METHOD_NAME, ContextDelegator.getReadMethodDesc(type));
if( type.getSort() >= Type.ARRAY) // non primitive
super.visitTypeInsn( Opcodes.CHECKCAST, Type.getType(desc).getInternalName());
break;
case Opcodes.PUTFIELD:
super.visitFieldInsn( Opcodes.GETSTATIC, fieldsHolderName, Util.getAddressField(name) , "J");
if(UseStrongIso.USE_STRONG_ISO)
super.visitFieldInsn( Opcodes.GETSTATIC, fieldsHolderName, Util.getObjectAddressField(name) , "J");
super.visitVarInsn(Opcodes.ALOAD, argumentsSize - 1); // load context
super.visitMethodInsn( Opcodes.INVOKESTATIC, ContextDelegator.CONTEXT_DELEGATOR_INTERNAL,
ContextDelegator.WRITE_METHOD_NAME, ContextDelegator.getWriteMethodDesc(type));
break;
case Opcodes.GETSTATIC: // check support for static fields
super.visitFieldInsn(Opcodes.GETSTATIC, fieldsHolderName,
StaticMethodTransformer.CLASS_BASE, "Ljava/lang/Object;");
addBeforeReadCall(fieldsHolderName, opcode, owner, name, desc);
super.visitFieldInsn(opcode, owner, name, desc);
super.visitFieldInsn(Opcodes.GETSTATIC, fieldsHolderName, Util.getAddressField(name) , "J");
super.visitVarInsn(Opcodes.ALOAD, argumentsSize - 1); // load context
super.visitMethodInsn( Opcodes.INVOKESTATIC, ContextDelegator.CONTEXT_DELEGATOR_INTERNAL,
ContextDelegator.READ_METHOD_NAME, ContextDelegator.getReadMethodDesc(type));
if( type.getSort() >= Type.ARRAY) // non primitive
super.visitTypeInsn( Opcodes.CHECKCAST, Type.getType(desc).getInternalName());
break;
case Opcodes.PUTSTATIC:
super.visitFieldInsn(Opcodes.GETSTATIC, fieldsHolderName,
StaticMethodTransformer.CLASS_BASE, "Ljava/lang/Object;");
super.visitFieldInsn( Opcodes.GETSTATIC, fieldsHolderName, Util.getAddressField(name) , "J");
if(UseStrongIso.USE_STRONG_ISO)
super.visitFieldInsn( Opcodes.GETSTATIC, fieldsHolderName, Util.getObjectAddressField(name) , "J");
super.visitVarInsn(Opcodes.ALOAD, argumentsSize - 1); // load context
super.visitMethodInsn( Opcodes.INVOKESTATIC, ContextDelegator.CONTEXT_DELEGATOR_INTERNAL,
ContextDelegator.STATIC_WRITE_METHOD_NAME, ContextDelegator.getStaticWriteMethodDesc(type));
break;
default:
super.visitFieldInsn(opcode, owner, name, desc);
}
mv.visitLabel(l2);
}
private void addBeforeReadCall(String fieldsHolderName, int opcode, String owner, String name, String desc) {
//super.visitInsn(Opcodes.DUP);
super.visitInsn(Opcodes.DUP);
if(UseStrongIso.USE_STRONG_ISO)
super.visitInsn(Opcodes.DUP);
super.visitFieldInsn( Opcodes.GETSTATIC, fieldsHolderName, Util.getAddressField(name) , "J");
if(UseStrongIso.USE_STRONG_ISO) {
// FIXME Dont need to do this because not doing the arrays right anyway
UseStrongIso.owner = fieldsHolderName;
UseStrongIso.name = name;
UseStrongIso.desc = desc;
//super.visitFieldInsn( Opcodes.GETFIELD, fieldsHolderName, name , desc);
//super.visitInsn(Opcodes.DUP);
super.visitFieldInsn( Opcodes.GETSTATIC, fieldsHolderName, Util.getObjectAddressField(name) , "J");
}
super.visitVarInsn(Opcodes.ALOAD, argumentsSize - 1); // load context
super.visitMethodInsn( Opcodes.INVOKESTATIC, ContextDelegator.CONTEXT_DELEGATOR_INTERNAL,
ContextDelegator.BEFORE_READ_METHOD_NAME, ContextDelegator.BEFORE_READ_METHOD_DESC);
}
/**
* Adds for each array cell visited a call to the context
*/
@Override
public void visitInsn(int opcode) {
boolean load = false;
boolean store = false;
String desc = null;
String arrayMemeberType = null;
switch( opcode) {
case Opcodes.AALOAD:
// handle Object[] arrays type, the type before the last is the array.
// The substring removes the '[' from the array type
String arrayType =
(String)this.analyzerAdapter.stack.get(this.analyzerAdapter.stack.size() - 2);
arrayMemeberType = getArrayMemberType( arrayType);
desc = ContextDelegator.READ_ARRAY_METHOD_OBJ_DESC;
load = true;
break;
case Opcodes.BALOAD:
desc = ContextDelegator.READ_ARRAY_METHOD_BYTE_DESC;
load = true;
break;
case Opcodes.CALOAD:
desc = ContextDelegator.READ_ARRAY_METHOD_CHAR_DESC;
load = true;
break;
case Opcodes.SALOAD:
desc = ContextDelegator.READ_ARRAY_METHOD_SHORT_DESC;
load = true;
break;
case Opcodes.IALOAD:
desc = ContextDelegator.READ_ARRAY_METHOD_INT_DESC;
load = true;
break;
case Opcodes.LALOAD:
desc = ContextDelegator.READ_ARRAY_METHOD_LONG_DESC;
load = true;
break;
case Opcodes.FALOAD:
desc = ContextDelegator.READ_ARRAY_METHOD_FLOAT_DESC;
load = true;
break;
case Opcodes.DALOAD:
desc = ContextDelegator.READ_ARRAY_METHOD_DOUBLE_DESC;
load = true;
break;
case Opcodes.AASTORE:
desc = ContextDelegator.WRITE_ARRAY_METHOD_OBJ_DESC;
store = true;
break;
case Opcodes.BASTORE:
desc = ContextDelegator.WRITE_ARRAY_METHOD_BYTE_DESC;
store = true;
break;
case Opcodes.CASTORE:
desc = ContextDelegator.WRITE_ARRAY_METHOD_CHAR_DESC;
store = true;
break;
case Opcodes.SASTORE:
desc = ContextDelegator.WRITE_ARRAY_METHOD_SHORT_DESC;
store = true;
break;
case Opcodes.IASTORE:
desc = ContextDelegator.WRITE_ARRAY_METHOD_INT_DESC;
store = true;
break;
case Opcodes.LASTORE:
desc = ContextDelegator.WRITE_ARRAY_METHOD_LONG_DESC;
store = true;
break;
case Opcodes.FASTORE:
desc = ContextDelegator.WRITE_ARRAY_METHOD_FLOAT_DESC;
store = true;
break;
case Opcodes.DASTORE:
desc = ContextDelegator.WRITE_ARRAY_METHOD_DOUBLE_DESC;
store = true;
break;
}
if( load)
{
if(UseStrongIso.USE_STRONG_ISO) {
String sisoDesc = Type.getType(ArrayReadFieldAccess.class).getDescriptor();
//super.visitFieldInsn( Opcodes.GETFIELD, UseStrongIso.owner, Util.getObjectField(UseStrongIso.name) , sisoDesc);
// FIXME THIS IS WRONG, HOW TO DO ARRAYS????
super.visitFieldInsn( Opcodes.GETSTATIC, UseStrongIso.owner, Util.getObjectAddressField(UseStrongIso.name) , "J");
}
super.visitVarInsn(Opcodes.ALOAD, argumentsSize - 1); // load context
super.visitMethodInsn( Opcodes.INVOKESTATIC, ContextDelegator.CONTEXT_DELEGATOR_INTERNAL,
ContextDelegator.READ_ARR_METHOD_NAME, desc);
if( opcode == Opcodes.AALOAD){ // non primitive array need cast
super.visitTypeInsn( Opcodes.CHECKCAST, arrayMemeberType);
}
}
else if( store)
{
super.visitVarInsn(Opcodes.ALOAD, argumentsSize - 1); // load context
super.visitMethodInsn( Opcodes.INVOKESTATIC, ContextDelegator.CONTEXT_DELEGATOR_INTERNAL,
ContextDelegator.WRITE_ARR_METHOD_NAME, desc);
}
else{
super.visitInsn(opcode);
}
}
@Override
public void visitIincInsn(int var, int increment) {
super.visitIincInsn( newIndex(var), increment); // increase index due to context
}
@Override
public void visitLabel(Label label) {
if( firstLabel == null)
firstLabel = label;
lastLabel = label;
super.visitLabel(label);
}
@Override
public void visitLocalVariable(String name, String desc, String signature, Label start,
Label end, int index) {
if( this.argumentsSize > index + 1) // argument
{
super.visitLocalVariable(name, desc, signature, start, end, index); // non static method has this
return;
}
// add context as last argument
// the first local variable and was never added before
if( this.argumentsSize == index + 1 && !addContextToTable)
{
addContextToTable = true;
super.visitLocalVariable(LOCAL_VARIBALE_NAME, Context.CONTEXT_DESC, null,
firstLabel, lastLabel, index);
}
// increase all the locals index
super.visitLocalVariable(name, desc, signature, start, end, index + 1);
}
@Override
public void visitMaxs(int maxStack, int maxLocals) {
super.visitMaxs(maxStack + 3, maxLocals + 1);
}
@Override
public void visitVarInsn(int opcode, int var) {
// increase the local variable index by 1
super.visitVarInsn(opcode, newIndex(var));
}
/**
* Calculate the new local index according to its position.
* If it's not a function argument (local variable) its index increased by 1.
* @param currIndex current index
* @return new index
*/
private int newIndex( int currIndex){
return currIndex + 1 < this.argumentsSize ? currIndex : currIndex + 1;
}
private int calcArgumentsSize( boolean isStatic, Method newMethod){
int size = isStatic ? 0 : 1; // if not static "this" is the first argument
for( Type type : newMethod.getArgumentTypes()){
size += type.getSize();
}
return size;
}
private String getArrayMemberType( String arrayType){
if( arrayType.charAt(arrayType.length() - 1) == ';' && // primitive array
arrayType.charAt(1) != '[' ) // array of arrays
return arrayType.substring(2, arrayType.length()-1);
return arrayType.substring(1, arrayType.length()); // array of Objects
}
}