org.codehaus.aspectwerkz.transform.inlining.compiler.RuntimeCheckVisitor.java Source code

Java tutorial

Introduction

Here is the source code for org.codehaus.aspectwerkz.transform.inlining.compiler.RuntimeCheckVisitor.java

Source

/**************************************************************************************
 * 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.compiler;

import org.codehaus.aspectwerkz.expression.ExpressionVisitor;
import org.codehaus.aspectwerkz.expression.Undeterministic;
import org.codehaus.aspectwerkz.expression.ExpressionContext;
import org.codehaus.aspectwerkz.expression.ExpressionNamespace;
import org.codehaus.aspectwerkz.expression.ExpressionInfo;
import org.codehaus.aspectwerkz.expression.ast.ASTOr;
import org.codehaus.aspectwerkz.expression.ast.ASTAnd;
import org.codehaus.aspectwerkz.expression.ast.ASTNot;
import org.codehaus.aspectwerkz.expression.ast.ASTTarget;
import org.codehaus.aspectwerkz.expression.ast.ASTPointcutReference;
import org.codehaus.aspectwerkz.expression.ast.ASTExecution;
import org.codehaus.aspectwerkz.expression.ast.ASTCall;
import org.codehaus.aspectwerkz.expression.ast.ASTSet;
import org.codehaus.aspectwerkz.expression.ast.ASTGet;
import org.codehaus.aspectwerkz.expression.ast.ASTHandler;
import org.codehaus.aspectwerkz.expression.ast.ASTStaticInitialization;
import org.codehaus.aspectwerkz.expression.ast.ASTWithin;
import org.codehaus.aspectwerkz.expression.ast.ASTWithinCode;
import org.codehaus.aspectwerkz.expression.ast.ASTHasMethod;
import org.codehaus.aspectwerkz.expression.ast.ASTHasField;
import org.codehaus.aspectwerkz.expression.ast.ASTThis;
import org.codehaus.aspectwerkz.expression.ast.ASTCflow;
import org.codehaus.aspectwerkz.expression.ast.ASTCflowBelow;
import org.codehaus.aspectwerkz.expression.ast.ASTArgs;
import org.codehaus.aspectwerkz.transform.inlining.compiler.AbstractJoinPointCompiler;
import org.codehaus.aspectwerkz.transform.inlining.AsmHelper;
import org.codehaus.aspectwerkz.transform.TransformationConstants;
import org.codehaus.aspectwerkz.cflow.CflowCompiler;
import org.codehaus.aspectwerkz.aspect.AdviceInfo;
import org.codehaus.aspectwerkz.aspect.container.AspectFactoryManager;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

/**
 * Visit an expression and push on the bytecode stack the boolean expression that corresponds to the residual
 * part for the target(CALLEE) filtering and cflow / cflowbelow runtime checks
 * <p/>
 * TODO: for now OR / AND / NOT are turned in IAND etc, ie "&" and not "&&" that is more efficient but is using labels.
 * <p/>
 * Note: we have to override here (and maintain) every visit Method that visit a node that appears in an expression
 * (f.e. set , get, etc, but not ASTParameter), since we cannot rely on AND/OR/NOT nodes to push the boolean expressions.
 *
 * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur</a>
 */
public class RuntimeCheckVisitor extends ExpressionVisitor implements Opcodes {
    public static final int NULL_PER_OBJECT_TYPE = -1;
    public static final int PER_THIS_TYPE = 1;
    public static final int PER_TARGET_TYPE = 2;

    private MethodVisitor cv;

    private ExpressionInfo m_expressionInfo;

    private CompilerInput m_input;

    private int m_perObjectCheckType = NULL_PER_OBJECT_TYPE;

    private String m_aspectQName;

    /**
     * Create a new visitor given a specific AdviceInfo
     *
     * @param cv                   of the method block we are compiling
     * @param info                 expression info
     * @param input
     * @param perObjectType
     * @param aspectQName
     */
    public RuntimeCheckVisitor(final MethodVisitor cv, final ExpressionInfo info, final CompilerInput input,
            final int perObjectType, final String aspectQName) {
        super(info, info.toString(), info.getNamespace(), info.getExpression().getASTRoot());
        m_expressionInfo = info;
        m_input = input;
        m_perObjectCheckType = perObjectType;
        m_aspectQName = aspectQName;

        this.cv = cv;
    }

    /**
     * Push the boolean typed expression on the stack.
     *
     * @param adviceInfo
     */
    public void pushCheckOnStack(AdviceInfo adviceInfo) {
        super.match(adviceInfo.getExpressionContext());

        switch (m_perObjectCheckType) {
        case PER_THIS_TYPE: {
            AbstractJoinPointCompiler.loadCaller(cv, m_input);
            cv.visitMethodInsn(INVOKESTATIC,
                    AspectFactoryManager.getAspectFactoryClassName(adviceInfo.getAspectClassName(),
                            adviceInfo.getAspectQualifiedName()),
                    TransformationConstants.FACTORY_HASASPECT_METHOD_NAME,
                    TransformationConstants.FACTORY_HASASPECT_PEROBJECT_METHOD_SIGNATURE);
            cv.visitInsn(IAND);

            break;
        }

        case PER_TARGET_TYPE: {
            AbstractJoinPointCompiler.loadCallee(cv, m_input);
            cv.visitMethodInsn(INVOKESTATIC,
                    AspectFactoryManager.getAspectFactoryClassName(adviceInfo.getAspectClassName(),
                            adviceInfo.getAspectQualifiedName()),
                    TransformationConstants.FACTORY_HASASPECT_METHOD_NAME,
                    TransformationConstants.FACTORY_HASASPECT_PEROBJECT_METHOD_SIGNATURE);
            cv.visitInsn(IAND);

            break;
        }
        }
    }

    /**
     * Handles OR expression
     *
     * @param node
     * @param data
     * @return
     */
    public Object visit(ASTOr node, Object data) {
        Boolean matchL = (Boolean) node.jjtGetChild(0).jjtAccept(this, data);
        Boolean matchR = (Boolean) node.jjtGetChild(1).jjtAccept(this, data);
        Boolean intermediate = Undeterministic.or(matchL, matchR);
        cv.visitInsn(IOR);
        for (int i = 2; i < node.jjtGetNumChildren(); i++) {
            Boolean matchNext = (Boolean) node.jjtGetChild(i).jjtAccept(this, data);
            intermediate = Undeterministic.or(intermediate, matchNext);
            cv.visitInsn(IOR);
        }
        return intermediate;
    }

    public Object visit(ASTAnd node, Object data) {
        Boolean matchL = (Boolean) node.jjtGetChild(0).jjtAccept(this, data);
        Boolean matchR = (Boolean) node.jjtGetChild(1).jjtAccept(this, data);
        Boolean intermediate = Undeterministic.and(matchL, matchR);
        cv.visitInsn(IAND);
        for (int i = 2; i < node.jjtGetNumChildren(); i++) {
            Boolean matchNext = (Boolean) node.jjtGetChild(i).jjtAccept(this, data);
            intermediate = Undeterministic.and(intermediate, matchNext);
            cv.visitInsn(IAND);
        }
        return intermediate;
    }

    public Object visit(ASTNot node, Object data) {
        Boolean match = (Boolean) node.jjtGetChild(0).jjtAccept(this, data);
        cv.visitInsn(INEG);
        return Undeterministic.not(match);
    }

    public Object visit(ASTTarget node, Object data) {
        Boolean match = (Boolean) super.visit(node, data);
        if (match != null) {
            push(match);
        } else {
            // runtime check
            String boundedTypeDesc = AsmHelper.convertReflectDescToTypeDesc(node.getBoundedType(m_expressionInfo));
            AbstractJoinPointCompiler.loadCallee(cv, m_input);
            cv.visitTypeInsn(INSTANCEOF, boundedTypeDesc.substring(1, boundedTypeDesc.length() - 1));
        }
        return match;
    }

    public Object visit(ASTThis node, Object data) {
        Boolean match = (Boolean) super.visit(node, data);
        push(match);
        return match;
    }

    public Object visit(ASTCflow node, Object data) {
        // runtime check
        String cflowClassName = CflowCompiler.getCflowAspectClassName(node.hashCode());
        cv.visitMethodInsn(INVOKESTATIC, cflowClassName, TransformationConstants.IS_IN_CFLOW_METOD_NAME,
                TransformationConstants.IS_IN_CFLOW_METOD_SIGNATURE);
        return (Boolean) super.visit(node, data);
    }

    public Object visit(ASTCflowBelow node, Object data) {
        // runtime check
        //TODO: cflowbelow ID will differ from cflow one.. => not optimized
        String cflowClassName = CflowCompiler.getCflowAspectClassName(node.hashCode());
        cv.visitMethodInsn(INVOKESTATIC, cflowClassName, TransformationConstants.IS_IN_CFLOW_METOD_NAME,
                TransformationConstants.IS_IN_CFLOW_METOD_SIGNATURE);
        return (Boolean) super.visit(node, data);
    }

    public Object visit(ASTArgs node, Object data) {
        Boolean match = (Boolean) super.visit(node, data);
        push(match);
        return match;
    }

    public Object visit(ASTPointcutReference node, Object data) {
        ExpressionContext context = (ExpressionContext) data;
        ExpressionNamespace namespace = ExpressionNamespace.getNamespace(m_namespace);
        ExpressionVisitor expression = namespace.getExpression(node.getName());

        // build a new RuntimeCheckVisitor to visit the sub expression
        RuntimeCheckVisitor referenced = new RuntimeCheckVisitor(cv, expression.getExpressionInfo(), m_input,
                m_perObjectCheckType, m_aspectQName);
        return referenced.matchUndeterministic(context);
    }

    public Object visit(ASTExecution node, Object data) {
        Boolean match = (Boolean) super.visit(node, data);
        push(match);
        return match;
    }

    public Object visit(ASTCall node, Object data) {
        Boolean match = (Boolean) super.visit(node, data);
        push(match);
        return match;
    }

    public Object visit(ASTSet node, Object data) {
        Boolean match = (Boolean) super.visit(node, data);
        push(match);
        return match;
    }

    public Object visit(ASTGet node, Object data) {
        Boolean match = (Boolean) super.visit(node, data);
        push(match);
        return match;
    }

    public Object visit(ASTHandler node, Object data) {
        Boolean match = (Boolean) super.visit(node, data);
        push(match);
        return match;
    }

    public Object visit(ASTStaticInitialization node, Object data) {
        Boolean match = (Boolean) super.visit(node, data);
        push(match);
        return match;
    }

    public Object visit(ASTWithin node, Object data) {
        Boolean match = (Boolean) super.visit(node, data);
        push(match);
        return match;
    }

    public Object visit(ASTWithinCode node, Object data) {
        Boolean match = (Boolean) super.visit(node, data);
        push(match);
        return match;
    }

    public Object visit(ASTHasMethod node, Object data) {
        Boolean match = (Boolean) super.visit(node, data);
        push(match);
        return match;
    }

    public Object visit(ASTHasField node, Object data) {
        Boolean match = (Boolean) super.visit(node, data);
        push(match);
        return match;
    }

    private void push(Boolean b) {
        if (b == null) {
            throw new Error("attempt to push an undetermined match result");
        } else if (b.booleanValue()) {
            cv.visitInsn(ICONST_1);
        } else {
            cv.visitInsn(ICONST_M1);
        }
    }
}