/**************************************************************************************
* 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);
}
}
}