/**************************************************************************************
* Copyright (c) Jonas Bon�r, Alexandre Vasseur. All rights reserved. *
* http://aspectwerkz.codehaus.org *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the LGPL license *
* a copy of which has been included with this distribution in the license.txt file. *
**************************************************************************************/
package org.codehaus.aspectwerkz.transform.inlining.weaver;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import org.codehaus.aspectwerkz.transform.Context;
import org.codehaus.aspectwerkz.transform.TransformationUtil;
import org.codehaus.aspectwerkz.transform.TransformationConstants;
import org.codehaus.aspectwerkz.transform.inlining.ContextImpl;
import org.codehaus.aspectwerkz.transform.inlining.EmittedJoinPoint;
import org.codehaus.aspectwerkz.transform.inlining.AsmHelper;
import org.codehaus.aspectwerkz.joinpoint.management.JoinPointType;
import java.util.List;
import java.util.Iterator;
import java.util.Set;
import java.lang.reflect.Modifier;
/**
* Adds field and method and ctor wrappers when there has been at least one joinpoint emitted.
*
* @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
* @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r </a>
*/
public class AddWrapperVisitor extends ClassAdapter implements Opcodes, TransformationConstants {
private ContextImpl m_context;
private Set m_addedMethods;
public AddWrapperVisitor(ClassVisitor classVisitor, Context context, Set alreadyAddedMethods) {
super(classVisitor);
m_context = (ContextImpl) context;
m_addedMethods = alreadyAddedMethods;
}
/**
* Visits the class.
*
* @param access
* @param name
* @param signature
* @param superName
* @param interfaces
*/
public void visit(final int version, final int access,
final String name,
final String signature,
final String superName,
final String[] interfaces) {
// iterate on the emitted joinpoints
// we don't need to filter more since the joinpoint type and the weaving phase did that for us
List jps = m_context.getEmittedJoinPoints();
for (Iterator iterator = jps.iterator(); iterator.hasNext();) {
EmittedJoinPoint emittedJoinPoint = (EmittedJoinPoint) iterator.next();
int jpType = emittedJoinPoint.getJoinPointType();
if (Modifier.isPublic(emittedJoinPoint.getCalleeMemberModifiers())
|| !name.equals(emittedJoinPoint.getCalleeClassName())) {//TODO ?
continue;
}
switch (jpType) {
case (JoinPointType.FIELD_GET_INT) :
createGetFieldWrapperMethod(
Modifier.isStatic(emittedJoinPoint.getCalleeMemberModifiers()),
name,
emittedJoinPoint.getCalleeMemberName(),
emittedJoinPoint.getCalleeMemberDesc(),
null//FIXME generic is that ok ?
);
break;
case (JoinPointType.FIELD_SET_INT) :
createPutFieldWrapperMethod(
Modifier.isStatic(emittedJoinPoint.getCalleeMemberModifiers()),
name,
emittedJoinPoint.getCalleeMemberName(),
emittedJoinPoint.getCalleeMemberDesc(),
null//FIXME generic is that ok ?
);
break;
case (JoinPointType.METHOD_EXECUTION_INT) :
case (JoinPointType.METHOD_CALL_INT) :
createMethodWrapperMethod(
emittedJoinPoint.getCalleeMemberModifiers(),
name,
emittedJoinPoint.getCalleeMemberName(),
emittedJoinPoint.getCalleeMemberDesc(),
null,//FIXME generic is that ok ?
EMPTY_STRING_ARRAY//TODO should throw Throwable ??
);
break;
case (JoinPointType.CONSTRUCTOR_CALL_INT) :
case (JoinPointType.CONSTRUCTOR_EXECUTION_INT) :
createConstructorWrapperMethod(
emittedJoinPoint.getCalleeMemberModifiers(),
name,
emittedJoinPoint.getCalleeMemberName(),
emittedJoinPoint.getCalleeMemberDesc(),
null,//FIXME generic is that ok ?
EMPTY_STRING_ARRAY//TODO should throw Throwable ??
);
break;
}
}
super.visit(version, access, name, signature, superName, interfaces);
}
/**
* Creates a public wrapper method that delegates to the GETFIELD instruction of the non-public field.
*
* @param isStaticField
* @param declaringTypeName
* @param name
* @param desc
* @param signature
*/
private void createGetFieldWrapperMethod(final boolean isStaticField,
final String declaringTypeName,
final String name,
final String desc,
final String signature) {
String wrapperName = TransformationUtil.getWrapperMethodName(
name, desc, declaringTypeName, GETFIELD_WRAPPER_METHOD_PREFIX
);
StringBuffer wsignature = new StringBuffer();
wsignature.append('(');
wsignature.append(')');
wsignature.append(desc);
final String wrapperKey = AlreadyAddedMethodAdapter.getMethodKey(wrapperName, wsignature.toString());
if (m_addedMethods.contains(wrapperKey)) {
return;
}
m_addedMethods.add(wrapperKey);
int modifiers = ACC_SYNTHETIC;
if (isStaticField) {
modifiers |= ACC_STATIC;
}
MethodVisitor mv = cv.visitMethod(
modifiers,
wrapperName,
wsignature.toString(),
signature,
EMPTY_STRING_ARRAY
);
if (isStaticField) {
mv.visitFieldInsn(GETSTATIC, declaringTypeName, name, desc);
} else {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, declaringTypeName, name, desc);
}
AsmHelper.addReturnStatement(mv, Type.getType(desc));
mv.visitMaxs(0, 0);
}
/**
* Creates a public wrapper method that delegates to the PUTFIELD instruction of the non-public field.
* Static method if field is static (PUTSTATIC instr)
*
* @param isStaticField
* @param declaringTypeName
* @param name
* @param desc
* @param signature
*/
private void createPutFieldWrapperMethod(boolean isStaticField,
final String declaringTypeName,
final String name,
final String desc,
final String signature) {
String wrapperName = TransformationUtil.getWrapperMethodName(
name, desc, declaringTypeName, PUTFIELD_WRAPPER_METHOD_PREFIX
);
StringBuffer wsignature = new StringBuffer();
wsignature.append('(');
wsignature.append(desc);
wsignature.append(')');
wsignature.append('V');
final String wrapperKey = AlreadyAddedMethodAdapter.getMethodKey(wrapperName, wsignature.toString());
if (m_addedMethods.contains(wrapperKey)) {
return;
}
m_addedMethods.add(wrapperKey);
int modifiers = ACC_SYNTHETIC;
if (isStaticField) {
modifiers |= ACC_STATIC;
}
MethodVisitor mv = cv.visitMethod(
modifiers,
wrapperName,
wsignature.toString(),
signature,
EMPTY_STRING_ARRAY
);
Type fieldType = Type.getType(desc);
if (isStaticField) {
AsmHelper.loadArgumentTypes(mv, new Type[]{fieldType}, true);
mv.visitFieldInsn(PUTSTATIC, declaringTypeName, name, desc);
} else {
mv.visitVarInsn(ALOAD, 0);
AsmHelper.loadArgumentTypes(mv, new Type[]{fieldType}, false);
mv.visitFieldInsn(PUTFIELD, declaringTypeName, name, desc);
}
AsmHelper.addReturnStatement(mv, Type.VOID_TYPE);
mv.visitMaxs(0, 0);
}
/**
* Creates a public wrapper method that delegates to the non-public target method.
*
* @param access
* @param declaringTypeName
* @param name
* @param desc
* @param signature
* @param exceptions
*/
private void createMethodWrapperMethod(final int access,
final String declaringTypeName,
final String name,
final String desc,
final String signature,
final String[] exceptions) {
final String wrapperName = TransformationUtil.getWrapperMethodName(
name, desc, declaringTypeName, INVOKE_WRAPPER_METHOD_PREFIX
);
final String wrapperKey = AlreadyAddedMethodAdapter.getMethodKey(wrapperName, desc);
if (m_addedMethods.contains(wrapperKey)) {
return;
}
m_addedMethods.add(wrapperKey);
int modifiers = ACC_SYNTHETIC;
if (Modifier.isStatic(access)) {
modifiers |= ACC_STATIC;
}
MethodVisitor mv = super.visitMethod(
modifiers,
wrapperName,
desc,
signature,
exceptions
);
if (Modifier.isStatic(access)) {
AsmHelper.loadArgumentTypes(mv, Type.getArgumentTypes(desc), Modifier.isStatic(access));
mv.visitMethodInsn(INVOKESTATIC, declaringTypeName, name, desc);
} else {
mv.visitVarInsn(ALOAD, 0);
AsmHelper.loadArgumentTypes(mv, Type.getArgumentTypes(desc), Modifier.isStatic(access));
mv.visitMethodInsn(INVOKEVIRTUAL, declaringTypeName, name, desc);
}
AsmHelper.addReturnStatement(mv, Type.getReturnType(desc));
mv.visitMaxs(0, 0);
}
/**
* Creates a public wrapper static method that delegates to the non-public target ctor.
*
* @param access
* @param declaringTypeName
* @param name
* @param desc
* @param signature
* @param exceptions
*/
private void createConstructorWrapperMethod(final int access,
final String declaringTypeName,
final String name,
final String desc,
final String signature,
final String[] exceptions) {
final String wrapperName = TransformationUtil.getWrapperMethodName(
name, desc, declaringTypeName, INVOKE_WRAPPER_METHOD_PREFIX
);
final String wrapperKey = AlreadyAddedMethodAdapter.getMethodKey(wrapperName, desc);
if (m_addedMethods.contains(wrapperKey)) {
return;
}
m_addedMethods.add(wrapperKey);
int modifiers = ACC_SYNTHETIC;
modifiers |= ACC_STATIC;
Type declaringType = Type.getType('L'+declaringTypeName+';');
String ctorDesc = Type.getMethodDescriptor(declaringType, Type.getArgumentTypes(desc));
MethodVisitor mv = super.visitMethod(
modifiers,
wrapperName,
ctorDesc,
signature,
exceptions
);
mv.visitTypeInsn(NEW, declaringTypeName);
mv.visitInsn(DUP);
AsmHelper.loadArgumentTypes(mv, Type.getArgumentTypes(desc), true);
mv.visitMethodInsn(INVOKESPECIAL, declaringTypeName, name, desc);
AsmHelper.addReturnStatement(mv, declaringType);
mv.visitMaxs(0, 0);
}
}