/* *******************************************************************
* Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* PARC initial implementation
* Alexandre Vasseur if() implementation for @AJ style
* ******************************************************************/
package org.aspectj.weaver.patterns;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.util.FuzzyBoolean;
import org.aspectj.weaver.Advice;
import org.aspectj.weaver.AjcMemberMaker;
import org.aspectj.weaver.BCException;
import org.aspectj.weaver.CompressingDataOutputStream;
import org.aspectj.weaver.ISourceContext;
import org.aspectj.weaver.IntMap;
import org.aspectj.weaver.ResolvedMember;
import org.aspectj.weaver.ResolvedMemberImpl;
import org.aspectj.weaver.ResolvedPointcutDefinition;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.Shadow;
import org.aspectj.weaver.ShadowMunger;
import org.aspectj.weaver.UnresolvedType;
import org.aspectj.weaver.VersionedDataInputStream;
import org.aspectj.weaver.WeaverMessages;
import org.aspectj.weaver.World;
import org.aspectj.weaver.ast.Expr;
import org.aspectj.weaver.ast.Literal;
import org.aspectj.weaver.ast.Test;
import org.aspectj.weaver.ast.Var;
public class IfPointcut extends Pointcut {
public ResolvedMember testMethod;
public int extraParameterFlags;
/**
* A token source dump that looks like a pointcut (but is NOT parseable at all - just here to help debugging etc)
*/
private final String enclosingPointcutHint;
public Pointcut residueSource;
int baseArgsCount;
// XXX some way to compute args
public IfPointcut(ResolvedMember testMethod, int extraParameterFlags) {
this.testMethod = testMethod;
this.extraParameterFlags = extraParameterFlags;
this.pointcutKind = IF;
this.enclosingPointcutHint = null;
}
/**
* No-arg constructor for @AJ style, where the if() body is actually the @Pointcut annotated method
*/
public IfPointcut(String enclosingPointcutHint) {
this.pointcutKind = IF;
this.enclosingPointcutHint = enclosingPointcutHint;
this.testMethod = null;// resolved during concretize
this.extraParameterFlags = -1;// allows to keep track of the @Aj style
}
@Override
public int couldMatchKinds() {
return Shadow.ALL_SHADOW_KINDS_BITS;
}
@Override
public FuzzyBoolean fastMatch(FastMatchInfo type) {
return FuzzyBoolean.MAYBE;
}
@Override
protected FuzzyBoolean matchInternal(Shadow shadow) {
if ((extraParameterFlags & Advice.ConstantReference) != 0) {
if ((extraParameterFlags & Advice.ConstantValue) != 0) {
return FuzzyBoolean.YES;
} else {
return FuzzyBoolean.NO;
}
}
// ??? this is not maximally efficient
return FuzzyBoolean.MAYBE;
}
public boolean alwaysFalse() {
return false;
}
public boolean alwaysTrue() {
return false;
}
// enh 76055
public Pointcut getResidueSource() {
return residueSource;
}
@Override
public void write(CompressingDataOutputStream s) throws IOException {
s.writeByte(Pointcut.IF);
s.writeBoolean(testMethod != null); // do we have a test method?
if (testMethod != null) {
testMethod.write(s);
}
s.writeByte(extraParameterFlags);
writeLocation(s);
}
public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException {
boolean hasTestMethod = s.readBoolean();
ResolvedMember resolvedTestMethod = null;
if (hasTestMethod) { // should always have a test method unless @AJ style
resolvedTestMethod = ResolvedMemberImpl.readResolvedMember(s, context);
}
IfPointcut ret = new IfPointcut(resolvedTestMethod, s.readByte());
ret.readLocation(context, s);
return ret;
}
@Override
public void resolveBindings(IScope scope, Bindings bindings) {
// ??? all we need is good error messages in here in cflow contexts
}
@Override
public boolean equals(Object other) {
if (!(other instanceof IfPointcut)) {
return false;
}
IfPointcut o = (IfPointcut) other;
if (o.testMethod == null) {
return (this.testMethod == null);
}
return o.testMethod.equals(this.testMethod);
}
@Override
public int hashCode() {
int result = 17;
result = 37 * result + testMethod.hashCode();
return result;
}
@Override
public String toString() {
if (extraParameterFlags < 0) {
// @AJ style
return "if()";
} else {
return "if(" + testMethod + ")";// FIXME AV - bad, this makes it unparsable. Perhaps we can use if() for code style
// behind the scene!
}
}
// ??? The implementation of name binding and type checking in if PCDs is very convoluted
// There has to be a better way...
private boolean findingResidue = false;
// Similar to lastMatchedShadowId - but only for if PCDs.
private int ifLastMatchedShadowId;
private Test ifLastMatchedShadowResidue;
/**
* At each shadow that matched, the residue can be different.
*/
@Override
protected Test findResidueInternal(Shadow shadow, ExposedState state) {
if (findingResidue) {
return Literal.TRUE;
}
findingResidue = true;
try {
// Have we already been asked this question?
if (shadow.shadowId == ifLastMatchedShadowId) {
return ifLastMatchedShadowResidue;
}
Test ret = Literal.TRUE;
List<Var> args = new ArrayList<Var>();
// code style
if (extraParameterFlags >= 0) {
if ((extraParameterFlags & Advice.ConstantReference) != 0) {
// it is either always true or always false, no need for test
if ((extraParameterFlags & Advice.ConstantValue) != 0) {
ret = Literal.TRUE;
ifLastMatchedShadowId = shadow.shadowId;
ifLastMatchedShadowResidue = ret;
return ret;
} else {
// Dont think we should be in here as the match cannot have succeeded...
ret = Literal.FALSE;
ifLastMatchedShadowId = shadow.shadowId;
ifLastMatchedShadowResidue = ret;
return ret;
}
}
// If there are no args to sort out, don't bother with the recursive call
if (baseArgsCount > 0) {
ExposedState myState = new ExposedState(baseArgsCount);
myState.setConcreteAspect(state.getConcreteAspect());
// ??? we throw out the test that comes from this walk. All we want here
// is bindings for the arguments
residueSource.findResidue(shadow, myState);
UnresolvedType[] pTypes = (testMethod == null ? null : testMethod.getParameterTypes());
if (pTypes != null && baseArgsCount > pTypes.length) { // pr155347
throw new BCException("Unexpected problem with testMethod " + testMethod + ": expecting " + baseArgsCount
+ " arguments");
}
// pr118149
// It is possible for vars in myState (which would normally be set
// in the call to residueSource.findResidue) to not be set (be null)
// in an Or pointcut with if expressions in both branches, and where
// one branch is known statically to not match. In this situation we
// simply return Test.
for (int i = 0; i < baseArgsCount; i++) {
Var v = myState.get(i);
if (v == null) {
continue; // pr118149
}
args.add(v);
ret = Test.makeAnd(ret, Test.makeInstanceof(v, pTypes[i].resolve(shadow.getIWorld())));
}
}
// handle thisJoinPoint parameters
if ((extraParameterFlags & Advice.ThisJoinPoint) != 0) {
args.add(shadow.getThisJoinPointVar());
}
if ((extraParameterFlags & Advice.ThisJoinPointStaticPart) != 0) {
args.add(shadow.getThisJoinPointStaticPartVar());
}
if ((extraParameterFlags & Advice.ThisEnclosingJoinPointStaticPart) != 0) {
args.add(shadow.getThisEnclosingJoinPointStaticPartVar());
}
if ((extraParameterFlags & Advice.ThisAspectInstance) != 0) {
args.add(shadow.getThisAspectInstanceVar(state.getConcreteAspect()));
}
} else {
// @style is slightly different
int currentStateIndex = 0;
// FIXME AV - "args(jp)" test(jp, thejp) will fail here
for (int i = 0; i < testMethod.getParameterTypes().length; i++) {
String argSignature = testMethod.getParameterTypes()[i].getSignature();
if (AjcMemberMaker.TYPEX_JOINPOINT.getSignature().equals(argSignature)) {
args.add(shadow.getThisJoinPointVar());
} else if (AjcMemberMaker.TYPEX_PROCEEDINGJOINPOINT.getSignature().equals(argSignature)) {
args.add(shadow.getThisJoinPointVar());
} else if (AjcMemberMaker.TYPEX_STATICJOINPOINT.getSignature().equals(argSignature)) {
args.add(shadow.getThisJoinPointStaticPartVar());
} else if (AjcMemberMaker.TYPEX_ENCLOSINGSTATICJOINPOINT.getSignature().equals(argSignature)) {
args.add(shadow.getThisEnclosingJoinPointStaticPartVar());
} else {
if (state.size() == 0 || currentStateIndex > state.size()) {
String[] paramNames = testMethod.getParameterNames();
StringBuffer errorParameter = new StringBuffer();
if (paramNames != null) {
errorParameter.append(testMethod.getParameterTypes()[i].getName()).append(" ");
errorParameter.append(paramNames[i]);
shadow.getIWorld()
.getMessageHandler()
.handleMessage(
MessageUtil.error("Missing binding for if() pointcut method. Parameter " + (i + 1)
+ "(" + errorParameter.toString()
+ ") must be bound - even in reference pointcuts (compiler limitation)",
testMethod.getSourceLocation()));
} else {
shadow.getIWorld()
.getMessageHandler()
.handleMessage(
MessageUtil.error("Missing binding for if() pointcut method. Parameter " + (i + 1)
+ " must be bound - even in reference pointcuts (compiler limitation)",
testMethod.getSourceLocation()));
}
return Literal.TRUE; // exit quickly
}
// we don't use i as JoinPoint.* can be anywhere in the signature in @style
Var v = state.get(currentStateIndex++);
while (v == null && currentStateIndex < state.size()) { // pr162135
v = state.get(currentStateIndex++);
}
args.add(v);
ret = Test.makeAnd(ret,
Test.makeInstanceof(v, testMethod.getParameterTypes()[i].resolve(shadow.getIWorld())));
}
}
}
ret = Test.makeAnd(ret, Test.makeCall(testMethod, (Expr[]) args.toArray(new Expr[args.size()])));
// Remember...
ifLastMatchedShadowId = shadow.shadowId;
ifLastMatchedShadowResidue = ret;
return ret;
} finally {
findingResidue = false;
}
}
// amc - the only reason this override seems to be here is to stop the copy, but
// that can be prevented by overriding shouldCopyLocationForConcretization,
// allowing me to make the method final in Pointcut.
// public Pointcut concretize(ResolvedType inAspect, IntMap bindings) {
// return this.concretize1(inAspect, bindings);
// }
@Override
protected boolean shouldCopyLocationForConcretize() {
return false;
}
private IfPointcut partiallyConcretized = null;
@Override
public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) {
// System.err.println("concretize: " + this + " already: " + partiallyConcretized);
if (isDeclare(bindings.getEnclosingAdvice())) {
// Enforce rule about which designators are supported in declare
inAspect.getWorld().showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.IF_IN_DECLARE),
bindings.getEnclosingAdvice().getSourceLocation(), null);
return Pointcut.makeMatchesNothing(Pointcut.CONCRETE);
}
if (partiallyConcretized != null) {
return partiallyConcretized;
}
final IfPointcut ret;
if (extraParameterFlags < 0 && testMethod == null) {
// @AJ style, we need to find the testMethod in the aspect defining the "if()" enclosing pointcut
ResolvedPointcutDefinition def = bindings.peekEnclosingDefinition();
if (def != null) {
ResolvedType aspect = inAspect.getWorld().resolve(def.getDeclaringType());
for (Iterator memberIter = aspect.getMethods(true, true); memberIter.hasNext();) {
ResolvedMember method = (ResolvedMember) memberIter.next();
if (def.getName().equals(method.getName())
&& def.getParameterTypes().length == method.getParameterTypes().length) {
boolean sameSig = true;
for (int j = 0; j < method.getParameterTypes().length; j++) {
UnresolvedType argJ = method.getParameterTypes()[j];
if (!argJ.equals(def.getParameterTypes()[j])) {
sameSig = false;
break;
}
}
if (sameSig) {
testMethod = method;
break;
}
}
}
if (testMethod == null) {
inAspect.getWorld().showMessage(IMessage.ERROR,
"Cannot find if() body from '" + def.toString() + "' for '" + enclosingPointcutHint + "'",
this.getSourceLocation(), null);
return Pointcut.makeMatchesNothing(Pointcut.CONCRETE);
}
} else {
testMethod = inAspect.getWorld().resolve(bindings.getAdviceSignature());
}
ret = new IfPointcut(enclosingPointcutHint);
ret.testMethod = testMethod;
} else {
ret = new IfPointcut(testMethod, extraParameterFlags);
}
ret.copyLocationFrom(this);
partiallyConcretized = ret;
// It is possible to directly code your pointcut expression in a per clause
// rather than defining a pointcut declaration and referencing it in your
// per clause. If you do this, we have problems (bug #62458). For now,
// let's police that you are trying to code a pointcut in a per clause and
// put out a compiler error.
if (bindings.directlyInAdvice() && bindings.getEnclosingAdvice() == null) {
// Assumption: if() is in a per clause if we say we are directly in advice
// but we have no enclosing advice.
inAspect.getWorld().showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.IF_IN_PERCLAUSE),
this.getSourceLocation(), null);
return Pointcut.makeMatchesNothing(Pointcut.CONCRETE);
}
if (bindings.directlyInAdvice()) {
ShadowMunger advice = bindings.getEnclosingAdvice();
if (advice instanceof Advice) {
ret.baseArgsCount = ((Advice) advice).getBaseParameterCount();
} else {
ret.baseArgsCount = 0;
}
ret.residueSource = advice.getPointcut().concretize(inAspect, inAspect, ret.baseArgsCount, advice);
} else {
ResolvedPointcutDefinition def = bindings.peekEnclosingDefinition();
if (def == CflowPointcut.CFLOW_MARKER) {
inAspect.getWorld().showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.IF_LEXICALLY_IN_CFLOW),
getSourceLocation(), null);
return Pointcut.makeMatchesNothing(Pointcut.CONCRETE);
}
ret.baseArgsCount = def.getParameterTypes().length;
// for @style, we have implicit binding for JoinPoint.* things
// FIXME AV - will lead to failure for "args(jp)" test(jp, thejp) / see args() implementation
if (ret.extraParameterFlags < 0) {
ret.baseArgsCount = 0;
for (int i = 0; i < testMethod.getParameterTypes().length; i++) {
String argSignature = testMethod.getParameterTypes()[i].getSignature();
if (AjcMemberMaker.TYPEX_JOINPOINT.getSignature().equals(argSignature)
|| AjcMemberMaker.TYPEX_PROCEEDINGJOINPOINT.getSignature().equals(argSignature)
|| AjcMemberMaker.TYPEX_STATICJOINPOINT.getSignature().equals(argSignature)
|| AjcMemberMaker.TYPEX_ENCLOSINGSTATICJOINPOINT.getSignature().equals(argSignature)) {
} else {
ret.baseArgsCount++;
}
}
}
IntMap newBindings = IntMap.idMap(ret.baseArgsCount);
newBindings.copyContext(bindings);
ret.residueSource = def.getPointcut().concretize(inAspect, declaringType, newBindings);
}
return ret;
}
// we can't touch "if" methods
@Override
public Pointcut parameterizeWith(Map typeVariableMap, World w) {
return this;
}
// public static Pointcut MatchesNothing = new MatchesNothingPointcut();
// ??? there could possibly be some good optimizations to be done at this point
public static IfPointcut makeIfFalsePointcut(State state) {
IfPointcut ret = new IfFalsePointcut();
ret.state = state;
return ret;
}
@Override
public Object accept(PatternNodeVisitor visitor, Object data) {
return visitor.visit(this, data);
}
public static class IfFalsePointcut extends IfPointcut {
public IfFalsePointcut() {
super(null, 0);
this.pointcutKind = Pointcut.IF_FALSE;
}
@Override
public int couldMatchKinds() {
return Shadow.NO_SHADOW_KINDS_BITS;
}
@Override
public boolean alwaysFalse() {
return true;
}
@Override
protected Test findResidueInternal(Shadow shadow, ExposedState state) {
return Literal.FALSE; // can only get here if an earlier error occurred
}
@Override
public FuzzyBoolean fastMatch(FastMatchInfo type) {
return FuzzyBoolean.NO;
}
@Override
protected FuzzyBoolean matchInternal(Shadow shadow) {
return FuzzyBoolean.NO;
}
@Override
public void resolveBindings(IScope scope, Bindings bindings) {
}
@Override
public void postRead(ResolvedType enclosingType) {
}
@Override
public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) {
if (isDeclare(bindings.getEnclosingAdvice())) {
// Enforce rule about which designators are supported in declare
inAspect.getWorld().showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.IF_IN_DECLARE),
bindings.getEnclosingAdvice().getSourceLocation(), null);
return Pointcut.makeMatchesNothing(Pointcut.CONCRETE);
}
return makeIfFalsePointcut(state);
}
@Override
public void write(CompressingDataOutputStream s) throws IOException {
s.writeByte(Pointcut.IF_FALSE);
}
@Override
public int hashCode() {
int result = 17;
return result;
}
@Override
public String toString() {
return "if(false)";
}
}
public static IfPointcut makeIfTruePointcut(State state) {
IfPointcut ret = new IfTruePointcut();
ret.state = state;
return ret;
}
public static class IfTruePointcut extends IfPointcut {
public IfTruePointcut() {
super(null, 0);
this.pointcutKind = Pointcut.IF_TRUE;
}
@Override
public boolean alwaysTrue() {
return true;
}
@Override
protected Test findResidueInternal(Shadow shadow, ExposedState state) {
return Literal.TRUE; // can only get here if an earlier error occurred
}
@Override
public FuzzyBoolean fastMatch(FastMatchInfo type) {
return FuzzyBoolean.YES;
}
@Override
protected FuzzyBoolean matchInternal(Shadow shadow) {
return FuzzyBoolean.YES;
}
@Override
public void resolveBindings(IScope scope, Bindings bindings) {
}
@Override
public void postRead(ResolvedType enclosingType) {
}
@Override
public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) {
if (isDeclare(bindings.getEnclosingAdvice())) {
// Enforce rule about which designators are supported in declare
inAspect.getWorld().showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.IF_IN_DECLARE),
bindings.getEnclosingAdvice().getSourceLocation(), null);
return Pointcut.makeMatchesNothing(Pointcut.CONCRETE);
}
return makeIfTruePointcut(state);
}
@Override
public void write(CompressingDataOutputStream s) throws IOException {
s.writeByte(IF_TRUE);
}
@Override
public int hashCode() {
int result = 37;
return result;
}
@Override
public String toString() {
return "if(true)";
}
}
/**
* Called when it is determined that the pointcut refers to a constant value of TRUE or FALSE - enabling exact matching and no
* unnecessary calls to the method representing the if body.
*/
public void setAlways(boolean matches) {
extraParameterFlags |= Advice.ConstantReference;
if (matches) {
extraParameterFlags |= Advice.ConstantValue;
}
}
}