package org.deuce.transform.asm;
import java.lang.annotation.Annotation;
import java.util.LinkedList;
import org.deuce.objectweb.asm.AnnotationVisitor;
import org.deuce.objectweb.asm.FieldVisitor;
import org.deuce.objectweb.asm.MethodVisitor;
import org.deuce.objectweb.asm.Opcodes;
import org.deuce.objectweb.asm.Type;
import org.deuce.objectweb.asm.commons.Method;
import org.deuce.transaction.Context;
import org.deuce.transaction.strongiso.field.ReadFieldAccess;
import org.deuce.transform.Exclude;
import org.deuce.transform.UseStrongIso;
import org.deuce.transform.asm.method.MethodTransformer;
import org.deuce.transform.asm.method.StaticMethodTransformer;
import org.deuce.transform.asm.type.TypeCodeResolver;
import org.deuce.transform.asm.type.TypeCodeResolverFactory;
import org.deuce.transform.util.Util;
@Exclude
public class ClassTransformer extends ByteCodeVisitor implements FieldsHolder{
final private static String ENUM_DESC = Type.getInternalName(Enum.class);
private boolean exclude = false;
private boolean visitclinit = false;
final private LinkedList<Field> fields = new LinkedList<Field>();
private String staticField = null;
final static public String EXCLUDE_DESC = Type.getDescriptor(Exclude.class);
final static private String ANNOTATION_NAME = Type.getInternalName(Annotation.class);
private boolean isInterface;
private boolean isEnum;
private MethodVisitor staticMethod;
private final FieldsHolder fieldsHolder;
public ClassTransformer( String className, FieldsHolder fieldsHolder){
super( className);
this.fieldsHolder = fieldsHolder == null ? this : fieldsHolder;
}
@Override
public void visit(final int version, final int access, final String name,
final String signature, final String superName, final String[] interfaces) {
fieldsHolder.visit(superName);
isInterface = (access & Opcodes.ACC_INTERFACE) != 0;
isEnum = ENUM_DESC.equals(superName);
for(String inter : interfaces){
if( inter.equals(ANNOTATION_NAME)){
exclude = true;
break;
}
}
super.visit(version, access, name, signature, superName, interfaces);
}
/**
* Checks if the class is marked as {@link Exclude @Exclude}
*/
@Override
public AnnotationVisitor visitAnnotation( String desc, boolean visible) {
exclude = exclude ? exclude : EXCLUDE_DESC.equals(desc);
return super.visitAnnotation(desc, visible);
}
/**
* Creates a new static filed for each existing field.
* The field will be statically initialized to hold the field address.
*/
@Override
public FieldVisitor visitField(int access, String name, String desc, String signature,
Object value) {
FieldVisitor fieldVisitor = super.visitField(access, name, desc, signature, value);
if( exclude)
return fieldVisitor;
// Define as constant
int fieldAccess = Opcodes.ACC_FINAL | Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC;
int fieldAccess2 = Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC;
String addressFieldName = Util.getAddressField( name);
String objectFieldName = Util.getObjectField(name);
String objectAddressFieldName = Util.getObjectAddressField(name);
final boolean include = (access & Opcodes.ACC_FINAL) == 0;
final boolean isStatic = (access & Opcodes.ACC_STATIC) != 0;
if( include){ // include field if not final
Field field = new Field(name, addressFieldName);
fields.add( field);
if(isStatic)
staticField = name;
fieldsHolder.addField( fieldAccess, addressFieldName, Type.LONG_TYPE.getDescriptor(), null);
if(UseStrongIso.USE_STRONG_ISO) {
//field = new Field(name, objectFieldName);
//fields.add( field);
if(Type.getType(desc).getSort() == Type.ARRAY)
fieldsHolder.addField( fieldAccess2, objectFieldName, Type.getType(ReadFieldAccess[].class).getDescriptor(), null);
else
fieldsHolder.addField( fieldAccess2, objectFieldName, Type.getType(ReadFieldAccess.class).getDescriptor(), null);
field = new Field(name, objectAddressFieldName);
fields.add( field);
field.objName = objectFieldName;
fieldsHolder.addField( fieldAccess, objectAddressFieldName, Type.LONG_TYPE.getDescriptor(), null);
}
}else{
// If this field is final mark with a negative address.
fieldsHolder.addField( fieldAccess, addressFieldName, Type.LONG_TYPE.getDescriptor(), -1L);
}
return fieldVisitor;
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature,
String[] exceptions) {
MethodVisitor originalMethod = super.visitMethod(access, name, desc, signature, exceptions);
if( exclude)
return originalMethod;
final boolean isNative = (access & Opcodes.ACC_NATIVE) != 0;
if(isNative){
createNativeMethod(access, name, desc, signature, exceptions);
return originalMethod;
}
if( name.equals("<clinit>")) {
staticMethod = originalMethod;
visitclinit = true;
if( isInterface){
return originalMethod;
}
int fieldAccess = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC;
fieldsHolder.addField( fieldAccess, StaticMethodTransformer.CLASS_BASE,
Type.getDescriptor(Object.class), null);
MethodVisitor staticMethodVisitor = fieldsHolder.getStaticMethodVisitor();
return createStaticMethodTransformer( originalMethod, staticMethodVisitor);
}
Method newMethod = createNewMethod(name, desc);
// Create a new duplicate SYNTHETIC method and remove the final marker if has one.
MethodVisitor copyMethod = super.visitMethod((access | Opcodes.ACC_SYNTHETIC) & ~Opcodes.ACC_FINAL, name, newMethod.getDescriptor(),
signature, exceptions);
return new MethodTransformer( originalMethod, copyMethod, className,
access, name, desc, newMethod, fieldsHolder);
}
/**
* Build a dummy method that delegates the call to the native method
*/
private void createNativeMethod(int access, String name, String desc,
String signature, String[] exceptions) {
Method newMethod = createNewMethod(name, desc);
final int newAccess = access & ~Opcodes.ACC_NATIVE;
MethodVisitor copyMethod = super.visitMethod(newAccess | Opcodes.ACC_SYNTHETIC, name, newMethod.getDescriptor(),
signature, exceptions);
copyMethod.visitCode();
// load the arguments before calling the original method
final boolean isStatic = (access & ~Opcodes.ACC_STATIC) != 0;
int place = 0; // place on the stack
if(!isStatic){
copyMethod.visitVarInsn(Opcodes.ALOAD, 0); // load this
place = 1;
}
Type[] argumentTypes = newMethod.getArgumentTypes();
for(int i=0 ; i<(argumentTypes.length-1) ; ++i){
Type type = argumentTypes[i];
copyMethod.visitVarInsn(type.getOpcode(Opcodes.ILOAD), place);
place += type.getSize();
}
// call the original method
copyMethod.visitMethodInsn(isStatic ? Opcodes.INVOKESTATIC : Opcodes.INVOKEVIRTUAL, className, name, desc);
TypeCodeResolver returnReolver = TypeCodeResolverFactory.getReolver(newMethod.getReturnType());
if( returnReolver == null) {
copyMethod.visitInsn( Opcodes.RETURN); // return;
}else {
copyMethod.visitInsn(returnReolver.returnCode());
}
copyMethod.visitMaxs(1, 1);
copyMethod.visitEnd();
}
@Override
public void visitEnd() {
//Didn't see any static method till now, so creates one.
if(!exclude){
super.visitAnnotation(EXCLUDE_DESC, false);
if( !visitclinit && fields.size() > 0) { // creates a new <clinit> in case we didn't see one already.
//TODO avoid creating new static method in case of external fields holder
visitclinit = true;
MethodVisitor method = visitMethod(Opcodes.ACC_STATIC, "<clinit>", "()V", null, null);
method.visitCode();
method.visitInsn(Opcodes.RETURN);
method.visitMaxs(100, 100); // TODO set the right value
method.visitEnd();
}
if(isEnum){ // Build a dummy ordinal() method
MethodVisitor ordinalMethod =
super.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, "ordinal", "(Lorg/deuce/transaction/Context;)I", null, null);
ordinalMethod.visitCode();
ordinalMethod.visitVarInsn(Opcodes.ALOAD, 0);
ordinalMethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, className, "ordinal", "()I");
ordinalMethod.visitInsn(Opcodes.IRETURN);
ordinalMethod.visitMaxs(1, 2);
ordinalMethod.visitEnd();
}
}
super.visitEnd();
fieldsHolder.close();
}
private StaticMethodTransformer createStaticMethodTransformer(MethodVisitor originalMethod, MethodVisitor staticMethod){
return new StaticMethodTransformer( originalMethod, staticMethod, fields, staticField,
className, fieldsHolder.getFieldsHolderName(className));
}
public static Method createNewMethod(String name, String desc) {
Method method = new Method( name, desc);
Type[] arguments = method.getArgumentTypes();
Type[] newArguments = new Type[ arguments.length + 1];
System.arraycopy( arguments, 0, newArguments, 0, arguments.length);
newArguments[newArguments.length - 1] = Context.CONTEXT_TYPE; // add as a constant
return new Method( name, method.getReturnType(), newArguments);
}
@Override
public void addField(int fieldAccess, String addressFieldName, String desc, Object value){
super.visitField( fieldAccess, addressFieldName, desc, null, value);
}
@Override
public void close(){
}
@Override
public MethodVisitor getStaticMethodVisitor(){
return staticMethod;
}
@Override
public String getFieldsHolderName(String owner){
return owner;
}
@Override
public void visit(String superName) {
//nothing to do
}
}