org.codehaus.aspectwerkz.transform.inlining.weaver.InstanceLevelAspectVisitor.java Source code

Java tutorial

Introduction

Here is the source code for org.codehaus.aspectwerkz.transform.inlining.weaver.InstanceLevelAspectVisitor.java

Source

/**************************************************************************************
 * Copyright (c) Jonas Bonr, 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 Bonr </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 Bonr </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);
            }
        }
    }
}