/**************************************************************************************
* 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 java.util.Set;
import java.util.Iterator;
import java.util.Collection;
import org.objectweb.asm.*;
import org.codehaus.aspectwerkz.transform.Context;
import org.codehaus.aspectwerkz.transform.TransformationConstants;
import org.codehaus.aspectwerkz.transform.inlining.ContextImpl;
import org.codehaus.aspectwerkz.reflect.ClassInfo;
import org.codehaus.aspectwerkz.expression.ExpressionContext;
import org.codehaus.aspectwerkz.expression.PointcutType;
import org.codehaus.aspectwerkz.expression.ExpressionInfo;
import org.codehaus.aspectwerkz.definition.SystemDefinition;
import org.codehaus.aspectwerkz.definition.AdviceDefinition;
import org.codehaus.aspectwerkz.definition.DeploymentScope;
import org.codehaus.aspectwerkz.DeploymentModel;
import org.codehaus.aspectwerkz.perx.PerObjectAspect;
/**
* Adds an instance level aspect management to the target class.
*
* @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r </a>
* @author <a href='mailto:the_mindstorm@evolva.ro'>Alexandru Popescu</a>
*/
public class InstanceLevelAspectVisitor extends ClassAdapter implements TransformationConstants {
private final ContextImpl m_ctx;
private final ClassInfo m_classInfo;
private boolean m_isAdvised = false;
/**
* Creates a new add interface class adapter.
*
* @param cv
* @param classInfo
* @param ctx
*/
public InstanceLevelAspectVisitor(final ClassVisitor cv,
final ClassInfo classInfo,
final Context ctx) {
super(cv);
m_classInfo = classInfo;
m_ctx = (ContextImpl) ctx;
}
/**
* 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) {
if (classFilter(m_classInfo, m_ctx.getDefinitions())) {
super.visit(version, access, name, signature, superName, interfaces);
return;
}
for (int i = 0; i < interfaces.length; i++) {
String anInterface = interfaces[i];
if (anInterface.equals(HAS_INSTANCE_LEVEL_ASPECT_INTERFACE_NAME)) {
super.visit(version, access, name, signature, superName, interfaces);
return;
}
}
String[] newInterfaceArray = new String[interfaces.length + 1];
for (int i = 0; i < interfaces.length; i++) {
newInterfaceArray[i] = interfaces[i];
}
newInterfaceArray[interfaces.length] = HAS_INSTANCE_LEVEL_ASPECT_INTERFACE_NAME;
// add the interface
super.visit(version, access, name, signature, superName, newInterfaceArray);
// add the field with the aspect instance map
addAspectMapField();
// add the getAspect(..) method
addGetAspectMethod(name);
// add the hasAspect(...) method
addHasAspectMethod(name);
addBindAspectMethod(name);
}
/**
* Appends mixin instantiation to the clinit method and/or init method.
*
* @param access
* @param name
* @param desc
* @param signature
* @param exceptions
* @return
*/
public MethodVisitor visitMethod(final int access,
final String name,
final String desc,
final String signature,
final String[] exceptions) {
if (m_isAdvised) {
if (name.equals(INIT_METHOD_NAME)) {
MethodVisitor mv = new AppendToInitMethodCodeAdapter(
cv.visitMethod(access, name, desc, signature, exceptions),
name
);
mv.visitMaxs(0, 0);
return mv;
}
}
return cv.visitMethod(access, name, desc, signature, exceptions);
}
/**
* Adds the aspect map field to the target class.
*/
private void addAspectMapField() {
super.visitField(
ACC_PRIVATE + ACC_SYNTHETIC + ACC_TRANSIENT,
INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE,
null, null
);
}
/**
* Adds the getAspect(..) method to the target class.
*
* @param name the class name of the target class
*/
private void addGetAspectMethod(final String name) {
MethodVisitor cv = super.visitMethod(
ACC_PUBLIC + ACC_SYNTHETIC,
INSTANCE_LEVEL_GETASPECT_METHOD_NAME,
INSTANCE_LEVEL_GETASPECT_METHOD_SIGNATURE,
null, null
);
cv.visitVarInsn(ALOAD, 0);
cv.visitFieldInsn(
GETFIELD,
name,
INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE
);
//--
cv.visitInsn(DUP);
Label ifMapNonNull = new Label();
cv.visitJumpInsn(IFNONNULL, ifMapNonNull);
cv.visitInsn(ACONST_NULL);
cv.visitInsn(ARETURN);
cv.visitLabel(ifMapNonNull);
// // if == null, field = new HashMap()
// Label ifFieldNullNotLabel = new Label();
// cv.visitJumpInsn(IFNONNULL, ifFieldNullNotLabel);
// cv.visitVarInsn(ALOAD, 0);
// cv.visitTypeInsn(NEW, HASH_MAP_CLASS_NAME);
// cv.visitInsn(DUP);
// cv.visitMethodInsn(
// INVOKESPECIAL,
// HASH_MAP_CLASS_NAME,
// INIT_METHOD_NAME,
// NO_PARAM_RETURN_VOID_SIGNATURE
// );
// cv.visitFieldInsn(
// PUTFIELD,
// name,
// INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
// INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE
// );
// cv.visitLabel(ifFieldNullNotLabel);
//
// cv.visitVarInsn(ALOAD, 0);
// cv.visitFieldInsn(
// GETFIELD,
// name,
// INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
// INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE
// );
//
// cv.visitVarInsn(ALOAD, 2);//qName
// cv.visitMethodInsn(INVOKEINTERFACE, MAP_CLASS_NAME, GET_METHOD_NAME, GET_METHOD_SIGNATURE);
// cv.visitVarInsn(ASTORE, 4);
// cv.visitVarInsn(ALOAD, 4);
// Label ifNullNotLabel = new Label();
// cv.visitJumpInsn(IFNONNULL, ifNullNotLabel);
// cv.visitVarInsn(ALOAD, 2);//qName
// cv.visitVarInsn(ALOAD, 3);//containerClassName
// cv.visitVarInsn(ALOAD, 0);//this (perInstance)
// cv.visitMethodInsn(
// INVOKESTATIC,
// ASPECTS_CLASS_NAME,
// ASPECT_OF_METHOD_NAME,
// ASPECT_OF_PER_INSTANCE_METHOD_SIGNATURE
// );
// cv.visitVarInsn(ASTORE, 4);
//cv.visitVarInsn(ALOAD, 0);
//--
cv.visitVarInsn(ALOAD, 1);
cv.visitMethodInsn(INVOKEINTERFACE, MAP_CLASS_NAME, GET_METHOD_NAME, GET_METHOD_SIGNATURE);
//--
// cv.visitFieldInsn(
// GETFIELD,
// name,
// INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
// INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE
// );
cv.visitInsn(ARETURN);
// cv.visitVarInsn(ALOAD, 2);
// cv.visitVarInsn(ALOAD, 4);
// cv.visitMethodInsn(INVOKEINTERFACE, MAP_CLASS_NAME, PUT_METHOD_NAME, PUT_METHOD_SIGNATURE);
// cv.visitInsn(POP);
// cv.visitLabel(ifNullNotLabel);
// cv.visitVarInsn(ALOAD, 4);
// cv.visitInsn(ARETURN);
cv.visitMaxs(0, 0);
m_ctx.markAsAdvised();
m_isAdvised = true;
}
private void addHasAspectMethod(String mapFieldName) {
MethodVisitor cv = super.visitMethod(ACC_PUBLIC + ACC_SYNTHETIC,
INSTANCE_LEVEL_HASASPECT_METHOD_NAME,
INSTANCE_LEVEL_HASASPECT_METHOD_SIGNATURE,
null,
null
);
cv.visitVarInsn(ALOAD, 0);
cv.visitFieldInsn(GETFIELD,
mapFieldName,
INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE
);
cv.visitInsn(DUP);
Label ifMapNonNull = new Label();
cv.visitJumpInsn(IFNONNULL, ifMapNonNull);
cv.visitInsn(ICONST_0);
cv.visitInsn(IRETURN);
cv.visitLabel(ifMapNonNull);
cv.visitVarInsn(ALOAD, 1);
cv.visitMethodInsn(INVOKEINTERFACE, MAP_CLASS_NAME, "containsKey", "(Ljava/lang/Object;)Z");
//cv.visitMethodInsn(INVOKEINTERFACE, MAP_CLASS_NAME, GET_METHOD_NAME, GET_METHOD_SIGNATURE);
//
// Label ifNullLabel = new Label();
// cv.visitJumpInsn(IFNULL, ifNullLabel);
// cv.visitInsn(ICONST_1);
// cv.visitInsn(IRETURN);
// cv.visitLabel(ifNullLabel);
// cv.visitInsn(ICONST_0);
cv.visitInsn(IRETURN);
cv.visitMaxs(0, 0);
m_ctx.markAsAdvised();
m_isAdvised = true;
}
private void addBindAspectMethod(final String name) {
MethodVisitor cv = super.visitMethod(
ACC_PUBLIC + ACC_SYNTHETIC,
INSTANCE_LEVEL_BINDASPECT_METHOD_NAME,
INSTANCE_LEVEL_BINDASPECT_METHOD_SIGNATURE,
null, null
);
cv.visitVarInsn(ALOAD, 0);
cv.visitFieldInsn(
GETFIELD,
name,
INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE
);
// if == null, field = new HashMap()
Label ifFieldNullNotLabel = new Label();
cv.visitJumpInsn(IFNONNULL, ifFieldNullNotLabel);
cv.visitVarInsn(ALOAD, 0);
cv.visitTypeInsn(NEW, HASH_MAP_CLASS_NAME);
cv.visitInsn(DUP);
cv.visitMethodInsn(
INVOKESPECIAL,
HASH_MAP_CLASS_NAME,
INIT_METHOD_NAME,
NO_PARAM_RETURN_VOID_SIGNATURE
);
cv.visitFieldInsn(
PUTFIELD,
name,
INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE
);
cv.visitLabel(ifFieldNullNotLabel);
cv.visitVarInsn(ALOAD, 0);
cv.visitFieldInsn(
GETFIELD,
name,
INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE
);
cv.visitVarInsn(ALOAD, 1);
cv.visitVarInsn(ALOAD, 2);
cv.visitMethodInsn(INVOKEINTERFACE, MAP_CLASS_NAME, PUT_METHOD_NAME, PUT_METHOD_SIGNATURE);
cv.visitVarInsn(ALOAD, 2);
cv.visitInsn(ARETURN);
}
/**
* Filters the classes to be transformed.
*
* @param classInfo the class to filter
* @param definitions a set with the definitions
* @return boolean true if the method should be filtered away
*/
public static boolean classFilter(final ClassInfo classInfo, final Set definitions) {
if (classInfo.isInterface()) {
return true;
}
ExpressionContext ctx = new ExpressionContext(PointcutType.WITHIN, null, classInfo);
for (Iterator it = definitions.iterator(); it.hasNext();) {
SystemDefinition systemDef = (SystemDefinition) it.next();
String className = classInfo.getName().replace('/', '.');
if (systemDef.inExcludePackage(className)) {
return true;
}
if (!systemDef.inIncludePackage(className)) {
return true;
}
Collection adviceDefs = systemDef.getAdviceDefinitions();
for (Iterator defs = adviceDefs.iterator(); defs.hasNext();) {
AdviceDefinition adviceDef = (AdviceDefinition) defs.next();
ExpressionInfo expressionInfo = adviceDef.getExpressionInfo();
if (expressionInfo == null) {
continue;
}
DeploymentModel deploymentModel = adviceDef.getDeploymentModel();
// match on perinstance deployed aspects
if (DeploymentModel.PER_INSTANCE.equals(deploymentModel)) {
if (expressionInfo.getAdvisedClassFilterExpression().match(ctx)) {
return false;
}
}
// match on perthis/pertarget perX X pointcuts
if (adviceDef.getAspectClassName().equals(PerObjectAspect.PEROBJECT_ASPECT_NAME)) {
ExpressionInfo perXExpressionInfo = adviceDef.getExpressionInfo();
if (perXExpressionInfo.getAdvisedClassFilterExpression().match(ctx)) {
return false;
}
}
}
// match on deployment scopes, e.g. potential perinstance deployment aspects
Collection deploymentScopes = systemDef.getDeploymentScopes();
for (Iterator scopes = deploymentScopes.iterator(); scopes.hasNext();) {
DeploymentScope deploymentScope = (DeploymentScope) scopes.next();
ExpressionInfo expression = new ExpressionInfo(
deploymentScope.getExpression(),
systemDef.getUuid()
);
if (expression.getAdvisedClassFilterExpression().match(ctx)) {
return false;
}
}
}
return true;
}
/**
* Adds initialization of aspect map field to end of the init method.
*
* @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r </a>
*/
private class AppendToInitMethodCodeAdapter extends AfterObjectInitializationCodeAdapter {
private boolean m_done = false;
public AppendToInitMethodCodeAdapter(final MethodVisitor ca, String callerMemberName) {
super(ca, callerMemberName);
}
/**
* Inserts the init of the aspect field right after the call to super(..) of this(..).
*
* @param opcode
* @param owner
* @param name
* @param desc
*/
public void visitMethodInsn(int opcode,
String owner,
String name,
String desc) {
super.visitMethodInsn(opcode, owner, name, desc);
if (opcode == INVOKESPECIAL && m_isObjectInitialized && !m_done) {
m_done = true;
// initialize aspect map field
mv.visitVarInsn(ALOAD, 0);
mv.visitTypeInsn(NEW, HASH_MAP_CLASS_NAME);
mv.visitInsn(DUP);
mv.visitMethodInsn(
INVOKESPECIAL,
HASH_MAP_CLASS_NAME,
INIT_METHOD_NAME,
NO_PARAM_RETURN_VOID_SIGNATURE
);
mv.visitFieldInsn(
PUTFIELD,
m_classInfo.getName().replace('.', '/'),
INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE
);
}
}
}
}