diff options
50 files changed, 1794 insertions, 156 deletions
diff --git a/tests/bugs/DisjunctVarBinding.java b/tests/bugs/DisjunctVarBinding.java index 6d59f354f..7c40cd070 100644 --- a/tests/bugs/DisjunctVarBinding.java +++ b/tests/bugs/DisjunctVarBinding.java @@ -14,7 +14,7 @@ class B extends A { aspect IfPointcut { after(A a, B b) returning: - call(* foo(*,*)) && + execution(* foo(*,*)) && (args(b,a) || args(a,b)) { System.out.println("Woven"); } diff --git a/tests/bugs/DisjunctVarBinding_3.java b/tests/bugs/DisjunctVarBinding_3.java index 3876b3521..95713d361 100644 --- a/tests/bugs/DisjunctVarBinding_3.java +++ b/tests/bugs/DisjunctVarBinding_3.java @@ -1,7 +1,7 @@ aspect IfPointcut { after(A a, B b) returning: - call(* foo(*,*)) && + execution(* foo(*,*)) && (args(b,a) || args(a,b)) { System.out.println("Woven"); } diff --git a/tests/src/org/aspectj/systemtest/ajc120/ajc120-tests.xml b/tests/src/org/aspectj/systemtest/ajc120/ajc120-tests.xml index 4ab8eb355..8381d4e01 100644 --- a/tests/src/org/aspectj/systemtest/ajc120/ajc120-tests.xml +++ b/tests/src/org/aspectj/systemtest/ajc120/ajc120-tests.xml @@ -454,8 +454,7 @@ <ajc-test dir="bugs" pr="61568" title="wrong variable binding in || pointcuts"> <compile files="DisjunctVarBinding.java"> - <message kind="error" line="34" text="Ambiguous binding of type B"/> - <message kind="error" line="34" text="Ambiguous binding of type A"/> + <message kind="error" line="17" text="ambiguous binding of parameter(s) a, b across '||' in pointcut"/> </compile> </ajc-test> diff --git a/tests/src/org/aspectj/systemtest/ajc121/ajc121-tests.xml b/tests/src/org/aspectj/systemtest/ajc121/ajc121-tests.xml index 77d52f3dc..9089289e9 100644 --- a/tests/src/org/aspectj/systemtest/ajc121/ajc121-tests.xml +++ b/tests/src/org/aspectj/systemtest/ajc121/ajc121-tests.xml @@ -4,12 +4,10 @@ <ajc-test dir="bugs" pr="62073" title="false ambiguous binding error (introduced in 1.2rc2)"> <compile files="DisjunctVarBinding_2.java,DisjunctVarBinding_3.java"> - <message kind="error" line="25" file="DisjunctVarBinding_2.java" text="Ambiguous binding of type B"/> - <message kind="error" line="25" file="DisjunctVarBinding_2.java" text="Ambiguous binding of type A"/> + <message kind="error" line="4" file="DisjunctVarBinding_3.java" text="ambiguous binding of parameter(s) a, b across '||' in pointcut"/> </compile> <compile files="DisjunctVarBinding_3.java,DisjunctVarBinding_2.java"> - <message kind="error" line="25" file="DisjunctVarBinding_2.java" text="Ambiguous binding of type B"/> - <message kind="error" line="25" file="DisjunctVarBinding_2.java" text="Ambiguous binding of type A"/> + <message kind="error" line="4" file="DisjunctVarBinding_3.java" text="ambiguous binding of parameter(s) a, b across '||' in pointcut"/> </compile> </ajc-test> @@ -213,14 +211,11 @@ <ajc-test dir="bugs/oxford" pr="65319" title="ajc crashes when compiling the following program (binding this() and target())"> <compile files="PR65319.java"> - <message kind="error" line="7" text="Cannot use target() to match at this"/> - <message kind="error" line="7" text="Cannot use this() to match at this"/> - <message kind="error" line="11" text="Cannot use target() to match at this"/> - <message kind="error" line="11" text="Cannot use this() to match at this"/> - <message kind="error" line="11" text="Ambiguous binding of type Test"/> - <message kind="error" line="15" text="Cannot use target() to match at this"/> - <message kind="error" line="15" text="Cannot use this() to match at this"/> - <message kind="error" line="15" text="Ambiguous binding of type Test"/> + <!-- target comes before this comes before args, and we only report one error --> + <message kind="error" line="23" text="ambiguous binding of parameter(s) x across '||' in pointcut"/> + <message kind="error" line="25" text="ambiguous binding of parameter(s) x across '||' in pointcut"/> + <message kind="error" line="27" text="ambiguous binding of parameter(s) x across '||' in pointcut"/> + <message kind="error" line="29" text="ambiguous binding of parameter(s) x across '||' in pointcut"/> </compile> </ajc-test> diff --git a/tests/src/org/aspectj/systemtest/ajc150/VarargsTests.java b/tests/src/org/aspectj/systemtest/ajc150/VarargsTests.java index 7845f6bd1..178e82b18 100644 --- a/tests/src/org/aspectj/systemtest/ajc150/VarargsTests.java +++ b/tests/src/org/aspectj/systemtest/ajc150/VarargsTests.java @@ -59,10 +59,12 @@ public class VarargsTests extends TestUtils { // In this test, it can be tricky to understand the results!! The reason being that the shadow // isn't included in the error message (it really should be, but thats a bit hard to do cleanly) public void test003_cantMatchVarargsWithObjectArray_withincodePCD() { - CompilationResult cR = binaryWeave("testcode.jar","VarargsAspect04.aj",0,7,true); + CompilationResult cR = binaryWeave("testcode.jar","VarargsAspect04.aj",0,6,true); // There are 7. Each piece of the pointcut is matched against all the shadows, so both // the 'withincode' PCD and the 'call' PCD are matched against every join point. + // AMC - there are now SIX. We detect early that a call(* *(..)) pcd cannot match + // constructor call shadows and never do the match. assertTrue("Did not get expected message about a varargs mismatch, instead got: "+cR.getWarningMessages(), ((IMessage)cR.getWarningMessages().get(0)).toString().indexOf("varargs declared method")!=-1); diff --git a/weaver/src/org/aspectj/weaver/Advice.java b/weaver/src/org/aspectj/weaver/Advice.java index 9a6e57c10..bafbb3ef5 100644 --- a/weaver/src/org/aspectj/weaver/Advice.java +++ b/weaver/src/org/aspectj/weaver/Advice.java @@ -185,6 +185,17 @@ public abstract class Advice extends ShadowMunger { return getSignature().getParameterTypes().length - getExtraParameterCount(); } + public String[] getBaseParameterNames(World world) { + String[] allNames = getSignature().getParameterNames(world); + int extras = getExtraParameterCount(); + if (extras == 0) return allNames; + String[] result = new String[getBaseParameterCount()]; + for (int i = 0; i < result.length; i++) { + result[i] = allNames[i]; + } + return result; + } + public TypeX getExtraParameterType() { if (!hasExtraParameter()) return ResolvedTypeX.MISSING; return signature.getParameterTypes()[getBaseParameterCount()]; @@ -215,7 +226,9 @@ public abstract class Advice extends ShadowMunger { // assert !fromType.isAbstract(); Pointcut p = pointcut.concretize(fromType, signature.getArity(), this); if (clause != null) { + Pointcut oldP = p; p = new AndPointcut(clause, p); + p.copyLocationFrom(oldP); p.state = Pointcut.CONCRETE; } diff --git a/weaver/src/org/aspectj/weaver/Shadow.java b/weaver/src/org/aspectj/weaver/Shadow.java index 85cd0ad87..c7c3309a6 100644 --- a/weaver/src/org/aspectj/weaver/Shadow.java +++ b/weaver/src/org/aspectj/weaver/Shadow.java @@ -16,8 +16,10 @@ package org.aspectj.weaver; import java.io.DataInputStream; import java.io.IOException; import java.util.ArrayList; +import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Set; import org.aspectj.asm.IRelationship; import org.aspectj.bridge.*; @@ -35,11 +37,16 @@ import org.aspectj.weaver.bcel.BcelAdvice; */ public abstract class Shadow { + + // every Shadow has a unique id, doesn't matter if it wraps... + private static int nextShadowID = 100; // easier to spot than zero. + private final Kind kind; private final Member signature; protected final Shadow enclosingShadow; protected List mungers = new ArrayList(1); + public int shadowId = nextShadowID++; // every time we build a shadow, it gets a new id // ---- protected Shadow(Kind kind, Member signature, Shadow enclosingShadow) { @@ -202,6 +209,12 @@ public abstract class Shadow { AdviceExecution, Initialization, ExceptionHandler, }; + public static final Set ALL_SHADOW_KINDS = new HashSet(); + static { + for (int i = 0; i < SHADOW_KINDS.length; i++) { + ALL_SHADOW_KINDS.add(SHADOW_KINDS[i]); + } + } /** A type-safe enum representing the kind of shadows */ diff --git a/weaver/src/org/aspectj/weaver/ShadowMunger.java b/weaver/src/org/aspectj/weaver/ShadowMunger.java index 36218cdb2..9924d2e7f 100644 --- a/weaver/src/org/aspectj/weaver/ShadowMunger.java +++ b/weaver/src/org/aspectj/weaver/ShadowMunger.java @@ -106,6 +106,11 @@ public abstract class ShadowMunger implements PartialOrder.PartialComparable, IH public Pointcut getPointcut() { return pointcut; } + + // pointcut may be updated during rewriting... + public void setPointcut(Pointcut pointcut) { + this.pointcut = pointcut; + } /** diff --git a/weaver/src/org/aspectj/weaver/WeaverMessages.java b/weaver/src/org/aspectj/weaver/WeaverMessages.java index 8aaa2e215..1fcde28ca 100644 --- a/weaver/src/org/aspectj/weaver/WeaverMessages.java +++ b/weaver/src/org/aspectj/weaver/WeaverMessages.java @@ -102,6 +102,11 @@ public class WeaverMessages { public static final String XLINT_KEY_ERROR = "invalidXLintKey"; public static final String XLINT_VALUE_ERROR = "invalidXLintMessageKind"; + public static final String UNBOUND_FORMAL = "unboundFormalInPC"; + public static final String AMBIGUOUS_BINDING = "ambiguousBindingInPC"; + public static final String AMBIGUOUS_BINDING_IN_OR = "ambiguousBindingInOrPC"; + public static final String NEGATION_DOESNT_ALLOW_BINDING = "negationDoesntAllowBinding"; + // Java5 messages public static final String ITDC_ON_ENUM_NOT_ALLOWED = "itdcOnEnumNotAllowed"; public static final String ITDM_ON_ENUM_NOT_ALLOWED = "itdmOnEnumNotAllowed"; diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelWeaver.java b/weaver/src/org/aspectj/weaver/bcel/BcelWeaver.java index 1f850999e..fe6392bcb 100644 --- a/weaver/src/org/aspectj/weaver/bcel/BcelWeaver.java +++ b/weaver/src/org/aspectj/weaver/bcel/BcelWeaver.java @@ -27,9 +27,11 @@ import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.jar.Attributes; import java.util.jar.JarEntry; @@ -48,6 +50,7 @@ import org.aspectj.bridge.Message; import org.aspectj.bridge.SourceLocation; import org.aspectj.util.FileUtil; import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.Advice; import org.aspectj.weaver.ConcreteTypeMunger; import org.aspectj.weaver.CrosscuttingMembersSet; import org.aspectj.weaver.IClassFileProvider; @@ -61,9 +64,19 @@ import org.aspectj.weaver.TypeX; import org.aspectj.weaver.WeaverMessages; import org.aspectj.weaver.WeaverMetrics; import org.aspectj.weaver.WeaverStateInfo; +import org.aspectj.weaver.patterns.AndPointcut; +import org.aspectj.weaver.patterns.BindingAnnotationTypePattern; +import org.aspectj.weaver.patterns.BindingTypePattern; import org.aspectj.weaver.patterns.CflowPointcut; +import org.aspectj.weaver.patterns.ConcreteCflowPointcut; import org.aspectj.weaver.patterns.DeclareParents; import org.aspectj.weaver.patterns.FastMatchInfo; +import org.aspectj.weaver.patterns.IfPointcut; +import org.aspectj.weaver.patterns.NameBindingPointcut; +import org.aspectj.weaver.patterns.NotPointcut; +import org.aspectj.weaver.patterns.OrPointcut; +import org.aspectj.weaver.patterns.Pointcut; +import org.aspectj.weaver.patterns.PointcutRewriter; public class BcelWeaver implements IWeaver { @@ -340,6 +353,7 @@ public class BcelWeaver implements IWeaver { } shadowMungerList = xcutSet.getShadowMungers(); + rewritePointcuts(shadowMungerList); typeMungerList = xcutSet.getTypeMungers(); declareParentsList = xcutSet.getDeclareParents(); @@ -353,6 +367,246 @@ public class BcelWeaver implements IWeaver { }); } + /* + * Rewrite all of the pointcuts in the world into their most efficient + * form for subsequent matching. Also ensure that if pc1.equals(pc2) + * then pc1 == pc2 (for non-binding pcds) by making references all + * point to the same instance. + * Since pointcuts remember their match decision on the last shadow, + * this makes matching faster when many pointcuts share common elements, + * or even when one single pointcut has one common element (which can + * be a side-effect of DNF rewriting). + */ + private void rewritePointcuts(List/*ShadowMunger*/ shadowMungers) { + PointcutRewriter rewriter = new PointcutRewriter(); + for (Iterator iter = shadowMungers.iterator(); iter.hasNext();) { + ShadowMunger munger = (ShadowMunger) iter.next(); + Pointcut p = munger.getPointcut(); + Pointcut newP = rewriter.rewrite(p); + // validateBindings now whilst we still have around the pointcut + // that resembles what the user actually wrote in their program + // text. + if (munger instanceof Advice) { + Advice advice = (Advice) munger; + if (advice.getSignature() != null) { + int numFormals = advice.getBaseParameterCount(); + if (numFormals > 0) { + String[] names = advice.getBaseParameterNames(world); + validateBindings(newP,p,numFormals,names); + } + } + } + munger.setPointcut(newP); + } + // now that we have optimized individual pointcuts, optimize + // across the set of pointcuts.... + // Use a map from key based on pc equality, to value based on + // pc identity. + Map/*<Pointcut,Pointcut>*/ pcMap = new HashMap(); + for (Iterator iter = shadowMungers.iterator(); iter.hasNext();) { + ShadowMunger munger = (ShadowMunger) iter.next(); + Pointcut p = munger.getPointcut(); + munger.setPointcut(shareEntriesFromMap(p,pcMap)); + } + } + + private Pointcut shareEntriesFromMap(Pointcut p,Map pcMap) { + // some things cant be shared... + if (p instanceof NameBindingPointcut) return p; + if (p instanceof IfPointcut) return p; + if (p instanceof ConcreteCflowPointcut) return p; + if (p instanceof AndPointcut) { + AndPointcut apc = (AndPointcut) p; + Pointcut left = shareEntriesFromMap(apc.getLeft(),pcMap); + Pointcut right = shareEntriesFromMap(apc.getRight(),pcMap); + return new AndPointcut(left,right); + } else if (p instanceof OrPointcut) { + OrPointcut opc = (OrPointcut) p; + Pointcut left = shareEntriesFromMap(opc.getLeft(),pcMap); + Pointcut right = shareEntriesFromMap(opc.getRight(),pcMap); + return new OrPointcut(left,right); + } else if (p instanceof NotPointcut) { + NotPointcut npc = (NotPointcut) p; + Pointcut not = shareEntriesFromMap(npc.getNegatedPointcut(),pcMap); + return new NotPointcut(not); + } else { + // primitive pcd + if (pcMap.containsKey(p)) { // based on equality + return (Pointcut) pcMap.get(p); // same instance (identity) + } else { + pcMap.put(p,p); + return p; + } + } + } + + // userPointcut is the pointcut that the user wrote in the program text. + // dnfPointcut is the same pointcut rewritten in DNF + // numFormals is the number of formal parameters in the pointcut + // if numFormals > 0 then every branch of a disjunction must bind each formal once and only once. + // in addition, the left and right branches of a disjunction must hold on join point kinds in + // common. + private void validateBindings(Pointcut dnfPointcut, Pointcut userPointcut, int numFormals, String[] names) { + if (numFormals == 0) return; // nothing to check + if (dnfPointcut.couldMatchKinds().isEmpty()) return; // cant have problems if you dont match! + if (dnfPointcut instanceof OrPointcut) { + OrPointcut orBasedDNFPointcut = (OrPointcut) dnfPointcut; + Pointcut[] leftBindings = new Pointcut[numFormals]; + Pointcut[] rightBindings = new Pointcut[numFormals]; + validateOrBranch(orBasedDNFPointcut,userPointcut,numFormals,names,leftBindings,rightBindings); + } else { + Pointcut[] bindings = new Pointcut[numFormals]; + validateSingleBranch(dnfPointcut, userPointcut, numFormals, names,bindings); + } + } + + private void validateOrBranch(OrPointcut pc, Pointcut userPointcut, int numFormals, + String[] names, Pointcut[] leftBindings, Pointcut[] rightBindings) { + Pointcut left = pc.getLeft(); + Pointcut right = pc.getRight(); + if (left instanceof OrPointcut) { + Pointcut[] newRightBindings = new Pointcut[numFormals]; + validateOrBranch((OrPointcut)left,userPointcut,numFormals,names,leftBindings,newRightBindings); + } else { + if (left.couldMatchKinds().size() > 0) + validateSingleBranch(left, userPointcut, numFormals, names, leftBindings); + } + if (right instanceof OrPointcut) { + Pointcut[] newLeftBindings = new Pointcut[numFormals]; + validateOrBranch((OrPointcut)right,userPointcut,numFormals,names,newLeftBindings,rightBindings); + } else { + if (right.couldMatchKinds().size() > 0) + validateSingleBranch(right, userPointcut, numFormals, names, rightBindings); + } + Set kindsInCommon = left.couldMatchKinds(); + kindsInCommon.retainAll(right.couldMatchKinds()); + if (!kindsInCommon.isEmpty()) { + // we know that every branch binds every formal, so there is no ambiguity + // if each branch binds it in exactly the same way... + List ambiguousNames = new ArrayList(); + for (int i = 0; i < numFormals; i++) { + if (!leftBindings[i].equals(rightBindings[i])) { + ambiguousNames.add(names[i]); + } + } + if (!ambiguousNames.isEmpty()) + raiseAmbiguityInDisjunctionError(userPointcut,ambiguousNames); + } + } + + // pc is a pointcut that does not contain any disjunctions + // check that every formal is bound (negation doesn't count). + // we know that numFormals > 0 or else we would not be called + private void validateSingleBranch(Pointcut pc, Pointcut userPointcut, int numFormals, String[] names, Pointcut[] bindings) { + boolean[] foundFormals = new boolean[numFormals]; + for (int i = 0; i < foundFormals.length; i++) { + foundFormals[i] = false; + } + validateSingleBranchRecursion(pc, userPointcut, foundFormals, names, bindings); + for (int i = 0; i < foundFormals.length; i++) { + if (!foundFormals[i]) { + raiseUnboundFormalError(names[i],userPointcut); + } + } + } + + // each formal must appear exactly once + private void validateSingleBranchRecursion(Pointcut pc, Pointcut userPointcut, boolean[] foundFormals, String[] names, Pointcut[] bindings) { + if (pc instanceof NotPointcut) { + // nots can only appear at leaves in DNF + NotPointcut not = (NotPointcut) pc; + if (not.getNegatedPointcut() instanceof NameBindingPointcut) { + NameBindingPointcut nnbp = (NameBindingPointcut) not.getNegatedPointcut(); + if (!nnbp.getBindingAnnotationTypePatterns().isEmpty() && !nnbp.getBindingTypePatterns().isEmpty()) + raiseNegationBindingError(userPointcut); + } + } else if (pc instanceof AndPointcut) { + AndPointcut and = (AndPointcut) pc; + validateSingleBranchRecursion(and.getLeft(), userPointcut,foundFormals,names,bindings); + validateSingleBranchRecursion(and.getRight(),userPointcut,foundFormals,names,bindings); + } else if (pc instanceof NameBindingPointcut) { + List/*BindingTypePattern*/ btps = ((NameBindingPointcut)pc).getBindingTypePatterns(); + for (Iterator iter = btps.iterator(); iter.hasNext();) { + BindingTypePattern btp = (BindingTypePattern) iter.next(); + int index = btp.getFormalIndex(); + bindings[index] = pc; + if (foundFormals[index]) { + raiseAmbiguousBindingError(names[index],userPointcut); + } else { + foundFormals[index] = true; + } + } + List/*BindingAnnotationTypePattern*/ baps = ((NameBindingPointcut)pc).getBindingAnnotationTypePatterns(); + for (Iterator iter = baps.iterator(); iter.hasNext();) { + BindingAnnotationTypePattern bap = (BindingAnnotationTypePattern) iter.next(); + int index = bap.getFormalIndex(); + bindings[index] = pc; + if (foundFormals[index]) { + raiseAmbiguousBindingError(names[index],userPointcut); + } else { + foundFormals[index] = true; + } + } + } else if (pc instanceof ConcreteCflowPointcut) { + ConcreteCflowPointcut cfp = (ConcreteCflowPointcut) pc; + int[] slots = cfp.getUsedFormalSlots(); + for (int i = 0; i < slots.length; i++) { + bindings[slots[i]] = cfp; + if (foundFormals[slots[i]]) { + raiseAmbiguousBindingError(names[slots[i]],userPointcut); + } else { + foundFormals[slots[i]] = true; + } + } + } + } + + /** + * @param userPointcut + */ + private void raiseNegationBindingError(Pointcut userPointcut) { + world.showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.NEGATION_DOESNT_ALLOW_BINDING), + userPointcut.getSourceContext().makeSourceLocation(userPointcut),null); + } + + /** + * @param string + * @param userPointcut + */ + private void raiseAmbiguousBindingError(String name, Pointcut userPointcut) { + world.showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.AMBIGUOUS_BINDING, + name), + userPointcut.getSourceContext().makeSourceLocation(userPointcut),null); + } + + /** + * @param userPointcut + */ + private void raiseAmbiguityInDisjunctionError(Pointcut userPointcut, List names) { + StringBuffer formalNames = new StringBuffer(names.get(0).toString()); + for (int i = 1; i < names.size(); i++) { + formalNames.append(", "); + formalNames.append(names.get(i)); + } + world.showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.AMBIGUOUS_BINDING_IN_OR,formalNames), + userPointcut.getSourceContext().makeSourceLocation(userPointcut),null); + } + + /** + * @param string + * @param userPointcut + */ + private void raiseUnboundFormalError(String name, Pointcut userPointcut) { + world.showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.UNBOUND_FORMAL, + name), + userPointcut.getSourceContext().makeSourceLocation(userPointcut),null); + } + + // public void dumpUnwoven(File file) throws IOException { // BufferedOutputStream os = FileUtil.makeOutputStream(file); // this.zipOutputStream = new ZipOutputStream(os); diff --git a/weaver/src/org/aspectj/weaver/patterns/AndPointcut.java b/weaver/src/org/aspectj/weaver/patterns/AndPointcut.java index 812c5e0d5..1775ba3db 100644 --- a/weaver/src/org/aspectj/weaver/patterns/AndPointcut.java +++ b/weaver/src/org/aspectj/weaver/patterns/AndPointcut.java @@ -17,6 +17,8 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.lang.reflect.Member; +import java.util.HashSet; +import java.util.Set; import org.aspectj.lang.JoinPoint; import org.aspectj.util.FuzzyBoolean; @@ -29,19 +31,28 @@ import org.aspectj.weaver.ast.Test; public class AndPointcut extends Pointcut { Pointcut left, right; // exposed for testing + private Set couldMatchKinds; + public AndPointcut(Pointcut left, Pointcut right) { super(); this.left = left; this.right = right; this.pointcutKind = AND; setLocation(left.getSourceContext(), left.getStart(), right.getEnd()); + couldMatchKinds = new HashSet(); + couldMatchKinds.addAll(left.couldMatchKinds()); + couldMatchKinds.retainAll(right.couldMatchKinds()); + } + + public Set couldMatchKinds() { + return couldMatchKinds; } public FuzzyBoolean fastMatch(FastMatchInfo type) { return left.fastMatch(type).and(right.fastMatch(type)); } - public FuzzyBoolean match(Shadow shadow) { + protected FuzzyBoolean matchInternal(Shadow shadow) { return left.match(shadow).and(right.match(shadow)); } @@ -114,13 +125,15 @@ public class AndPointcut extends Pointcut { } - public Test findResidue(Shadow shadow, ExposedState state) { + protected Test findResidueInternal(Shadow shadow, ExposedState state) { return Test.makeAnd(left.findResidue(shadow, state), right.findResidue(shadow, state)); } public Pointcut concretize1(ResolvedTypeX inAspect, IntMap bindings) { - return new AndPointcut(left.concretize(inAspect, bindings), - right.concretize(inAspect, bindings)); + AndPointcut ret = new AndPointcut(left.concretize(inAspect, bindings), + right.concretize(inAspect, bindings)); + ret.copyLocationFrom(this); + return ret; } public Pointcut getLeft() { diff --git a/weaver/src/org/aspectj/weaver/patterns/AndTypePattern.java b/weaver/src/org/aspectj/weaver/patterns/AndTypePattern.java index 15b705407..e3bb565b2 100644 --- a/weaver/src/org/aspectj/weaver/patterns/AndTypePattern.java +++ b/weaver/src/org/aspectj/weaver/patterns/AndTypePattern.java @@ -33,7 +33,7 @@ public class AndTypePattern extends TypePattern { private TypePattern left, right; public AndTypePattern(TypePattern left, TypePattern right) { - super(false,false); //??? we override all methods that care about includeSubtypes + super(false,false); //?? we override all methods that care about includeSubtypes this.left = left; this.right = right; setLocation(left.getSourceContext(), left.getStart(), right.getEnd()); @@ -114,4 +114,20 @@ public class AndTypePattern extends TypePattern { return buff.toString(); } + public boolean equals(Object obj) { + if (! (obj instanceof AndTypePattern)) return false; + AndTypePattern atp = (AndTypePattern) obj; + return left.equals(atp.left) && right.equals(atp.right); + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + int ret = 17; + ret = ret + 37 * left.hashCode(); + ret = ret + 37 * right.hashCode(); + return ret; + } + } diff --git a/weaver/src/org/aspectj/weaver/patterns/AnnotationPatternList.java b/weaver/src/org/aspectj/weaver/patterns/AnnotationPatternList.java index a5e8cc4cd..d586a824c 100644 --- a/weaver/src/org/aspectj/weaver/patterns/AnnotationPatternList.java +++ b/weaver/src/org/aspectj/weaver/patterns/AnnotationPatternList.java @@ -53,6 +53,10 @@ public class AnnotationPatternList extends PatternNode { this((AnnotationTypePattern[]) l.toArray(new AnnotationTypePattern[l.size()])); } + protected AnnotationTypePattern[] getAnnotationPatterns() { + return typePatterns; + } + public void resolve(World inWorld) { for (int i = 0; i < typePatterns.length; i++) { typePatterns[i].resolve(inWorld); diff --git a/weaver/src/org/aspectj/weaver/patterns/AnnotationPointcut.java b/weaver/src/org/aspectj/weaver/patterns/AnnotationPointcut.java index a2325e9d3..f156c7a7c 100644 --- a/weaver/src/org/aspectj/weaver/patterns/AnnotationPointcut.java +++ b/weaver/src/org/aspectj/weaver/patterns/AnnotationPointcut.java @@ -13,6 +13,10 @@ package org.aspectj.weaver.patterns; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; import org.aspectj.bridge.ISourceLocation; import org.aspectj.bridge.Message; @@ -67,6 +71,10 @@ public class AnnotationPointcut extends NameBindingPointcut { this.munger = munger; } + public Set couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS; + } + /* (non-Javadoc) * @see org.aspectj.weaver.patterns.Pointcut#fastMatch(org.aspectj.weaver.patterns.FastMatchInfo) */ @@ -81,7 +89,7 @@ public class AnnotationPointcut extends NameBindingPointcut { /* (non-Javadoc) * @see org.aspectj.weaver.patterns.Pointcut#match(org.aspectj.weaver.Shadow) */ - public FuzzyBoolean match(Shadow shadow) { + protected FuzzyBoolean matchInternal(Shadow shadow) { AnnotatedElement toMatchAgainst = null; Member member = shadow.getSignature(); ResolvedMember rMember = member.resolve(shadow.getIWorld()); @@ -137,7 +145,7 @@ public class AnnotationPointcut extends NameBindingPointcut { /* (non-Javadoc) * @see org.aspectj.weaver.patterns.Pointcut#findResidue(org.aspectj.weaver.Shadow, org.aspectj.weaver.patterns.ExposedState) */ - public Test findResidue(Shadow shadow, ExposedState state) { + protected Test findResidueInternal(Shadow shadow, ExposedState state) { if (annotationTypePattern instanceof BindingAnnotationTypePattern) { BindingAnnotationTypePattern btp = (BindingAnnotationTypePattern)annotationTypePattern; @@ -145,15 +153,15 @@ public class AnnotationPointcut extends NameBindingPointcut { Var var = shadow.getKindedAnnotationVar(annotationType); if (var == null) return Literal.FALSE; // Check if we have already bound something to this formal - if (state.get(btp.getFormalIndex())!=null) { - ISourceLocation pcdSloc = getSourceLocation(); - ISourceLocation shadowSloc = shadow.getSourceLocation(); - Message errorMessage = new Message( - "Cannot use @pointcut to match at this location and bind a formal to type '"+var.getType()+ - "' - the formal is already bound to type '"+state.get(btp.getFormalIndex()).getType()+"'"+ - ". The secondary source location points to the problematic binding.", - shadowSloc,true,new ISourceLocation[]{pcdSloc}); - shadow.getIWorld().getMessageHandler().handleMessage(errorMessage); + if ((state.get(btp.getFormalIndex())!=null) &&(lastMatchedShadowId == shadow.shadowId)) { +// ISourceLocation pcdSloc = getSourceLocation(); +// ISourceLocation shadowSloc = shadow.getSourceLocation(); +// Message errorMessage = new Message( +// "Cannot use @pointcut to match at this location and bind a formal to type '"+var.getType()+ +// "' - the formal is already bound to type '"+state.get(btp.getFormalIndex()).getType()+"'"+ +// ". The secondary source location points to the problematic binding.", +// shadowSloc,true,new ISourceLocation[]{pcdSloc}); +// shadow.getIWorld().getMessageHandler().handleMessage(errorMessage); state.setErroneousVar(btp.getFormalIndex()); } state.set(btp.getFormalIndex(),var); @@ -162,6 +170,24 @@ public class AnnotationPointcut extends NameBindingPointcut { } /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingAnnotationTypePatterns() + */ + public List getBindingAnnotationTypePatterns() { + if (annotationTypePattern instanceof BindingAnnotationTypePattern) { + List l = new ArrayList(); + l.add(annotationTypePattern); + return l; + } else return Collections.EMPTY_LIST; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingTypePatterns() + */ + public List getBindingTypePatterns() { + return Collections.EMPTY_LIST; + } + + /* (non-Javadoc) * @see org.aspectj.weaver.patterns.PatternNode#write(java.io.DataOutputStream) */ public void write(DataOutputStream s) throws IOException { diff --git a/weaver/src/org/aspectj/weaver/patterns/ArgsAnnotationPointcut.java b/weaver/src/org/aspectj/weaver/patterns/ArgsAnnotationPointcut.java index 363e27928..d78dd8751 100644 --- a/weaver/src/org/aspectj/weaver/patterns/ArgsAnnotationPointcut.java +++ b/weaver/src/org/aspectj/weaver/patterns/ArgsAnnotationPointcut.java @@ -12,6 +12,10 @@ package org.aspectj.weaver.patterns; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; import org.aspectj.bridge.IMessage; import org.aspectj.bridge.ISourceLocation; @@ -35,7 +39,7 @@ import org.aspectj.weaver.ast.Test; public class ArgsAnnotationPointcut extends NameBindingPointcut { private AnnotationPatternList arguments; - + /** * */ @@ -43,7 +47,11 @@ public class ArgsAnnotationPointcut extends NameBindingPointcut { super(); this.arguments = arguments; } - + + public Set couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS; // empty args() matches jps with no args + } + /* (non-Javadoc) * @see org.aspectj.weaver.patterns.Pointcut#fastMatch(org.aspectj.weaver.patterns.FastMatchInfo) */ @@ -54,7 +62,7 @@ public class ArgsAnnotationPointcut extends NameBindingPointcut { /* (non-Javadoc) * @see org.aspectj.weaver.patterns.Pointcut#match(org.aspectj.weaver.Shadow) */ - public FuzzyBoolean match(Shadow shadow) { + protected FuzzyBoolean matchInternal(Shadow shadow) { arguments.resolve(shadow.getIWorld()); FuzzyBoolean ret = arguments.matches(shadow.getIWorld().resolve(shadow.getArgTypes())); @@ -91,13 +99,15 @@ public class ArgsAnnotationPointcut extends NameBindingPointcut { return Pointcut.makeMatchesNothing(Pointcut.CONCRETE); } AnnotationPatternList list = arguments.resolveReferences(bindings); - return new ArgsAnnotationPointcut(list); + Pointcut ret = new ArgsAnnotationPointcut(list); + ret.copyLocationFrom(this); + return ret; } /* (non-Javadoc) * @see org.aspectj.weaver.patterns.Pointcut#findResidue(org.aspectj.weaver.Shadow, org.aspectj.weaver.patterns.ExposedState) */ - public Test findResidue(Shadow shadow, ExposedState state) { + protected Test findResidueInternal(Shadow shadow, ExposedState state) { int len = shadow.getArgCount(); // do some quick length tests first @@ -141,6 +151,28 @@ public class ArgsAnnotationPointcut extends NameBindingPointcut { return ret; } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingAnnotationTypePatterns() + */ + public List getBindingAnnotationTypePatterns() { + List l = new ArrayList(); + AnnotationTypePattern[] pats = arguments.getAnnotationPatterns(); + for (int i = 0; i < pats.length; i++) { + if (pats[i] instanceof BindingAnnotationTypePattern) { + l.add(pats[i]); + } + } + return l; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingTypePatterns() + */ + public List getBindingTypePatterns() { + return Collections.EMPTY_LIST; + } + /* (non-Javadoc) * @see org.aspectj.weaver.patterns.PatternNode#write(java.io.DataOutputStream) */ diff --git a/weaver/src/org/aspectj/weaver/patterns/ArgsPointcut.java b/weaver/src/org/aspectj/weaver/patterns/ArgsPointcut.java index 70f8535c7..aaa780cdd 100644 --- a/weaver/src/org/aspectj/weaver/patterns/ArgsPointcut.java +++ b/weaver/src/org/aspectj/weaver/patterns/ArgsPointcut.java @@ -20,8 +20,12 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Member; import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.Iterator; +import java.util.List; +import java.util.Set; import org.aspectj.bridge.IMessage; import org.aspectj.bridge.ISourceLocation; @@ -53,12 +57,16 @@ public class ArgsPointcut extends NameBindingPointcut { this.arguments = arguments; this.pointcutKind = ARGS; } - + + public Set couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS; // empty args() matches jps with no args + } + public FuzzyBoolean fastMatch(FastMatchInfo type) { return FuzzyBoolean.MAYBE; } - public FuzzyBoolean match(Shadow shadow) { + protected FuzzyBoolean matchInternal(Shadow shadow) { FuzzyBoolean ret = arguments.matches(shadow.getIWorld().resolve(shadow.getArgTypes()), TypePattern.DYNAMIC); return ret; @@ -135,6 +143,27 @@ public class ArgsPointcut extends NameBindingPointcut { return ret; } + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingAnnotationTypePatterns() + */ + public List getBindingAnnotationTypePatterns() { + return Collections.EMPTY_LIST; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingTypePatterns() + */ + public List getBindingTypePatterns() { + List l = new ArrayList(); + TypePattern[] pats = arguments.getTypePatterns(); + for (int i = 0; i < pats.length; i++) { + if (pats[i] instanceof BindingTypePattern) { + l.add(pats[i]); + } + } + return l; + } + public void write(DataOutputStream s) throws IOException { s.writeByte(Pointcut.ARGS); arguments.write(s); @@ -190,7 +219,9 @@ public class ArgsPointcut extends NameBindingPointcut { if (inAspect.crosscuttingMembers != null) { inAspect.crosscuttingMembers.exposeTypes(args.getExactTypes()); } - return new ArgsPointcut(args); + Pointcut ret = new ArgsPointcut(args); + ret.copyLocationFrom(this); + return ret; } private Test findResidueNoEllipsis(Shadow shadow, ExposedState state, TypePattern[] patterns) { @@ -218,14 +249,14 @@ public class ArgsPointcut extends NameBindingPointcut { } else { BindingTypePattern btp = (BindingTypePattern)type; // Check if we have already bound something to this formal - if (state.get(btp.getFormalIndex())!=null) { - ISourceLocation isl = getSourceLocation(); - Message errorMessage = new Message( - "Ambiguous binding of type "+type.getExactType().toString()+ - " using args(..) at this line - formal is already bound"+ - ". See secondary source location for location of args(..)", - shadow.getSourceLocation(),true,new ISourceLocation[]{getSourceLocation()}); - shadow.getIWorld().getMessageHandler().handleMessage(errorMessage); + if ((state.get(btp.getFormalIndex())!=null) &&(lastMatchedShadowId != shadow.shadowId)) { +// ISourceLocation isl = getSourceLocation(); +// Message errorMessage = new Message( +// "Ambiguous binding of type "+type.getExactType().toString()+ +// " using args(..) at this line - formal is already bound"+ +// ". See secondary source location for location of args(..)", +// shadow.getSourceLocation(),true,new ISourceLocation[]{getSourceLocation()}); +// shadow.getIWorld().getMessageHandler().handleMessage(errorMessage); state.setErroneousVar(btp.getFormalIndex()); } } @@ -236,7 +267,7 @@ public class ArgsPointcut extends NameBindingPointcut { return ret; } - public Test findResidue(Shadow shadow, ExposedState state) { + protected Test findResidueInternal(Shadow shadow, ExposedState state) { if (arguments.matches(shadow.getIWorld().resolve(shadow.getArgTypes()), TypePattern.DYNAMIC).alwaysFalse()) { return Literal.FALSE; } diff --git a/weaver/src/org/aspectj/weaver/patterns/BindingTypePattern.java b/weaver/src/org/aspectj/weaver/patterns/BindingTypePattern.java index e2633aa3f..b49b75f8b 100644 --- a/weaver/src/org/aspectj/weaver/patterns/BindingTypePattern.java +++ b/weaver/src/org/aspectj/weaver/patterns/BindingTypePattern.java @@ -40,6 +40,8 @@ public class BindingTypePattern extends ExactTypePattern implements BindingPatte public boolean equals(Object other) { if (!(other instanceof BindingTypePattern)) return false; BindingTypePattern o = (BindingTypePattern)other; + if (includeSubtypes != o.includeSubtypes) return false; + if (isVarArgs != o.isVarArgs) return false; return o.type.equals(this.type) && o.formalIndex == this.formalIndex; } public int hashCode() { diff --git a/weaver/src/org/aspectj/weaver/patterns/CflowPointcut.java b/weaver/src/org/aspectj/weaver/patterns/CflowPointcut.java index 5a94ec668..7203339f8 100644 --- a/weaver/src/org/aspectj/weaver/patterns/CflowPointcut.java +++ b/weaver/src/org/aspectj/weaver/patterns/CflowPointcut.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Hashtable; import java.util.List; +import java.util.Set; import org.aspectj.bridge.IMessage; import org.aspectj.util.FileUtil; @@ -66,12 +67,16 @@ public class CflowPointcut extends Pointcut { this.freeVars = freeVars; this.pointcutKind = CFLOW; } - + + public Set couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS; + } + public FuzzyBoolean fastMatch(FastMatchInfo type) { return FuzzyBoolean.MAYBE; } - public FuzzyBoolean match(Shadow shadow) { + protected FuzzyBoolean matchInternal(Shadow shadow) { //??? this is not maximally efficient return FuzzyBoolean.MAYBE; } @@ -147,7 +152,7 @@ public class CflowPointcut extends Pointcut { return "cflow" + (isBelow ? "below" : "") + "(" + entry + ")"; } - public Test findResidue(Shadow shadow, ExposedState state) { + protected Test findResidueInternal(Shadow shadow, ExposedState state) { throw new RuntimeException("unimplemented"); } @@ -227,7 +232,9 @@ public class CflowPointcut extends Pointcut { putCflowfield(concreteEntry,localCflowField); // Remember it } - return new ConcreteCflowPointcut(localCflowField, null,true); + Pointcut ret = new ConcreteCflowPointcut(localCflowField, null,true); + ret.copyLocationFrom(this); + return ret; } else { List slots = new ArrayList(); @@ -268,7 +275,9 @@ public class CflowPointcut extends Pointcut { world.makeCflowStackFieldAdder(localCflowField)); putCflowfield(concreteEntry,localCflowField); } - return new ConcreteCflowPointcut(localCflowField, slots,false); + Pointcut ret = new ConcreteCflowPointcut(localCflowField, slots,false); + ret.copyLocationFrom(this); + return ret; } } diff --git a/weaver/src/org/aspectj/weaver/patterns/ConcreteCflowPointcut.java b/weaver/src/org/aspectj/weaver/patterns/ConcreteCflowPointcut.java index a536dd4fa..9c1309c74 100644 --- a/weaver/src/org/aspectj/weaver/patterns/ConcreteCflowPointcut.java +++ b/weaver/src/org/aspectj/weaver/patterns/ConcreteCflowPointcut.java @@ -17,6 +17,7 @@ import java.io.DataOutputStream; import java.io.IOException; import java.util.Iterator; import java.util.List; +import java.util.Set; import org.aspectj.util.FuzzyBoolean; import org.aspectj.weaver.IntMap; @@ -43,11 +44,15 @@ public class ConcreteCflowPointcut extends Pointcut { this.pointcutKind = CFLOW; } + public Set couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS; + } + public FuzzyBoolean fastMatch(FastMatchInfo type) { return FuzzyBoolean.MAYBE; } - public FuzzyBoolean match(Shadow shadow) { + protected FuzzyBoolean matchInternal(Shadow shadow) { //??? this is not maximally efficient return FuzzyBoolean.MAYBE; } @@ -70,6 +75,16 @@ public class ConcreteCflowPointcut extends Pointcut { throw new UnsupportedOperationException("cflow pointcut matching not supported by this operation"); } + // used by weaver when validating bindings + public int[] getUsedFormalSlots() { + if (slots == null) return new int[0]; + int[] indices = new int[slots.size()]; + for (int i = 0; i < indices.length; i++) { + indices[i] = ((Slot)slots.get(i)).formalIndex; + } + return indices; + } + public void write(DataOutputStream s) throws IOException { throw new RuntimeException("unimplemented"); } @@ -97,7 +112,7 @@ public class ConcreteCflowPointcut extends Pointcut { return "concretecflow(" + cflowField + ")"; } - public Test findResidue(Shadow shadow, ExposedState state) { + protected Test findResidueInternal(Shadow shadow, ExposedState state) { //System.out.println("find residue: " + this); if (usesCounter) { return Test.makeFieldGetCall(cflowField, cflowCounterIsValidMethod, Expr.NONE); diff --git a/weaver/src/org/aspectj/weaver/patterns/ExactTypePattern.java b/weaver/src/org/aspectj/weaver/patterns/ExactTypePattern.java index 945b76ba7..4b2ca0c36 100644 --- a/weaver/src/org/aspectj/weaver/patterns/ExactTypePattern.java +++ b/weaver/src/org/aspectj/weaver/patterns/ExactTypePattern.java @@ -151,6 +151,8 @@ public class ExactTypePattern extends TypePattern { public boolean equals(Object other) { if (!(other instanceof ExactTypePattern)) return false; ExactTypePattern o = (ExactTypePattern)other; + if (includeSubtypes != o.includeSubtypes) return false; + if (isVarArgs != o.isVarArgs) return false; return (o.type.equals(this.type) && o.annotationPattern.equals(this.annotationPattern)); } diff --git a/weaver/src/org/aspectj/weaver/patterns/HandlerPointcut.java b/weaver/src/org/aspectj/weaver/patterns/HandlerPointcut.java index ce6163055..f27f1f81d 100644 --- a/weaver/src/org/aspectj/weaver/patterns/HandlerPointcut.java +++ b/weaver/src/org/aspectj/weaver/patterns/HandlerPointcut.java @@ -17,6 +17,8 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.lang.reflect.Member; +import java.util.HashSet; +import java.util.Set; import org.aspectj.lang.JoinPoint; import org.aspectj.util.FuzzyBoolean; @@ -37,17 +39,26 @@ import org.aspectj.weaver.internal.tools.PointcutExpressionImpl; public class HandlerPointcut extends Pointcut { TypePattern exceptionType; + private static final Set MATCH_KINDS = new HashSet(); + static { + MATCH_KINDS.add(Shadow.ExceptionHandler); + } + public HandlerPointcut(TypePattern exceptionType) { this.exceptionType = exceptionType; this.pointcutKind = HANDLER; } + public Set couldMatchKinds() { + return MATCH_KINDS; + } + public FuzzyBoolean fastMatch(FastMatchInfo type) { //??? should be able to do better by finding all referenced types in type return FuzzyBoolean.MAYBE; } - public FuzzyBoolean match(Shadow shadow) { + protected FuzzyBoolean matchInternal(Shadow shadow) { if (shadow.getKind() != Shadow.ExceptionHandler) return FuzzyBoolean.NO; // we know we have exactly one parameter since we're checking an exception handler @@ -134,11 +145,13 @@ public class HandlerPointcut extends Pointcut { exceptionType = exceptionType.resolveBindingsFromRTTI(false,false); } - public Test findResidue(Shadow shadow, ExposedState state) { + protected Test findResidueInternal(Shadow shadow, ExposedState state) { return match(shadow).alwaysTrue() ? Literal.TRUE : Literal.FALSE; } public Pointcut concretize1(ResolvedTypeX inAspect, IntMap bindings) { - return new HandlerPointcut(exceptionType); + Pointcut ret = new HandlerPointcut(exceptionType); + ret.copyLocationFrom(this); + return ret; } } diff --git a/weaver/src/org/aspectj/weaver/patterns/IfPointcut.java b/weaver/src/org/aspectj/weaver/patterns/IfPointcut.java index 1336adb2f..612d18a01 100644 --- a/weaver/src/org/aspectj/weaver/patterns/IfPointcut.java +++ b/weaver/src/org/aspectj/weaver/patterns/IfPointcut.java @@ -18,7 +18,9 @@ import java.io.DataOutputStream; import java.io.IOException; import java.lang.reflect.Member; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Set; import org.aspectj.bridge.IMessage; import org.aspectj.lang.JoinPoint; @@ -54,15 +56,27 @@ public class IfPointcut extends Pointcut { this.pointcutKind = IF; } - public FuzzyBoolean fastMatch(FastMatchInfo type) { + public Set couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS; + } + + public FuzzyBoolean fastMatch(FastMatchInfo type) { return FuzzyBoolean.MAYBE; } - public FuzzyBoolean match(Shadow shadow) { + protected FuzzyBoolean matchInternal(Shadow shadow) { //??? this is not maximally efficient return FuzzyBoolean.MAYBE; } + public boolean alwaysFalse() { + return false; + } + + public boolean alwaysTrue() { + return false; + } + /* (non-Javadoc) * @see org.aspectj.weaver.patterns.Pointcut#matchesDynamically(java.lang.Object, java.lang.Object, java.lang.Object[]) */ @@ -116,7 +130,7 @@ public class IfPointcut extends Pointcut { //??? 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; - public Test findResidue(Shadow shadow, ExposedState state) { + protected Test findResidueInternal(Shadow shadow, ExposedState state) { if (findingResidue) return Literal.TRUE; findingResidue = true; try { @@ -161,9 +175,16 @@ public class IfPointcut extends Pointcut { } } + + // 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(ResolvedTypeX inAspect, IntMap bindings) { +// return this.concretize1(inAspect, bindings); +// } - public Pointcut concretize(ResolvedTypeX inAspect, IntMap bindings) { - return this.concretize1(inAspect, bindings); + protected boolean shouldCopyLocationForConcretize() { + return false; } private IfPointcut partiallyConcretized = null; @@ -183,6 +204,7 @@ public class IfPointcut extends Pointcut { return partiallyConcretized; } IfPointcut ret = new IfPointcut(testMethod, extraParameterFlags); + ret.copyLocationFrom(this); partiallyConcretized = ret; // It is possible to directly code your pointcut expression in a per clause @@ -239,7 +261,15 @@ public class IfPointcut extends Pointcut { super(null,0); } - public Test findResidue(Shadow shadow, ExposedState state) { + public Set couldMatchKinds() { + return Collections.EMPTY_SET; + } + + public boolean alwaysFalse() { + return true; + } + + protected Test findResidueInternal(Shadow shadow, ExposedState state) { return Literal.FALSE; // can only get here if an earlier error occurred } @@ -247,7 +277,7 @@ public class IfPointcut extends Pointcut { return FuzzyBoolean.NO; } - public FuzzyBoolean match(Shadow shadow) { + protected FuzzyBoolean matchInternal(Shadow shadow) { return FuzzyBoolean.NO; } @@ -304,8 +334,12 @@ public class IfPointcut extends Pointcut { public IfTruePointcut() { super(null,0); } + + public boolean alwaysTrue() { + return true; + } - public Test findResidue(Shadow shadow, ExposedState state) { + protected Test findResidueInternal(Shadow shadow, ExposedState state) { return Literal.TRUE; // can only get here if an earlier error occurred } @@ -313,7 +347,7 @@ public class IfPointcut extends Pointcut { return FuzzyBoolean.YES; } - public FuzzyBoolean match(Shadow shadow) { + protected FuzzyBoolean matchInternal(Shadow shadow) { return FuzzyBoolean.YES; } diff --git a/weaver/src/org/aspectj/weaver/patterns/KindedPointcut.java b/weaver/src/org/aspectj/weaver/patterns/KindedPointcut.java index 1e34b1404..8886e3209 100644 --- a/weaver/src/org/aspectj/weaver/patterns/KindedPointcut.java +++ b/weaver/src/org/aspectj/weaver/patterns/KindedPointcut.java @@ -16,6 +16,8 @@ package org.aspectj.weaver.patterns; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.util.HashSet; +import java.util.Set; import org.aspectj.bridge.ISourceLocation; import org.aspectj.lang.JoinPoint; @@ -36,6 +38,7 @@ import org.aspectj.weaver.ast.Test; public class KindedPointcut extends Pointcut { Shadow.Kind kind; SignaturePattern signature; + private Set matchKinds; private ShadowMunger munger = null; // only set after concretization @@ -45,6 +48,8 @@ public class KindedPointcut extends Pointcut { this.kind = kind; this.signature = signature; this.pointcutKind = KINDED; + this.matchKinds = new HashSet(); + matchKinds.add(kind); } public KindedPointcut( Shadow.Kind kind, @@ -54,6 +59,13 @@ public class KindedPointcut extends Pointcut { this(kind, signature); this.munger = munger; } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#couldMatchKinds() + */ + public Set couldMatchKinds() { + return matchKinds; + } public FuzzyBoolean fastMatch(FastMatchInfo info) { if (info.getKind() != null) { @@ -63,7 +75,7 @@ public class KindedPointcut extends Pointcut { return FuzzyBoolean.MAYBE; } - public FuzzyBoolean match(Shadow shadow) { + protected FuzzyBoolean matchInternal(Shadow shadow) { if (shadow.getKind() != kind) return FuzzyBoolean.NO; if (!signature.matches(shadow.getSignature(), shadow.getIWorld())){ @@ -265,7 +277,7 @@ public class KindedPointcut extends Pointcut { signature = signature.resolveBindingsFromRTTI(); } - public Test findResidue(Shadow shadow, ExposedState state) { + protected Test findResidueInternal(Shadow shadow, ExposedState state) { return match(shadow).alwaysTrue() ? Literal.TRUE : Literal.FALSE; } diff --git a/weaver/src/org/aspectj/weaver/patterns/NameBindingPointcut.java b/weaver/src/org/aspectj/weaver/patterns/NameBindingPointcut.java index 6fa9af440..b6be31406 100644 --- a/weaver/src/org/aspectj/weaver/patterns/NameBindingPointcut.java +++ b/weaver/src/org/aspectj/weaver/patterns/NameBindingPointcut.java @@ -13,6 +13,8 @@ package org.aspectj.weaver.patterns; +import java.util.List; + import org.aspectj.weaver.TypeX; import org.aspectj.weaver.World; import org.aspectj.weaver.ast.Test; @@ -40,7 +42,8 @@ public abstract class NameBindingPointcut extends Pointcut { return Test.makeInstanceof(var, myType.resolve(world)); } - + public abstract List/*<BindingTypePattern>*/ getBindingTypePatterns(); + public abstract List/*<BindingAnnotationTypePattern>*/ getBindingAnnotationTypePatterns(); } diff --git a/weaver/src/org/aspectj/weaver/patterns/NotPointcut.java b/weaver/src/org/aspectj/weaver/patterns/NotPointcut.java index 688f2e9c0..ea2587ed5 100644 --- a/weaver/src/org/aspectj/weaver/patterns/NotPointcut.java +++ b/weaver/src/org/aspectj/weaver/patterns/NotPointcut.java @@ -17,6 +17,7 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.lang.reflect.Member; +import java.util.Set; import org.aspectj.lang.JoinPoint; import org.aspectj.util.FuzzyBoolean; @@ -39,13 +40,17 @@ public class NotPointcut extends Pointcut { setLocation(pointcut.getSourceContext(), startPos, pointcut.getEnd()); } + public Set couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS; + } + public Pointcut getNegatedPointcut() { return body; } public FuzzyBoolean fastMatch(FastMatchInfo type) { return body.fastMatch(type).not(); } - public FuzzyBoolean match(Shadow shadow) { + protected FuzzyBoolean matchInternal(Shadow shadow) { return body.match(shadow).not(); } @@ -118,12 +123,14 @@ public class NotPointcut extends Pointcut { return ret; } - public Test findResidue(Shadow shadow, ExposedState state) { + protected Test findResidueInternal(Shadow shadow, ExposedState state) { return Test.makeNot(body.findResidue(shadow, state)); } public Pointcut concretize1(ResolvedTypeX inAspect, IntMap bindings) { - return new NotPointcut(body.concretize(inAspect, bindings)); + Pointcut ret = new NotPointcut(body.concretize(inAspect, bindings)); + ret.copyLocationFrom(this); + return ret; } } diff --git a/weaver/src/org/aspectj/weaver/patterns/NotTypePattern.java b/weaver/src/org/aspectj/weaver/patterns/NotTypePattern.java index cbe48e03b..8a0d2c619 100644 --- a/weaver/src/org/aspectj/weaver/patterns/NotTypePattern.java +++ b/weaver/src/org/aspectj/weaver/patterns/NotTypePattern.java @@ -105,4 +105,19 @@ public class NotTypePattern extends TypePattern { } return buff.toString(); } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + if (! (obj instanceof NotTypePattern)) return false; + return (pattern.equals(((NotTypePattern)obj).pattern)); + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return 17 + 37 * pattern.hashCode(); + } } diff --git a/weaver/src/org/aspectj/weaver/patterns/OrPointcut.java b/weaver/src/org/aspectj/weaver/patterns/OrPointcut.java index fa9efda07..e204ef329 100644 --- a/weaver/src/org/aspectj/weaver/patterns/OrPointcut.java +++ b/weaver/src/org/aspectj/weaver/patterns/OrPointcut.java @@ -17,6 +17,8 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.lang.reflect.Member; +import java.util.HashSet; +import java.util.Set; import org.aspectj.lang.JoinPoint; import org.aspectj.util.FuzzyBoolean; @@ -28,6 +30,7 @@ import org.aspectj.weaver.ast.Test; public class OrPointcut extends Pointcut { private Pointcut left, right; + private Set couldMatchKinds; public OrPointcut(Pointcut left, Pointcut right) { super(); @@ -35,14 +38,19 @@ public class OrPointcut extends Pointcut { this.right = right; setLocation(left.getSourceContext(), left.getStart(), right.getEnd()); this.pointcutKind = OR; + this.couldMatchKinds = new HashSet(left.couldMatchKinds()); + this.couldMatchKinds.addAll(right.couldMatchKinds()); } + public Set couldMatchKinds() { + return couldMatchKinds; + } public FuzzyBoolean fastMatch(FastMatchInfo type) { return left.fastMatch(type).or(right.fastMatch(type)); } - public FuzzyBoolean match(Shadow shadow) { + protected FuzzyBoolean matchInternal(Shadow shadow) { return left.match(shadow).or(right.match(shadow)); } @@ -119,13 +127,15 @@ public class OrPointcut extends Pointcut { return ret; } - public Test findResidue(Shadow shadow, ExposedState state) { + protected Test findResidueInternal(Shadow shadow, ExposedState state) { return Test.makeOr(left.findResidue(shadow, state), right.findResidue(shadow, state)); } public Pointcut concretize1(ResolvedTypeX inAspect, IntMap bindings) { - return new OrPointcut(left.concretize(inAspect, bindings), + Pointcut ret = new OrPointcut(left.concretize(inAspect, bindings), right.concretize(inAspect, bindings)); + ret.copyLocationFrom(this); + return ret; } public Pointcut getLeft() { diff --git a/weaver/src/org/aspectj/weaver/patterns/OrTypePattern.java b/weaver/src/org/aspectj/weaver/patterns/OrTypePattern.java index 37fa7767a..d071c942d 100644 --- a/weaver/src/org/aspectj/weaver/patterns/OrTypePattern.java +++ b/weaver/src/org/aspectj/weaver/patterns/OrTypePattern.java @@ -113,4 +113,23 @@ public class OrTypePattern extends TypePattern { } return buff.toString(); } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + if (! (obj instanceof OrTypePattern)) return false; + OrTypePattern other = (OrTypePattern) obj; + return left.equals(other.left) && right.equals(other.right); + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + int ret = 17; + ret = ret + 37 * left.hashCode(); + ret = ret + 37 * right.hashCode(); + return ret; + } } diff --git a/weaver/src/org/aspectj/weaver/patterns/PerCflow.java b/weaver/src/org/aspectj/weaver/patterns/PerCflow.java index f0626758f..d03daa5eb 100644 --- a/weaver/src/org/aspectj/weaver/patterns/PerCflow.java +++ b/weaver/src/org/aspectj/weaver/patterns/PerCflow.java @@ -20,6 +20,7 @@ import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Set; import org.aspectj.util.FuzzyBoolean; import org.aspectj.weaver.Advice; @@ -47,11 +48,15 @@ public class PerCflow extends PerClause { // ----- + public Set couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS; + } + public FuzzyBoolean fastMatch(FastMatchInfo type) { return FuzzyBoolean.MAYBE; } - public FuzzyBoolean match(Shadow shadow) { + protected FuzzyBoolean matchInternal(Shadow shadow) { return FuzzyBoolean.YES; } @@ -60,7 +65,7 @@ public class PerCflow extends PerClause { entry.resolve(scope); } - public Test findResidue(Shadow shadow, ExposedState state) { + protected Test findResidueInternal(Shadow shadow, ExposedState state) { Expr myInstance = Expr.makeCallExpr(AjcMemberMaker.perCflowAspectOfMethod(inAspect), Expr.NONE, inAspect); diff --git a/weaver/src/org/aspectj/weaver/patterns/PerFromSuper.java b/weaver/src/org/aspectj/weaver/patterns/PerFromSuper.java index 5b6acce03..c7274ea81 100644 --- a/weaver/src/org/aspectj/weaver/patterns/PerFromSuper.java +++ b/weaver/src/org/aspectj/weaver/patterns/PerFromSuper.java @@ -16,6 +16,7 @@ package org.aspectj.weaver.patterns; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.util.Set; import org.aspectj.bridge.MessageUtil; import org.aspectj.util.FuzzyBoolean; @@ -31,12 +32,16 @@ public class PerFromSuper extends PerClause { public PerFromSuper(PerClause.Kind kind) { this.kind = kind; } - + + public Set couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS; + } + public FuzzyBoolean fastMatch(FastMatchInfo type) { throw new RuntimeException("unimplemented"); } - public FuzzyBoolean match(Shadow shadow) { + protected FuzzyBoolean matchInternal(Shadow shadow) { throw new RuntimeException("unimplemented"); } @@ -44,7 +49,7 @@ public class PerFromSuper extends PerClause { // this method intentionally left blank } - public Test findResidue(Shadow shadow, ExposedState state) { + protected Test findResidueInternal(Shadow shadow, ExposedState state) { throw new RuntimeException("unimplemented"); } diff --git a/weaver/src/org/aspectj/weaver/patterns/PerObject.java b/weaver/src/org/aspectj/weaver/patterns/PerObject.java index bed5d4e81..ef74a8d13 100644 --- a/weaver/src/org/aspectj/weaver/patterns/PerObject.java +++ b/weaver/src/org/aspectj/weaver/patterns/PerObject.java @@ -16,6 +16,9 @@ package org.aspectj.weaver.patterns; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; import org.aspectj.util.FuzzyBoolean; import org.aspectj.weaver.Advice; @@ -33,11 +36,24 @@ import org.aspectj.weaver.ast.Var; public class PerObject extends PerClause { private boolean isThis; private Pointcut entry; + private static final Set thisKindSet = new HashSet(Shadow.ALL_SHADOW_KINDS); + private static final Set targetKindSet = new HashSet(Shadow.ALL_SHADOW_KINDS); + static { + for (Iterator iter = Shadow.ALL_SHADOW_KINDS.iterator(); iter.hasNext();) { + Shadow.Kind kind = (Shadow.Kind) iter.next(); + if (kind.neverHasThis()) thisKindSet.remove(kind); + if (kind.neverHasTarget()) targetKindSet.remove(kind); + } + } public PerObject(Pointcut entry, boolean isThis) { this.entry = entry; this.isThis = isThis; } + + public Set couldMatchKinds() { + return isThis ? thisKindSet : targetKindSet; + } // ----- public FuzzyBoolean fastMatch(FastMatchInfo type) { @@ -45,7 +61,7 @@ public class PerObject extends PerClause { } - public FuzzyBoolean match(Shadow shadow) { + protected FuzzyBoolean matchInternal(Shadow shadow) { //System.err.println("matches " + this + " ? " + shadow + ", " + shadow.hasTarget()); //??? could probably optimize this better by testing could match if (isThis) return FuzzyBoolean.fromBoolean(shadow.hasThis()); @@ -61,7 +77,7 @@ public class PerObject extends PerClause { return isThis ? shadow.getThisVar() : shadow.getTargetVar(); } - public Test findResidue(Shadow shadow, ExposedState state) { + protected Test findResidueInternal(Shadow shadow, ExposedState state) { Expr myInstance = Expr.makeCallExpr(AjcMemberMaker.perObjectAspectOfMethod(inAspect), new Expr[] {getVar(shadow)}, inAspect); diff --git a/weaver/src/org/aspectj/weaver/patterns/PerSingleton.java b/weaver/src/org/aspectj/weaver/patterns/PerSingleton.java index 2ffeec761..f4d8b9879 100644 --- a/weaver/src/org/aspectj/weaver/patterns/PerSingleton.java +++ b/weaver/src/org/aspectj/weaver/patterns/PerSingleton.java @@ -16,6 +16,7 @@ package org.aspectj.weaver.patterns; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.util.Set; import org.aspectj.util.FuzzyBoolean; import org.aspectj.weaver.AjcMemberMaker; @@ -29,12 +30,16 @@ import org.aspectj.weaver.ast.Test; public class PerSingleton extends PerClause { public PerSingleton() { } - + + public Set couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS; + } + public FuzzyBoolean fastMatch(FastMatchInfo type) { return FuzzyBoolean.YES; } - public FuzzyBoolean match(Shadow shadow) { + protected FuzzyBoolean matchInternal(Shadow shadow) { return FuzzyBoolean.YES; } @@ -42,7 +47,7 @@ public class PerSingleton extends PerClause { // this method intentionally left blank } - public Test findResidue(Shadow shadow, ExposedState state) { + protected Test findResidueInternal(Shadow shadow, ExposedState state) { Expr myInstance = Expr.makeCallExpr(AjcMemberMaker.perSingletonAspectOfMethod(inAspect), Expr.NONE, inAspect); diff --git a/weaver/src/org/aspectj/weaver/patterns/Pointcut.java b/weaver/src/org/aspectj/weaver/patterns/Pointcut.java index 0cc96ef3d..1ba966c05 100644 --- a/weaver/src/org/aspectj/weaver/patterns/Pointcut.java +++ b/weaver/src/org/aspectj/weaver/patterns/Pointcut.java @@ -17,6 +17,8 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.lang.reflect.Member; +import java.util.Collections; +import java.util.Set; import org.aspectj.lang.JoinPoint; import org.aspectj.util.FuzzyBoolean; @@ -40,6 +42,43 @@ import org.aspectj.weaver.ast.Test; * * @author Erik Hilsdale * @author Jim Hugunin + * + * A day in the life of a pointcut.... - AMC. + * ========================================== + * + * Pointcuts are created by the PatternParser, which is called by ajdt to + * parse a pointcut from the PseudoTokens AST node (which in turn are part + * of a PointcutDesignator AST node). + * + * Pointcuts are resolved by ajdt when an AdviceDeclaration or a + * PointcutDeclaration has its statements resolved. This happens as + * part of completeTypeBindings in the AjLookupEnvironment which is + * called after the diet parse phase of the compiler. Named pointcuts, + * and references to named pointcuts are instances of ReferencePointcut. + * + * At the end of the compilation process, the pointcuts are serialized + * (write method) into attributes in the class file. + * + * When the weaver loads the class files, it unpacks the attributes + * and deserializes the pointcuts (read). All aspects are added to the + * world, by calling addOrReplaceAspect on + * the crosscutting members set of the world. When aspects are added or + * replaced, the crosscutting members in the aspect are extracted as + * ShadowMungers (each holding a pointcut). The ShadowMungers are + * concretized, which concretizes the pointcuts. At this stage + * ReferencePointcuts are replaced by their declared content. + * + * During weaving, the weaver processes type by type. It first culls + * potentially matching ShadowMungers by calling the fastMatch method + * on their pointcuts. Only those that might match make it through to + * the next phase. At the next phase, all of the shadows within the + * type are created and passed to the pointcut for matching (match). + * + * When the actual munging happens, matched pointcuts are asked for + * their residue (findResidue) - the runtime test if any. Because of + * negation, findResidue may be called on pointcuts that could never + * match the shadow. + * */ public abstract class Pointcut extends PatternNode implements PointcutExpressionMatching { public static final class State extends TypeSafeEnum { @@ -53,9 +92,13 @@ public abstract class Pointcut extends PatternNode implements PointcutExpression public static final State CONCRETE = new State("concrete", 2); protected byte pointcutKind; - + public State state; + protected int lastMatchedShadowId; + private FuzzyBoolean lastMatchedShadowResult; + private Test lastMatchedShadowResidue; + /** * Constructor for Pattern. */ @@ -69,12 +112,31 @@ public abstract class Pointcut extends PatternNode implements PointcutExpression * Could I match any shadows in the code defined within this type? */ public abstract FuzzyBoolean fastMatch(FastMatchInfo info); + + /** + * The set of ShadowKinds that this Pointcut could possibly match + */ + public abstract /*Enum*/Set/*<Shadow.Kind>*/ couldMatchKinds(); /** * Do I really match this shadow? * XXX implementors need to handle state */ - public abstract FuzzyBoolean match(Shadow shadow); + public final FuzzyBoolean match(Shadow shadow) { + if (shadow.shadowId == lastMatchedShadowId) return lastMatchedShadowResult; + FuzzyBoolean ret; + // this next test will prevent a lot of un-needed matching going on.... + if (couldMatchKinds().contains(shadow.getKind())) { + ret = matchInternal(shadow); + } else { + ret = FuzzyBoolean.NO; + } + lastMatchedShadowId = shadow.shadowId; + lastMatchedShadowResult = ret; + return ret; + } + + protected abstract FuzzyBoolean matchInternal(Shadow shadow); /* * for runtime / dynamic pointcuts. @@ -143,7 +205,7 @@ public abstract class Pointcut extends PatternNode implements PointcutExpression /** * Returns this pointcut mutated */ - public Pointcut resolve(IScope scope) { + public final Pointcut resolve(IScope scope) { assertState(SYMBOLIC); Bindings bindingTable = new Bindings(scope.getFormalCount()); this.resolveBindings(scope, bindingTable); @@ -164,14 +226,15 @@ public abstract class Pointcut extends PatternNode implements PointcutExpression /** * Returns a new pointcut + * Only used by test cases */ - public Pointcut concretize(ResolvedTypeX inAspect, int arity) { + public final Pointcut concretize(ResolvedTypeX inAspect, int arity) { return concretize(inAspect, IntMap.idMap(arity)); } //XXX this is the signature we're moving to - public Pointcut concretize(ResolvedTypeX inAspect, int arity, ShadowMunger advice) { + public final Pointcut concretize(ResolvedTypeX inAspect, int arity, ShadowMunger advice) { //if (state == CONCRETE) return this; //??? IntMap map = IntMap.idMap(arity); map.setEnclosingAdvice(advice); @@ -187,7 +250,7 @@ public abstract class Pointcut extends PatternNode implements PointcutExpression } - public Pointcut concretize(ResolvedTypeX inAspect, IntMap bindings) { + public final Pointcut concretize(ResolvedTypeX inAspect, IntMap bindings) { //!!! add this test -- assertState(RESOLVED); Pointcut ret = this.concretize1(inAspect, bindings); if (shouldCopyLocationForConcretize()) ret.copyLocationFrom(this); @@ -221,7 +284,15 @@ public abstract class Pointcut extends PatternNode implements PointcutExpression * This can be called from NotPointcut even for Pointcuts that * don't match the shadow */ - public abstract Test findResidue(Shadow shadow, ExposedState state); + public final Test findResidue(Shadow shadow, ExposedState state) { +// if (shadow.shadowId == lastMatchedShadowId) return lastMatchedShadowResidue; + Test ret = findResidueInternal(shadow,state); +// lastMatchedShadowResidue = ret; + lastMatchedShadowId = shadow.shadowId; + return ret; + } + + protected abstract Test findResidueInternal(Shadow shadow,ExposedState state); //XXX we're not sure whether or not this is needed //XXX currently it's unused we're keeping it around as a stub @@ -272,15 +343,19 @@ public abstract class Pointcut extends PatternNode implements PointcutExpression } private static class MatchesNothingPointcut extends Pointcut { - public Test findResidue(Shadow shadow, ExposedState state) { + protected Test findResidueInternal(Shadow shadow, ExposedState state) { return Literal.FALSE; // can only get here if an earlier error occurred } + public Set couldMatchKinds() { + return Collections.EMPTY_SET; + } + public FuzzyBoolean fastMatch(FastMatchInfo type) { return FuzzyBoolean.NO; } - public FuzzyBoolean match(Shadow shadow) { + protected FuzzyBoolean matchInternal(Shadow shadow) { return FuzzyBoolean.NO; } diff --git a/weaver/src/org/aspectj/weaver/patterns/PointcutEvaluationExpenseComparator.java b/weaver/src/org/aspectj/weaver/patterns/PointcutEvaluationExpenseComparator.java new file mode 100644 index 000000000..578670031 --- /dev/null +++ b/weaver/src/org/aspectj/weaver/patterns/PointcutEvaluationExpenseComparator.java @@ -0,0 +1,114 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.util.Comparator; + +import org.aspectj.weaver.Shadow; + +public class PointcutEvaluationExpenseComparator implements Comparator { + + private static final int MATCHES_NOTHING = -1; + private static final int WITHIN = 1; + private static final int ATWITHIN = 2; + private static final int STATICINIT = 3; + private static final int ADVICEEXECUTION = 4; + private static final int HANDLER = 5; + private static final int GET_OR_SET = 6; + private static final int WITHINCODE = 7; + private static final int ATWITHINCODE = 8; + private static final int EXE_INIT_PREINIT = 9; + private static final int CALL = 10; + private static final int ANNOTATION = 11; + private static final int THIS_OR_TARGET = 12; + private static final int AT_THIS_OR_TARGET = 13; + private static final int ARGS = 14; + private static final int AT_ARGS = 15; + private static final int CFLOW = 16; + private static final int IF = 17; + private static final int OTHER = 20; + + /** + * Compare 2 pointcuts based on an estimate of how expensive they may be + * to evaluate. + * + * within + * @within + * staticinitialization [make sure this has a fast match method] + * adviceexecution + * handler + * get, set + * withincode + * @withincode + * execution, initialization, preinitialization + * call + * @annotation + * this, target + * @this, @target + * args + * @args + * cflow, cflowbelow + * if + */ + public int compare(Object o1, Object o2) { + Pointcut p1 = (Pointcut) o1; + Pointcut p2 = (Pointcut) o2; + + // important property for a well-defined comparator + if (p1.equals(p2)) return 0; + int result = getScore(p1) - getScore(p2); + if (result == 0) { + // they have the same evaluation expense, but are not 'equal' + // sort by hashCode + result = p1.hashCode() - p2.hashCode(); + if (result == 0) /*not allowed if ne*/ return -1; + } + return result; + } + + // a higher score means a more expensive evaluation + private int getScore(Pointcut p) { + if (p.couldMatchKinds().isEmpty()) return MATCHES_NOTHING; + if (p instanceof WithinPointcut) return WITHIN; + if (p instanceof WithinAnnotationPointcut) return ATWITHIN; + if (p instanceof KindedPointcut) { + KindedPointcut kp = (KindedPointcut) p; + Shadow.Kind kind = kp.getKind(); + if (kind == Shadow.AdviceExecution) { + return ADVICEEXECUTION; + } else if ((kind == Shadow.ConstructorCall) || (kind == Shadow.MethodCall)) { + return CALL; + } else if ((kind == Shadow.ConstructorExecution) || (kind == Shadow.MethodExecution) || + (kind == Shadow.Initialization) || (kind == Shadow.PreInitialization)) { + return EXE_INIT_PREINIT; + } else if (kind == Shadow.ExceptionHandler) { + return HANDLER; + } else if ((kind == Shadow.FieldGet) || (kind == Shadow.FieldSet)) { + return GET_OR_SET; + } else if (kind == Shadow.StaticInitialization) { + return STATICINIT; + } else return OTHER; + } + if (p instanceof AnnotationPointcut) return ANNOTATION; + if (p instanceof ArgsPointcut) return ARGS; + if (p instanceof ArgsAnnotationPointcut) return AT_ARGS; + if (p instanceof CflowPointcut) return CFLOW; + if (p instanceof HandlerPointcut) return HANDLER; + if (p instanceof IfPointcut) return IF; + if (p instanceof ThisOrTargetPointcut) return THIS_OR_TARGET; + if (p instanceof ThisOrTargetAnnotationPointcut) return AT_THIS_OR_TARGET; + if (p instanceof WithincodePointcut) return WITHINCODE; + if (p instanceof WithinCodeAnnotationPointcut) return ATWITHINCODE; + if (p instanceof NotPointcut) return getScore(((NotPointcut)p).getNegatedPointcut()); + if (p instanceof AndPointcut) return getScore(((AndPointcut)p).getLeft()); + if (p instanceof OrPointcut) return getScore(((OrPointcut)p).getLeft()); + return OTHER; + } +} diff --git a/weaver/src/org/aspectj/weaver/patterns/PointcutRewriter.java b/weaver/src/org/aspectj/weaver/patterns/PointcutRewriter.java new file mode 100644 index 000000000..8b725721b --- /dev/null +++ b/weaver/src/org/aspectj/weaver/patterns/PointcutRewriter.java @@ -0,0 +1,277 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.util.Iterator; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.aspectj.weaver.Shadow; + +/** + * @author colyer + * + * Performs term rewriting for pointcut expressions. + * + */ +public class PointcutRewriter { + + private static final boolean WATCH_PROGRESS = false; + + public Pointcut rewrite(Pointcut pc) { + if (WATCH_PROGRESS) System.out.println(pc); + Pointcut result = distributeNot(pc); + if (WATCH_PROGRESS) System.out.println("==> " + result); + result = pullUpDisjunctions(result); + if (WATCH_PROGRESS) System.out.println("==> " + result); + result = simplifyAnds(result); + if (WATCH_PROGRESS) System.out.println("==> " + result); + result = sortOrs(result); + if (WATCH_PROGRESS) System.out.println("==> " + result); + return result; + } + + + // !!X => X + // !(X && Y) => !X || !Y + // !(X || Y) => !X && !Y + private Pointcut distributeNot(Pointcut pc) { + if (isNot(pc)) { + NotPointcut npc = (NotPointcut) pc; + Pointcut notBody = distributeNot(npc.getNegatedPointcut()); + if (isNot(notBody)) { + // !!X => X + return ((NotPointcut)notBody).getNegatedPointcut(); + } else if (isAnd(notBody)) { + // !(X && Y) => !X || !Y + AndPointcut apc = (AndPointcut) notBody; + Pointcut newLeft = distributeNot(new NotPointcut(apc.getLeft())); + Pointcut newRight = distributeNot(new NotPointcut(apc.getRight())); + return new OrPointcut(newLeft,newRight); + } else if (isOr(notBody)) { + // !(X || Y) => !X && !Y + OrPointcut opc = (OrPointcut) notBody; + Pointcut newLeft = distributeNot(new NotPointcut(opc.getLeft())); + Pointcut newRight = distributeNot(new NotPointcut(opc.getRight())); + return new AndPointcut(newLeft,newRight); + } else { + return new NotPointcut(notBody); + } + } else if (isAnd(pc)) { + AndPointcut apc = (AndPointcut) pc; + Pointcut left = distributeNot(apc.getLeft()); + Pointcut right = distributeNot(apc.getRight()); + return new AndPointcut(left,right); + } else if (isOr(pc)) { + OrPointcut opc = (OrPointcut) pc; + Pointcut left = distributeNot(opc.getLeft()); + Pointcut right = distributeNot(opc.getRight()); + return new OrPointcut(left,right); + } else { + return pc; + } + } + + // A && (B || C) => (A && B) || (A && C) + // (A || B) && C => (A && C) || (B && C) + private Pointcut pullUpDisjunctions(Pointcut pc) { + if (isNot(pc)) { + NotPointcut npc = (NotPointcut)pc; + return new NotPointcut(pullUpDisjunctions(npc.getNegatedPointcut())); + } else if (isAnd(pc)) { + AndPointcut apc = (AndPointcut) pc; + // dive into left and right here... + Pointcut left = pullUpDisjunctions(apc.getLeft()); + Pointcut right = pullUpDisjunctions(apc.getRight()); + if (isOr(left) && !isOr(right)) { + // (A || B) && C => (A && C) || (B && C) + Pointcut leftLeft = ((OrPointcut)left).getLeft(); + Pointcut leftRight = ((OrPointcut)left).getRight(); + return new OrPointcut( + new AndPointcut(leftLeft,right), + new AndPointcut(leftRight,right)); + } else if (isOr(right) && !isOr(left)) { + // A && (B || C) => (A && B) || (A && C) + Pointcut rightLeft = ((OrPointcut)right).getLeft(); + Pointcut rightRight = ((OrPointcut)right).getRight(); + return new OrPointcut( + new AndPointcut(left,rightLeft), + new AndPointcut(left,rightRight)); + + } else { + return new AndPointcut(left,right); + } + } else if (isOr(pc)){ + OrPointcut opc = (OrPointcut) pc; + return new OrPointcut(pullUpDisjunctions(opc.getLeft()), + pullUpDisjunctions(opc.getRight())); + } else { + return pc; + } + } + + // NOT: execution(* TP.*(..)) => within(TP) && execution(* *(..)) + // since this breaks when the pattern matches an interface + // NOT: withincode(* TP.*(..)) => within(TP) && withincode(* *(..)) + // since this is not correct when an aspect makes an ITD +// private Pointcut splitOutWithins(Pointcut pc) { +// if (isExecution(pc)) { +// KindedPointcut kpc = (KindedPointcut) pc; +// SignaturePattern sp = kpc.signature; +// TypePattern within = sp.getDeclaringType(); +// if (isAnyType(within)) return pc; +// SignaturePattern simplified = removeDeclaringTypePattern(sp); +// return new AndPointcut(new WithinPointcut(within), +// new KindedPointcut(kpc.kind,simplified)); +// } else if (isNot(pc)) { +// return new NotPointcut(splitOutWithins(((NotPointcut)pc).getNegatedPointcut())); +// } else if (isAnd(pc)) { +// AndPointcut apc = (AndPointcut) pc; +// return new AndPointcut(splitOutWithins(apc.getLeft()), +// splitOutWithins(apc.getRight())); +// } else if (isOr(pc)) { +// OrPointcut opc = (OrPointcut) pc; +// return new OrPointcut(splitOutWithins(opc.getLeft()), +// splitOutWithins(opc.getRight())); +// } else { +// return pc; +// } +// } + + private SignaturePattern removeDeclaringTypePattern(SignaturePattern sp) { + return new SignaturePattern( + sp.getKind(), + sp.getModifiers(), + sp.getReturnType(), + TypePattern.ANY, + sp.getName(), + sp.getParameterTypes(), + sp.getThrowsPattern(), + sp.getAnnotationPattern() + ); + } + + // this finds the root of each && tree and then aggregates all of the branches + // into a sorted set: + // - duplicates are removed + // - A && !A is replaced by a matchesNothingPointcut + // - the kind(s) matched by the set are evaluated + // - elements are sorted by evaluation complexity + // - the result is written out with the least expensive branch leftmost + private Pointcut simplifyAnds(Pointcut pc) { + if (isNot(pc)) { + NotPointcut npc = (NotPointcut) pc; + return new NotPointcut(simplifyAnds(npc.getNegatedPointcut())); + } else if (isOr(pc)) { + OrPointcut opc = (OrPointcut) pc; + return new OrPointcut(simplifyAnds(opc.getLeft()),simplifyAnds(opc.getRight())); + } else if (isAnd(pc)) { + return simplifyAnd((AndPointcut)pc); + } else { + return pc; + } + } + + private Pointcut simplifyAnd(AndPointcut apc) { + SortedSet nodes = new TreeSet(new PointcutEvaluationExpenseComparator()); + collectAndNodes(apc,nodes); + // look for A and !A, or IfFalse + for (Iterator iter = nodes.iterator(); iter.hasNext();) { + Pointcut element = (Pointcut) iter.next(); + if (element instanceof NotPointcut) { + Pointcut body = ((NotPointcut)element).getNegatedPointcut(); + if (nodes.contains(body)) return Pointcut.makeMatchesNothing(body.state); + } + if (element instanceof IfPointcut) { + if (((IfPointcut)element).alwaysFalse()) return Pointcut.makeMatchesNothing(element.state); + } + if (element.toString().equals("")) return element; // matches nothing... + } + if (apc.couldMatchKinds().isEmpty()) return Pointcut.makeMatchesNothing(apc.state); + // write out with cheapest on left + Iterator iter = nodes.iterator(); + Pointcut result = (Pointcut) iter.next(); + while(iter.hasNext()) { + Pointcut right = (Pointcut) iter.next(); + result = new AndPointcut(result,right); + } + return result; + } + + private Pointcut sortOrs(Pointcut pc) { + SortedSet nodes = new TreeSet(new PointcutEvaluationExpenseComparator()); + collectOrNodes(pc,nodes); + // write out with cheapest on left + Iterator iter = nodes.iterator(); + Pointcut result = (Pointcut) iter.next(); + while(iter.hasNext()) { + Pointcut right = (Pointcut) iter.next(); + result = new OrPointcut(result,right); + } + return result; + } + + private void collectAndNodes(AndPointcut apc,Set nodesSoFar) { + Pointcut left = apc.getLeft(); + Pointcut right = apc.getRight(); + if (isAnd(left)) { + collectAndNodes((AndPointcut)left,nodesSoFar); + } else { + nodesSoFar.add(left); + } + if (isAnd(right)) { + collectAndNodes((AndPointcut)right,nodesSoFar); + } else { + nodesSoFar.add(right); + } + } + + private void collectOrNodes(Pointcut pc, Set nodesSoFar) { + if (isOr(pc)) { + OrPointcut opc = (OrPointcut) pc; + collectOrNodes(opc.getLeft(),nodesSoFar); + collectOrNodes(opc.getRight(),nodesSoFar); + } else { + nodesSoFar.add(pc); + } + } + + private boolean isNot(Pointcut pc) { + return (pc instanceof NotPointcut); + } + + private boolean isAnd(Pointcut pc) { + return (pc instanceof AndPointcut); + } + + private boolean isOr(Pointcut pc) { + return (pc instanceof OrPointcut); + } + + private boolean isExecution(Pointcut pc) { + if (pc instanceof KindedPointcut) { + KindedPointcut kp = (KindedPointcut) pc; + if (kp.kind == Shadow.MethodExecution) return true; + if (kp.kind == Shadow.ConstructorExecution) return true; + } + return false; + } + + private boolean isWithinCode(Pointcut pc) { + return (pc instanceof WithincodePointcut); + } + + private boolean isAnyType(TypePattern tp) { + if (tp == TypePattern.ANY) return true; + if (tp.toString().equals("*")) return true; + return false; + } +}
\ No newline at end of file diff --git a/weaver/src/org/aspectj/weaver/patterns/ReferencePointcut.java b/weaver/src/org/aspectj/weaver/patterns/ReferencePointcut.java index 6b5d17721..ded642b4e 100644 --- a/weaver/src/org/aspectj/weaver/patterns/ReferencePointcut.java +++ b/weaver/src/org/aspectj/weaver/patterns/ReferencePointcut.java @@ -17,6 +17,7 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.lang.reflect.Modifier; +import java.util.Set; import org.aspectj.bridge.IMessage; import org.aspectj.bridge.MessageUtil; @@ -58,6 +59,10 @@ public class ReferencePointcut extends Pointcut { this.pointcutKind = REFERENCE; } + public Set couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS; + } + //??? do either of these match methods make any sense??? public FuzzyBoolean fastMatch(FastMatchInfo type) { @@ -67,7 +72,7 @@ public class ReferencePointcut extends Pointcut { /** * Do I really match this shadow? */ - public FuzzyBoolean match(Shadow shadow) { + protected FuzzyBoolean matchInternal(Shadow shadow) { return FuzzyBoolean.NO; } @@ -210,7 +215,7 @@ public class ReferencePointcut extends Pointcut { arguments.postRead(enclosingType); } - public Test findResidue(Shadow shadow, ExposedState state) { + protected Test findResidueInternal(Shadow shadow, ExposedState state) { throw new RuntimeException("shouldn't happen"); } diff --git a/weaver/src/org/aspectj/weaver/patterns/SignaturePattern.java b/weaver/src/org/aspectj/weaver/patterns/SignaturePattern.java index 7c51135f7..f48cd76ae 100644 --- a/weaver/src/org/aspectj/weaver/patterns/SignaturePattern.java +++ b/weaver/src/org/aspectj/weaver/patterns/SignaturePattern.java @@ -488,6 +488,7 @@ public class SignaturePattern extends PatternNode { && o.declaringType.equals(this.declaringType) && o.name.equals(this.name) && o.parameterTypes.equals(this.parameterTypes) + && o.throwsPattern.equals(this.throwsPattern) && o.annotationPattern.equals(this.annotationPattern); } public int hashCode() { @@ -498,6 +499,7 @@ public class SignaturePattern extends PatternNode { result = 37*result + declaringType.hashCode(); result = 37*result + name.hashCode(); result = 37*result + parameterTypes.hashCode(); + result = 37*result + throwsPattern.hashCode(); result = 37*result + annotationPattern.hashCode(); return result; } diff --git a/weaver/src/org/aspectj/weaver/patterns/ThisOrTargetAnnotationPointcut.java b/weaver/src/org/aspectj/weaver/patterns/ThisOrTargetAnnotationPointcut.java index 946cdc99c..189eeac12 100644 --- a/weaver/src/org/aspectj/weaver/patterns/ThisOrTargetAnnotationPointcut.java +++ b/weaver/src/org/aspectj/weaver/patterns/ThisOrTargetAnnotationPointcut.java @@ -12,6 +12,12 @@ package org.aspectj.weaver.patterns; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; import org.aspectj.bridge.IMessage; import org.aspectj.bridge.ISourceLocation; @@ -41,6 +47,15 @@ public class ThisOrTargetAnnotationPointcut extends NameBindingPointcut { private boolean alreadyWarnedAboutDEoW = false; private ExactAnnotationTypePattern annotationTypePattern; private ShadowMunger munger; + private static final Set thisKindSet = new HashSet(Shadow.ALL_SHADOW_KINDS); + private static final Set targetKindSet = new HashSet(Shadow.ALL_SHADOW_KINDS); + static { + for (Iterator iter = Shadow.ALL_SHADOW_KINDS.iterator(); iter.hasNext();) { + Shadow.Kind kind = (Shadow.Kind) iter.next(); + if (kind.neverHasThis()) thisKindSet.remove(kind); + if (kind.neverHasTarget()) targetKindSet.remove(kind); + } + } /** * @@ -56,6 +71,10 @@ public class ThisOrTargetAnnotationPointcut extends NameBindingPointcut { this.munger = munger; } + public Set couldMatchKinds() { + return isThis ? thisKindSet : targetKindSet; + } + /* (non-Javadoc) * @see org.aspectj.weaver.patterns.Pointcut#fastMatch(org.aspectj.weaver.patterns.FastMatchInfo) */ @@ -66,7 +85,7 @@ public class ThisOrTargetAnnotationPointcut extends NameBindingPointcut { /* (non-Javadoc) * @see org.aspectj.weaver.patterns.Pointcut#match(org.aspectj.weaver.Shadow) */ - public FuzzyBoolean match(Shadow shadow) { + protected FuzzyBoolean matchInternal(Shadow shadow) { if (!couldMatch(shadow)) return FuzzyBoolean.NO; ResolvedTypeX toMatchAgainst = (isThis ? shadow.getThisType() : shadow.getTargetType() ).resolve(shadow.getIWorld()); @@ -136,7 +155,7 @@ public class ThisOrTargetAnnotationPointcut extends NameBindingPointcut { /* (non-Javadoc) * @see org.aspectj.weaver.patterns.Pointcut#findResidue(org.aspectj.weaver.Shadow, org.aspectj.weaver.patterns.ExposedState) */ - public Test findResidue(Shadow shadow, ExposedState state) { + protected Test findResidueInternal(Shadow shadow, ExposedState state) { if (!couldMatch(shadow)) return Literal.FALSE; boolean alwaysMatches = match(shadow).alwaysTrue(); Var var = isThis ? shadow.getThisVar() : shadow.getTargetVar(); @@ -149,15 +168,15 @@ public class ThisOrTargetAnnotationPointcut extends NameBindingPointcut { shadow.getTargetAnnotationVar(annotationType); if (annVar == null) return Literal.TRUE; // should be exception when we implement properly // Check if we have already bound something to this formal - if (state.get(btp.getFormalIndex())!=null) { - ISourceLocation pcdSloc = getSourceLocation(); - ISourceLocation shadowSloc = shadow.getSourceLocation(); - Message errorMessage = new Message( - "Cannot use @pointcut to match at this location and bind a formal to type '"+annVar.getType()+ - "' - the formal is already bound to type '"+state.get(btp.getFormalIndex()).getType()+"'"+ - ". The secondary source location points to the problematic binding.", - shadowSloc,true,new ISourceLocation[]{pcdSloc}); - shadow.getIWorld().getMessageHandler().handleMessage(errorMessage); + if ((state.get(btp.getFormalIndex())!=null) &&(lastMatchedShadowId == shadow.shadowId)) { +// ISourceLocation pcdSloc = getSourceLocation(); +// ISourceLocation shadowSloc = shadow.getSourceLocation(); +// Message errorMessage = new Message( +// "Cannot use @pointcut to match at this location and bind a formal to type '"+annVar.getType()+ +// "' - the formal is already bound to type '"+state.get(btp.getFormalIndex()).getType()+"'"+ +// ". The secondary source location points to the problematic binding.", +// shadowSloc,true,new ISourceLocation[]{pcdSloc}); +// shadow.getIWorld().getMessageHandler().handleMessage(errorMessage); state.setErroneousVar(btp.getFormalIndex()); } state.set(btp.getFormalIndex(),annVar); @@ -181,6 +200,25 @@ public class ThisOrTargetAnnotationPointcut extends NameBindingPointcut { return isThis ? shadow.hasThis() : shadow.hasTarget(); } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingAnnotationTypePatterns() + */ + public List getBindingAnnotationTypePatterns() { + if (annotationTypePattern instanceof BindingAnnotationTypePattern) { + List l = new ArrayList(); + l.add(annotationTypePattern); + return l; + } else return Collections.EMPTY_LIST; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingTypePatterns() + */ + public List getBindingTypePatterns() { + return Collections.EMPTY_LIST; + } + /* (non-Javadoc) * @see org.aspectj.weaver.patterns.PatternNode#write(java.io.DataOutputStream) */ diff --git a/weaver/src/org/aspectj/weaver/patterns/ThisOrTargetPointcut.java b/weaver/src/org/aspectj/weaver/patterns/ThisOrTargetPointcut.java index 4530c933a..c655d1d61 100644 --- a/weaver/src/org/aspectj/weaver/patterns/ThisOrTargetPointcut.java +++ b/weaver/src/org/aspectj/weaver/patterns/ThisOrTargetPointcut.java @@ -17,6 +17,12 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.lang.reflect.Member; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; import org.aspectj.bridge.IMessage; import org.aspectj.bridge.ISourceLocation; @@ -50,7 +56,17 @@ import org.aspectj.weaver.ast.Var; public class ThisOrTargetPointcut extends NameBindingPointcut { private boolean isThis; private TypePattern type; - + + private static final Set thisKindSet = new HashSet(Shadow.ALL_SHADOW_KINDS); + private static final Set targetKindSet = new HashSet(Shadow.ALL_SHADOW_KINDS); + static { + for (Iterator iter = Shadow.ALL_SHADOW_KINDS.iterator(); iter.hasNext();) { + Shadow.Kind kind = (Shadow.Kind) iter.next(); + if (kind.neverHasThis()) thisKindSet.remove(kind); + if (kind.neverHasTarget()) targetKindSet.remove(kind); + } + } + public ThisOrTargetPointcut(boolean isThis, TypePattern type) { this.isThis = isThis; this.type = type; @@ -58,7 +74,11 @@ public class ThisOrTargetPointcut extends NameBindingPointcut { } public boolean isThis() { return isThis; } - + + public Set couldMatchKinds() { + return isThis ? thisKindSet : targetKindSet; + } + public FuzzyBoolean fastMatch(FastMatchInfo type) { return FuzzyBoolean.MAYBE; } @@ -67,7 +87,7 @@ public class ThisOrTargetPointcut extends NameBindingPointcut { return isThis ? shadow.hasThis() : shadow.hasTarget(); } - public FuzzyBoolean match(Shadow shadow) { + protected FuzzyBoolean matchInternal(Shadow shadow) { if (!couldMatch(shadow)) return FuzzyBoolean.NO; TypeX typeToMatch = isThis ? shadow.getThisType() : shadow.getTargetType(); //if (typeToMatch == ResolvedTypeX.MISSING) return FuzzyBoolean.NO; @@ -131,6 +151,25 @@ public class ThisOrTargetPointcut extends NameBindingPointcut { type.postRead(enclosingType); } + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingAnnotationTypePatterns() + */ + public List getBindingAnnotationTypePatterns() { + return Collections.EMPTY_LIST; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingTypePatterns() + */ + public List getBindingTypePatterns() { + if (type instanceof BindingTypePattern) { + List l = new ArrayList(); + l.add(type); + return l; + } else return Collections.EMPTY_LIST; + } + + public boolean equals(Object other) { if (!(other instanceof ThisOrTargetPointcut)) return false; ThisOrTargetPointcut o = (ThisOrTargetPointcut)other; @@ -146,7 +185,7 @@ public class ThisOrTargetPointcut extends NameBindingPointcut { return (isThis ? "this(" : "target(") + type + ")"; } - public Test findResidue(Shadow shadow, ExposedState state) { + protected Test findResidueInternal(Shadow shadow, ExposedState state) { if (!couldMatch(shadow)) return Literal.FALSE; if (type == TypePattern.ANY) return Literal.TRUE; @@ -156,15 +195,15 @@ public class ThisOrTargetPointcut extends NameBindingPointcut { if (type instanceof BindingTypePattern) { BindingTypePattern btp = (BindingTypePattern)type; // Check if we have already bound something to this formal - if (state.get(btp.getFormalIndex())!=null) { - ISourceLocation pcdSloc = getSourceLocation(); - ISourceLocation shadowSloc = shadow.getSourceLocation(); - Message errorMessage = new Message( - "Cannot use "+(isThis?"this()":"target()")+" to match at this location and bind a formal to type '"+var.getType()+ - "' - the formal is already bound to type '"+state.get(btp.getFormalIndex()).getType()+"'"+ - ". The secondary source location points to the problematic "+(isThis?"this()":"target()")+".", - shadowSloc,true,new ISourceLocation[]{pcdSloc}); - shadow.getIWorld().getMessageHandler().handleMessage(errorMessage); + if ((state.get(btp.getFormalIndex())!=null) && (lastMatchedShadowId != shadow.shadowId)){ +// ISourceLocation pcdSloc = getSourceLocation(); +// ISourceLocation shadowSloc = shadow.getSourceLocation(); +// Message errorMessage = new Message( +// "Cannot use "+(isThis?"this()":"target()")+" to match at this location and bind a formal to type '"+var.getType()+ +// "' - the formal is already bound to type '"+state.get(btp.getFormalIndex()).getType()+"'"+ +// ". The secondary source location points to the problematic "+(isThis?"this()":"target()")+".", +// shadowSloc,true,new ISourceLocation[]{pcdSloc}); +// shadow.getIWorld().getMessageHandler().handleMessage(errorMessage); state.setErroneousVar(btp.getFormalIndex()); //return null; } @@ -186,7 +225,9 @@ public class ThisOrTargetPointcut extends NameBindingPointcut { inAspect.crosscuttingMembers.exposeType(newType.getExactType()); } - return new ThisOrTargetPointcut(isThis, newType); + Pointcut ret = new ThisOrTargetPointcut(isThis, newType); + ret.copyLocationFrom(this); + return ret; } } diff --git a/weaver/src/org/aspectj/weaver/patterns/ThrowsPattern.java b/weaver/src/org/aspectj/weaver/patterns/ThrowsPattern.java index dd95137ec..5af1c4049 100644 --- a/weaver/src/org/aspectj/weaver/patterns/ThrowsPattern.java +++ b/weaver/src/org/aspectj/weaver/patterns/ThrowsPattern.java @@ -49,8 +49,9 @@ public class ThrowsPattern extends PatternNode { public boolean equals(Object other) { if (!(other instanceof ThrowsPattern)) return false; ThrowsPattern o = (ThrowsPattern)other; - return o.required.equals(this.required) && + boolean ret = o.required.equals(this.required) && o.forbidden.equals(this.forbidden); + return ret; } public int hashCode() { int result = 17; diff --git a/weaver/src/org/aspectj/weaver/patterns/TypePattern.java b/weaver/src/org/aspectj/weaver/patterns/TypePattern.java index 0596bb1e9..1295be59d 100644 --- a/weaver/src/org/aspectj/weaver/patterns/TypePattern.java +++ b/weaver/src/org/aspectj/weaver/patterns/TypePattern.java @@ -319,6 +319,20 @@ class EllipsisTypePattern extends TypePattern { } public String toString() { return ".."; } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + return (obj instanceof EllipsisTypePattern); + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return 17 * 37; + } } class AnyTypePattern extends TypePattern { @@ -385,6 +399,14 @@ class AnyTypePattern extends TypePattern { } public String toString() { return "*"; } + + public boolean equals(Object obj) { + return (obj instanceof AnyTypePattern); + } + + public int hashCode() { + return 37; + } } class NoTypePattern extends TypePattern { @@ -447,5 +469,19 @@ class NoTypePattern extends TypePattern { } public String toString() { return "<nothing>"; } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + return (obj instanceof NoTypePattern); + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return 17 * 37 * 37; + } } diff --git a/weaver/src/org/aspectj/weaver/patterns/WildTypePattern.java b/weaver/src/org/aspectj/weaver/patterns/WildTypePattern.java index 4e7dc4b79..02344c47e 100644 --- a/weaver/src/org/aspectj/weaver/patterns/WildTypePattern.java +++ b/weaver/src/org/aspectj/weaver/patterns/WildTypePattern.java @@ -532,6 +532,9 @@ public class WildTypePattern extends TypePattern { WildTypePattern o = (WildTypePattern)other; int len = o.namePatterns.length; if (len != this.namePatterns.length) return false; + if (this.includeSubtypes != o.includeSubtypes) return false; + if (this.dim != o.dim) return false; + if (this.isVarArgs != o.isVarArgs) return false; for (int i=0; i < len; i++) { if (!o.namePatterns[i].equals(this.namePatterns[i])) return false; } diff --git a/weaver/src/org/aspectj/weaver/patterns/WithinAnnotationPointcut.java b/weaver/src/org/aspectj/weaver/patterns/WithinAnnotationPointcut.java index 76bf81abe..bc65e609e 100644 --- a/weaver/src/org/aspectj/weaver/patterns/WithinAnnotationPointcut.java +++ b/weaver/src/org/aspectj/weaver/patterns/WithinAnnotationPointcut.java @@ -12,6 +12,10 @@ package org.aspectj.weaver.patterns; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; import org.aspectj.bridge.IMessage; import org.aspectj.bridge.ISourceLocation; @@ -52,6 +56,10 @@ public class WithinAnnotationPointcut extends NameBindingPointcut { this.munger = munger; } + public Set couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS; + } + /* (non-Javadoc) * @see org.aspectj.weaver.patterns.Pointcut#fastMatch(org.aspectj.weaver.patterns.FastMatchInfo) */ @@ -62,7 +70,7 @@ public class WithinAnnotationPointcut extends NameBindingPointcut { /* (non-Javadoc) * @see org.aspectj.weaver.patterns.Pointcut#match(org.aspectj.weaver.Shadow) */ - public FuzzyBoolean match(Shadow shadow) { + protected FuzzyBoolean matchInternal(Shadow shadow) { ResolvedTypeX enclosingType = shadow.getIWorld().resolve(shadow.getEnclosingType(),true); if (enclosingType == ResolvedTypeX.MISSING) { IMessage msg = new Message( @@ -104,22 +112,22 @@ public class WithinAnnotationPointcut extends NameBindingPointcut { /* (non-Javadoc) * @see org.aspectj.weaver.patterns.Pointcut#findResidue(org.aspectj.weaver.Shadow, org.aspectj.weaver.patterns.ExposedState) */ - public Test findResidue(Shadow shadow, ExposedState state) { + protected Test findResidueInternal(Shadow shadow, ExposedState state) { if (annotationTypePattern instanceof BindingAnnotationTypePattern) { BindingAnnotationTypePattern btp = (BindingAnnotationTypePattern)annotationTypePattern; TypeX annotationType = btp.annotationType; Var var = shadow.getWithinAnnotationVar(annotationType); if (var == null) return Literal.FALSE; // Check if we have already bound something to this formal - if (state.get(btp.getFormalIndex())!=null) { - ISourceLocation pcdSloc = getSourceLocation(); - ISourceLocation shadowSloc = shadow.getSourceLocation(); - Message errorMessage = new Message( - "Cannot use @pointcut to match at this location and bind a formal to type '"+var.getType()+ - "' - the formal is already bound to type '"+state.get(btp.getFormalIndex()).getType()+"'"+ - ". The secondary source location points to the problematic binding.", - shadowSloc,true,new ISourceLocation[]{pcdSloc}); - shadow.getIWorld().getMessageHandler().handleMessage(errorMessage); + if ((state.get(btp.getFormalIndex())!=null) &&(lastMatchedShadowId == shadow.shadowId)) { +// ISourceLocation pcdSloc = getSourceLocation(); +// ISourceLocation shadowSloc = shadow.getSourceLocation(); +// Message errorMessage = new Message( +// "Cannot use @pointcut to match at this location and bind a formal to type '"+var.getType()+ +// "' - the formal is already bound to type '"+state.get(btp.getFormalIndex()).getType()+"'"+ +// ". The secondary source location points to the problematic binding.", +// shadowSloc,true,new ISourceLocation[]{pcdSloc}); +// shadow.getIWorld().getMessageHandler().handleMessage(errorMessage); state.setErroneousVar(btp.getFormalIndex()); } state.set(btp.getFormalIndex(),var); @@ -128,6 +136,24 @@ public class WithinAnnotationPointcut extends NameBindingPointcut { } /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingAnnotationTypePatterns() + */ + public List getBindingAnnotationTypePatterns() { + if (annotationTypePattern instanceof BindingAnnotationTypePattern) { + List l = new ArrayList(); + l.add(annotationTypePattern); + return l; + } else return Collections.EMPTY_LIST; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingTypePatterns() + */ + public List getBindingTypePatterns() { + return Collections.EMPTY_LIST; + } + + /* (non-Javadoc) * @see org.aspectj.weaver.patterns.PatternNode#write(java.io.DataOutputStream) */ public void write(DataOutputStream s) throws IOException { diff --git a/weaver/src/org/aspectj/weaver/patterns/WithinCodeAnnotationPointcut.java b/weaver/src/org/aspectj/weaver/patterns/WithinCodeAnnotationPointcut.java index c51d7008b..4eb6473e3 100644 --- a/weaver/src/org/aspectj/weaver/patterns/WithinCodeAnnotationPointcut.java +++ b/weaver/src/org/aspectj/weaver/patterns/WithinCodeAnnotationPointcut.java @@ -12,6 +12,11 @@ package org.aspectj.weaver.patterns; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import org.aspectj.bridge.ISourceLocation; import org.aspectj.bridge.Message; @@ -40,6 +45,14 @@ public class WithinCodeAnnotationPointcut extends NameBindingPointcut { private ExactAnnotationTypePattern annotationTypePattern; private ShadowMunger munger = null; // only set after concretization + private static final Set matchedShadowKinds = new HashSet(); + static { + matchedShadowKinds.addAll(Shadow.ALL_SHADOW_KINDS); + for (int i = 0; i < Shadow.SHADOW_KINDS.length; i++) { + if (Shadow.SHADOW_KINDS[i].isEnclosingKind()) + matchedShadowKinds.remove(Shadow.SHADOW_KINDS[i]); + } + } public WithinCodeAnnotationPointcut(ExactAnnotationTypePattern type) { super(); @@ -52,6 +65,10 @@ public class WithinCodeAnnotationPointcut extends NameBindingPointcut { this.munger = munger; } + public Set couldMatchKinds() { + return matchedShadowKinds; + } + /* (non-Javadoc) * @see org.aspectj.weaver.patterns.Pointcut#fastMatch(org.aspectj.weaver.patterns.FastMatchInfo) */ @@ -62,7 +79,7 @@ public class WithinCodeAnnotationPointcut extends NameBindingPointcut { /* (non-Javadoc) * @see org.aspectj.weaver.patterns.Pointcut#match(org.aspectj.weaver.Shadow) */ - public FuzzyBoolean match(Shadow shadow) { + protected FuzzyBoolean matchInternal(Shadow shadow) { AnnotatedElement toMatchAgainst = null; Member member = shadow.getEnclosingCodeSignature(); ResolvedMember rMember = member.resolve(shadow.getIWorld()); @@ -109,7 +126,7 @@ public class WithinCodeAnnotationPointcut extends NameBindingPointcut { /* (non-Javadoc) * @see org.aspectj.weaver.patterns.Pointcut#findResidue(org.aspectj.weaver.Shadow, org.aspectj.weaver.patterns.ExposedState) */ - public Test findResidue(Shadow shadow, ExposedState state) { + protected Test findResidueInternal(Shadow shadow, ExposedState state) { if (annotationTypePattern instanceof BindingAnnotationTypePattern) { BindingAnnotationTypePattern btp = (BindingAnnotationTypePattern)annotationTypePattern; @@ -117,15 +134,15 @@ public class WithinCodeAnnotationPointcut extends NameBindingPointcut { Var var = shadow.getWithinCodeAnnotationVar(annotationType); if (var == null) return Literal.FALSE; // Check if we have already bound something to this formal - if (state.get(btp.getFormalIndex())!=null) { - ISourceLocation pcdSloc = getSourceLocation(); - ISourceLocation shadowSloc = shadow.getSourceLocation(); - Message errorMessage = new Message( - "Cannot use @pointcut to match at this location and bind a formal to type '"+var.getType()+ - "' - the formal is already bound to type '"+state.get(btp.getFormalIndex()).getType()+"'"+ - ". The secondary source location points to the problematic binding.", - shadowSloc,true,new ISourceLocation[]{pcdSloc}); - shadow.getIWorld().getMessageHandler().handleMessage(errorMessage); + if ((state.get(btp.getFormalIndex())!=null) &&(lastMatchedShadowId == shadow.shadowId)) { +// ISourceLocation pcdSloc = getSourceLocation(); +// ISourceLocation shadowSloc = shadow.getSourceLocation(); +// Message errorMessage = new Message( +// "Cannot use @pointcut to match at this location and bind a formal to type '"+var.getType()+ +// "' - the formal is already bound to type '"+state.get(btp.getFormalIndex()).getType()+"'"+ +// ". The secondary source location points to the problematic binding.", +// shadowSloc,true,new ISourceLocation[]{pcdSloc}); +// shadow.getIWorld().getMessageHandler().handleMessage(errorMessage); state.setErroneousVar(btp.getFormalIndex()); } state.set(btp.getFormalIndex(),var); @@ -133,6 +150,25 @@ public class WithinCodeAnnotationPointcut extends NameBindingPointcut { return Literal.TRUE; } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingAnnotationTypePatterns() + */ + public List getBindingAnnotationTypePatterns() { + if (annotationTypePattern instanceof BindingAnnotationTypePattern) { + List l = new ArrayList(); + l.add(annotationTypePattern); + return l; + } else return Collections.EMPTY_LIST; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingTypePatterns() + */ + public List getBindingTypePatterns() { + return Collections.EMPTY_LIST; + } + /* (non-Javadoc) * @see org.aspectj.weaver.patterns.PatternNode#write(java.io.DataOutputStream) */ diff --git a/weaver/src/org/aspectj/weaver/patterns/WithinPointcut.java b/weaver/src/org/aspectj/weaver/patterns/WithinPointcut.java index 50181a6c7..edda70510 100644 --- a/weaver/src/org/aspectj/weaver/patterns/WithinPointcut.java +++ b/weaver/src/org/aspectj/weaver/patterns/WithinPointcut.java @@ -17,6 +17,7 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.lang.reflect.Member; +import java.util.Set; import org.aspectj.bridge.IMessage; import org.aspectj.bridge.ISourceLocation; @@ -48,12 +49,16 @@ public class WithinPointcut extends Pointcut { } return FuzzyBoolean.NO; } + + public Set couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS; + } public FuzzyBoolean fastMatch(FastMatchInfo info) { return isWithinType(info.getType()); } - public FuzzyBoolean match(Shadow shadow) { + protected FuzzyBoolean matchInternal(Shadow shadow) { ResolvedTypeX enclosingType = shadow.getIWorld().resolve(shadow.getEnclosingType(),true); if (enclosingType == ResolvedTypeX.MISSING) { IMessage msg = new Message( @@ -146,12 +151,14 @@ public class WithinPointcut extends Pointcut { return "within(" + typePattern + ")"; } - public Test findResidue(Shadow shadow, ExposedState state) { + protected Test findResidueInternal(Shadow shadow, ExposedState state) { return match(shadow).alwaysTrue() ? Literal.TRUE : Literal.FALSE; } public Pointcut concretize1(ResolvedTypeX inAspect, IntMap bindings) { - return new WithinPointcut(typePattern); + Pointcut ret = new WithinPointcut(typePattern); + ret.copyLocationFrom(this); + return ret; } } diff --git a/weaver/src/org/aspectj/weaver/patterns/WithincodePointcut.java b/weaver/src/org/aspectj/weaver/patterns/WithincodePointcut.java index 8778a679e..2de912cc4 100644 --- a/weaver/src/org/aspectj/weaver/patterns/WithincodePointcut.java +++ b/weaver/src/org/aspectj/weaver/patterns/WithincodePointcut.java @@ -17,6 +17,8 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.lang.reflect.Member; +import java.util.HashSet; +import java.util.Set; import org.aspectj.lang.JoinPoint; import org.aspectj.runtime.reflect.Factory; @@ -30,17 +32,32 @@ import org.aspectj.weaver.ast.Test; public class WithincodePointcut extends Pointcut { SignaturePattern signature; + private static final Set matchedShadowKinds = new HashSet(); + static { + matchedShadowKinds.addAll(Shadow.ALL_SHADOW_KINDS); + for (int i = 0; i < Shadow.SHADOW_KINDS.length; i++) { + if (Shadow.SHADOW_KINDS[i].isEnclosingKind()) + matchedShadowKinds.remove(Shadow.SHADOW_KINDS[i]); + } + // these next two are needed for inlining of field initializers + matchedShadowKinds.add(Shadow.ConstructorExecution); + matchedShadowKinds.add(Shadow.Initialization); + } public WithincodePointcut(SignaturePattern signature) { this.signature = signature; this.pointcutKind = WITHINCODE; } - + + public Set couldMatchKinds() { + return matchedShadowKinds; + } + public FuzzyBoolean fastMatch(FastMatchInfo type) { return FuzzyBoolean.MAYBE; } - public FuzzyBoolean match(Shadow shadow) { + protected FuzzyBoolean matchInternal(Shadow shadow) { //This will not match code in local or anonymous classes as if //they were withincode of the outer signature return FuzzyBoolean.fromBoolean( @@ -107,12 +124,14 @@ public class WithincodePointcut extends Pointcut { return "withincode(" + signature + ")"; } - public Test findResidue(Shadow shadow, ExposedState state) { + protected Test findResidueInternal(Shadow shadow, ExposedState state) { return match(shadow).alwaysTrue() ? Literal.TRUE : Literal.FALSE; } public Pointcut concretize1(ResolvedTypeX inAspect, IntMap bindings) { - return new WithincodePointcut(signature); + Pointcut ret = new WithincodePointcut(signature); + ret.copyLocationFrom(this); + return ret; } } diff --git a/weaver/src/org/aspectj/weaver/weaver-messages.properties b/weaver/src/org/aspectj/weaver/weaver-messages.properties index e3e7c9c7f..e33fa4557 100644 --- a/weaver/src/org/aspectj/weaver/weaver-messages.properties +++ b/weaver/src/org/aspectj/weaver/weaver-messages.properties @@ -108,6 +108,12 @@ errorLoadingXLintDefault=problem loading XlintDefault.properties, {0} invalidXLintKey=invalid Xlint key: {0} invalidXLintMessageKind=invalid Xlint message kind (must be one of ignore, warning, error): {0} +# Binding of formals +unboundFormalInPC=the parameter {0} is not bound in [all branches of] pointcut +ambiguousBindingInPC=the binding of parameter {0} is ambiguous in pointcut +ambiguousBindingInOrPC=ambiguous binding of parameter(s) {0} across ''||'' in pointcut +negationDoesntAllowBinding=cannot bind a parameter in a negated expression + # Java5 # Enum diff --git a/weaver/testsrc/org/aspectj/weaver/patterns/PatternsTests.java b/weaver/testsrc/org/aspectj/weaver/patterns/PatternsTests.java index 71f35d6df..f093740d9 100644 --- a/weaver/testsrc/org/aspectj/weaver/patterns/PatternsTests.java +++ b/weaver/testsrc/org/aspectj/weaver/patterns/PatternsTests.java @@ -39,6 +39,7 @@ public class PatternsTests extends TestCase { suite.addTestSuite(WithinCodeTestCase.class); suite.addTestSuite(AnnotationPatternTestCase.class); suite.addTestSuite(AnnotationPatternMatchingTestCase.class); + suite.addTestSuite(PointcutRewriterTest.class); //$JUnit-END$ return suite; } diff --git a/weaver/testsrc/org/aspectj/weaver/patterns/PointcutRewriterTest.java b/weaver/testsrc/org/aspectj/weaver/patterns/PointcutRewriterTest.java new file mode 100644 index 000000000..c208c621d --- /dev/null +++ b/weaver/testsrc/org/aspectj/weaver/patterns/PointcutRewriterTest.java @@ -0,0 +1,366 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.util.Iterator; +import java.util.Set; + +import org.aspectj.weaver.Shadow; + +import junit.framework.TestCase; + +/** + * @author colyer + * + * TODO To change the template for this generated type comment go to + * Window - Preferences - Java - Code Style - Code Templates + */ +public class PointcutRewriterTest extends TestCase { + + private PointcutRewriter prw; + + public void testDistributeNot() { + Pointcut plain = getPointcut("this(Foo)"); + assertEquals("Unchanged",plain,prw.rewrite(plain)); + Pointcut not = getPointcut("!this(Foo)"); + assertEquals("Unchanged",not,prw.rewrite(not)); + Pointcut notNot = getPointcut("!!this(Foo)"); + assertEquals("this(Foo)",prw.rewrite(notNot).toString()); + Pointcut notNotNOT = getPointcut("!!!this(Foo)"); + assertEquals("!this(Foo)",prw.rewrite(notNotNOT).toString()); + Pointcut and = getPointcut("!(this(Foo) && this(Goo))"); + assertEquals("(!this(Foo) || !this(Goo))",prw.rewrite(and).toString()); + Pointcut or = getPointcut("!(this(Foo) || this(Goo))"); + assertEquals("(!this(Foo) && !this(Goo))",prw.rewrite(or).toString()); + Pointcut nestedNot = getPointcut("!(this(Foo) && !this(Goo))"); + assertEquals("(!this(Foo) || this(Goo))",prw.rewrite(nestedNot).toString()); + } + + public void testPullUpDisjunctions() { + Pointcut aAndb = getPointcut("this(Foo) && this(Goo)"); + assertEquals("Unchanged",aAndb,prw.rewrite(aAndb)); + Pointcut aOrb = getPointcut("this(Foo) || this(Moo)"); + assertEquals("Unchanged",aOrb,prw.rewrite(aOrb)); + Pointcut leftOr = getPointcut("this(Foo) || (this(Goo) && this(Boo))"); + System.out.println(prw.rewrite(leftOr)); + assertEquals("(this(Foo) || (this(Boo) && this(Goo)))",prw.rewrite(leftOr).toString()); + Pointcut rightOr = getPointcut("(this(Goo) && this(Boo)) || this(Foo)"); + assertEquals("(this(Foo) || (this(Boo) && this(Goo)))",prw.rewrite(rightOr).toString()); + Pointcut leftAnd = getPointcut("this(Foo) && (this(Goo) || this(Boo))"); + assertEquals("((this(Boo) && this(Foo)) || (this(Foo) && this(Goo)))",prw.rewrite(leftAnd).toString()); + Pointcut rightAnd = getPointcut("(this(Goo) || this(Boo)) && this(Foo)"); + assertEquals("((this(Boo) && this(Foo)) || (this(Foo) && this(Goo)))",prw.rewrite(rightAnd).toString()); + Pointcut nestedOrs = getPointcut("this(Foo) || this(Goo) || this(Boo)"); + assertEquals("((this(Boo) || this(Foo)) || this(Goo))",prw.rewrite(nestedOrs).toString()); + Pointcut nestedAnds = getPointcut("(this(Foo) && (this(Boo) && (this(Goo) || this(Moo))))"); + // t(F) && (t(B) && (t(G) || t(M))) + // ==> t(F) && ((t(B) && t(G)) || (t(B) && t(M))) + // ==> (t(F) && (t(B) && t(G))) || (t(F) && (t(B) && t(M))) + assertEquals("(((this(Boo) && this(Foo)) && this(Goo)) || ((this(Boo) && this(Foo)) && this(Moo)))", + prw.rewrite(nestedAnds).toString()); + } + + +// public void testSplitOutWithins() { +// Pointcut simpleExecution = getPointcut("execution(* *.*(..))"); +// assertEquals("Unchanged",simpleExecution,prw.rewrite(simpleExecution)); +// Pointcut simpleWithinCode = getPointcut("withincode(* *.*(..))"); +// assertEquals("Unchanged",simpleWithinCode,prw.rewrite(simpleWithinCode)); +// Pointcut execution = getPointcut("execution(@Foo Foo (@Goo org.xyz..*).m*(Foo,Boo))"); +// assertEquals("(within((@(Goo) org.xyz..*)) && execution(@(Foo) Foo m*(Foo, Boo)))", +// prw.rewrite(execution).toString()); +// Pointcut withincode = getPointcut("withincode(@Foo Foo (@Goo org.xyz..*).m*(Foo,Boo))"); +// assertEquals("(within((@(Goo) org.xyz..*)) && withincode(@(Foo) Foo m*(Foo, Boo)))", +// prw.rewrite(withincode).toString()); +// Pointcut notExecution = getPointcut("!execution(Foo BankAccount+.*(..))"); +// assertEquals("(!within(BankAccount+) || !execution(Foo *(..)))", +// prw.rewrite(notExecution).toString()); +// Pointcut andWithincode = getPointcut("withincode(Foo.new(..)) && this(Foo)"); +// assertEquals("((within(Foo) && withincode(new(..))) && this(Foo))", +// prw.rewrite(andWithincode).toString()); +// Pointcut orExecution = getPointcut("this(Foo) || execution(Goo Foo.moo(Baa))"); +// assertEquals("((within(Foo) && execution(Goo moo(Baa))) || this(Foo))", +// prw.rewrite(orExecution).toString()); +// } + + + public void testRemoveDuplicatesInAnd() { + Pointcut dupAnd = getPointcut("this(Foo) && this(Foo)"); + assertEquals("this(Foo)",prw.rewrite(dupAnd).toString()); + Pointcut splitdupAnd = getPointcut("(this(Foo) && target(Boo)) && this(Foo)"); + assertEquals("(target(Boo) && this(Foo))",prw.rewrite(splitdupAnd).toString()); + } + + public void testNotRemoveNearlyDuplicatesInAnd() { + Pointcut toAndto = getPointcut("this(Object+) && this(Object)"); + Pointcut rewritten = prw.rewrite(toAndto); + } + + public void testAAndNotAinAnd() { + Pointcut aAndNota = getPointcut("this(Foo)&& !this(Foo)"); + assertEquals("Matches nothing","",prw.rewrite(aAndNota).toString()); + Pointcut aAndBAndNota = getPointcut("this(Foo) && execution(* *.*(..)) && !this(Foo)"); + assertEquals("Matches nothing","",prw.rewrite(aAndBAndNota).toString()); + } + + public void testIfFalseInAnd() { + Pointcut ifFalse = IfPointcut.makeIfFalsePointcut(Pointcut.CONCRETE); + Pointcut p = getPointcut("this(A)"); + assertEquals("Matches nothing","",prw.rewrite(new AndPointcut(ifFalse,p)).toString()); + } + + public void testMatchesNothinginAnd() { + Pointcut nothing = Pointcut.makeMatchesNothing(Pointcut.CONCRETE); + Pointcut p = getPointcut("this(A)"); + assertEquals("Matches nothing","",prw.rewrite(new AndPointcut(nothing,p)).toString()); + } + + public void testMixedKindsInAnd() { + Pointcut mixedKinds = getPointcut("call(* *(..)) && execution(* *(..))"); + assertEquals("Matches nothing","",prw.rewrite(mixedKinds).toString()); + Pointcut ok = getPointcut("call(* *(..)) && this(Foo)"); + assertEquals(ok,prw.rewrite(ok)); + } + + public void testDetermineKindSetOfAnd() { + Pointcut oneKind = getPointcut("execution(* foo(..)) && this(Boo)"); + AndPointcut rewritten = (AndPointcut) prw.rewrite(oneKind); + assertEquals("Only one kind",1,rewritten.couldMatchKinds().size()); + assertTrue("It's Shadow.MethodExecution",rewritten.couldMatchKinds().contains(Shadow.MethodExecution)); + } + + public void testKindSetOfExecution() { + Pointcut p = getPointcut("execution(* foo(..))"); + assertEquals("Only one kind",1,p.couldMatchKinds().size()); + assertTrue("It's Shadow.MethodExecution",p.couldMatchKinds().contains(Shadow.MethodExecution)); + p = getPointcut("execution(new(..))"); + assertEquals("Only one kind",1,p.couldMatchKinds().size()); + assertTrue("It's Shadow.ConstructorExecution",p.couldMatchKinds().contains(Shadow.ConstructorExecution)); + } + + public void testKindSetOfCall() { + Pointcut p = getPointcut("call(* foo(..))"); + assertEquals("Only one kind",1,p.couldMatchKinds().size()); + assertTrue("It's Shadow.MethodCall",p.couldMatchKinds().contains(Shadow.MethodCall)); + p = getPointcut("call(new(..))"); + assertEquals("Only one kind",1,p.couldMatchKinds().size()); + assertTrue("It's Shadow.ConstructorCall",p.couldMatchKinds().contains(Shadow.ConstructorCall)); + } + + public void testKindSetOfAdviceExecution() { + Pointcut p = getPointcut("adviceexecution()"); + assertEquals("Only one kind",1,p.couldMatchKinds().size()); + assertTrue("It's Shadow.AdviceExecution",p.couldMatchKinds().contains(Shadow.AdviceExecution)); + } + + public void testKindSetOfGet() { + Pointcut p = getPointcut("get(* *)"); + assertEquals("Only one kind",1,p.couldMatchKinds().size()); + assertTrue("It's Shadow.FieldGet",p.couldMatchKinds().contains(Shadow.FieldGet)); + } + + public void testKindSetOfSet() { + Pointcut p = getPointcut("set(* *)"); + assertEquals("Only one kind",1,p.couldMatchKinds().size()); + assertTrue("It's Shadow.FieldSet",p.couldMatchKinds().contains(Shadow.FieldSet)); + } + + public void testKindSetOfHandler() { + Pointcut p = getPointcut("handler(*)"); + assertEquals("Only one kind",1,p.couldMatchKinds().size()); + assertTrue("It's Shadow.ExceptionHandler",p.couldMatchKinds().contains(Shadow.ExceptionHandler)); + } + + public void testKindSetOfInitialization() { + Pointcut p = getPointcut("initialization(new (..))"); + assertEquals("Only one kind",1,p.couldMatchKinds().size()); + assertTrue("It's Shadow.Initialization",p.couldMatchKinds().contains(Shadow.Initialization)); + } + + public void testKindSetOfPreInitialization() { + Pointcut p = getPointcut("preinitialization(new (..))"); + assertEquals("Only one kind",1,p.couldMatchKinds().size()); + assertTrue("It's Shadow.PreInitialization",p.couldMatchKinds().contains(Shadow.PreInitialization)); + } + + public void testKindSetOfStaticInitialization() { + Pointcut p = getPointcut("staticinitialization(*)"); + assertEquals("Only one kind",1,p.couldMatchKinds().size()); + assertTrue("It's Shadow.StaticInitialization",p.couldMatchKinds().contains(Shadow.StaticInitialization)); + } + + public void testKindSetOfThis() { + Pointcut p = getPointcut("this(Foo)"); + Set matches = p.couldMatchKinds(); + for (Iterator iter = matches.iterator(); iter.hasNext();) { + Shadow.Kind kind = (Shadow.Kind) iter.next(); + assertFalse("No kinds that don't have a this",kind.neverHasThis()); + } + for (int i = 0; i < Shadow.SHADOW_KINDS.length; i++) { + if (!Shadow.SHADOW_KINDS[i].neverHasThis()) { + assertTrue("All kinds that do have this",matches.contains(Shadow.SHADOW_KINDS[i])); + } + } + // + @ + p = getPointcut("@this(@Foo)"); + matches = p.couldMatchKinds(); + for (Iterator iter = matches.iterator(); iter.hasNext();) { + Shadow.Kind kind = (Shadow.Kind) iter.next(); + assertFalse("No kinds that don't have a this",kind.neverHasThis()); + } + for (int i = 0; i < Shadow.SHADOW_KINDS.length; i++) { + if (!Shadow.SHADOW_KINDS[i].neverHasThis()) { + assertTrue("All kinds that do have this",matches.contains(Shadow.SHADOW_KINDS[i])); + } + } + } + + public void testKindSetOfTarget() { + Pointcut p = getPointcut("target(Foo)"); + Set matches = p.couldMatchKinds(); + for (Iterator iter = matches.iterator(); iter.hasNext();) { + Shadow.Kind kind = (Shadow.Kind) iter.next(); + assertFalse("No kinds that don't have a target",kind.neverHasTarget()); + } + for (int i = 0; i < Shadow.SHADOW_KINDS.length; i++) { + if (!Shadow.SHADOW_KINDS[i].neverHasTarget()) { + assertTrue("All kinds that do have target",matches.contains(Shadow.SHADOW_KINDS[i])); + } + } + // + @ + p = getPointcut("@target(@Foo)"); + matches = p.couldMatchKinds(); + for (Iterator iter = matches.iterator(); iter.hasNext();) { + Shadow.Kind kind = (Shadow.Kind) iter.next(); + assertFalse("No kinds that don't have a target",kind.neverHasTarget()); + } + for (int i = 0; i < Shadow.SHADOW_KINDS.length; i++) { + if (!Shadow.SHADOW_KINDS[i].neverHasTarget()) { + assertTrue("All kinds that do have target",matches.contains(Shadow.SHADOW_KINDS[i])); + } + } + } + + public void testKindSetOfArgs() { + Pointcut p = getPointcut("args(..)"); + assertTrue("All kinds",p.couldMatchKinds().containsAll(Shadow.ALL_SHADOW_KINDS)); + // + @ + p = getPointcut("@args(..)"); + assertTrue("All kinds",p.couldMatchKinds().containsAll(Shadow.ALL_SHADOW_KINDS)); + } + + public void testKindSetOfAnnotation() { + Pointcut p = getPointcut("@annotation(@Foo)"); + assertTrue("All kinds",p.couldMatchKinds().containsAll(Shadow.ALL_SHADOW_KINDS)); + } + + public void testKindSetOfWithin() { + Pointcut p = getPointcut("within(*)"); + assertTrue("All kinds",p.couldMatchKinds().containsAll(Shadow.ALL_SHADOW_KINDS)); + // + @ + p = getPointcut("@within(@Foo)"); + assertTrue("All kinds",p.couldMatchKinds().containsAll(Shadow.ALL_SHADOW_KINDS)); + } + + public void testKindSetOfWithinCode() { + Pointcut p = getPointcut("withincode(* foo(..))"); + Set matches = p.couldMatchKinds(); + for (Iterator iter = matches.iterator(); iter.hasNext();) { + Shadow.Kind kind = (Shadow.Kind) iter.next(); + assertFalse("No kinds that are themselves enclosing", + (kind.isEnclosingKind() && kind != Shadow.ConstructorExecution && kind != Shadow.Initialization)); + } + for (int i = 0; i < Shadow.SHADOW_KINDS.length; i++) { + if (!Shadow.SHADOW_KINDS[i].isEnclosingKind()) { + assertTrue("All kinds that are not enclosing",matches.contains(Shadow.SHADOW_KINDS[i])); + } + } + assertTrue("Need cons-exe for inlined field inits",matches.contains(Shadow.ConstructorExecution)); + assertTrue("Need init for inlined field inits",matches.contains(Shadow.Initialization)); + // + @ + p = getPointcut("@withincode(@Foo)"); + matches = p.couldMatchKinds(); + for (Iterator iter = matches.iterator(); iter.hasNext();) { + Shadow.Kind kind = (Shadow.Kind) iter.next(); + assertFalse("No kinds that are themselves enclosing",kind.isEnclosingKind()); + } + for (int i = 0; i < Shadow.SHADOW_KINDS.length; i++) { + if (!Shadow.SHADOW_KINDS[i].isEnclosingKind()) { + assertTrue("All kinds that are not enclosing",matches.contains(Shadow.SHADOW_KINDS[i])); + } + } + } + + public void testKindSetOfIf() { + Pointcut p = new IfPointcut(null,0); + assertTrue("All kinds",p.couldMatchKinds().containsAll(Shadow.ALL_SHADOW_KINDS)); + p = IfPointcut.makeIfTruePointcut(Pointcut.CONCRETE); + assertTrue("All kinds",p.couldMatchKinds().containsAll(Shadow.ALL_SHADOW_KINDS)); + p = IfPointcut.makeIfFalsePointcut(Pointcut.CONCRETE); + assertTrue("Nothing",p.couldMatchKinds().isEmpty()); + } + + public void testKindSetOfCflow() { + Pointcut p = getPointcut("cflow(this(Foo))"); + assertTrue("All kinds",p.couldMatchKinds().containsAll(Shadow.ALL_SHADOW_KINDS)); + // [below] + p = getPointcut("cflowbelow(this(Foo))"); + assertTrue("All kinds",p.couldMatchKinds().containsAll(Shadow.ALL_SHADOW_KINDS)); + } + + public void testKindSetInNegation() { + Pointcut p = getPointcut("!execution(new(..))"); + assertTrue("All kinds",p.couldMatchKinds().containsAll(Shadow.ALL_SHADOW_KINDS)); + } + + public void testKindSetOfOr() { + Pointcut p = getPointcut("execution(new(..)) || get(* *)"); + Set matches = p.couldMatchKinds(); + assertEquals("2 kinds",2,matches.size()); + assertTrue("ConstructorExecution",matches.contains(Shadow.ConstructorExecution)); + assertTrue("FieldGet",matches.contains(Shadow.FieldGet)); + } + + public void testOrderingInAnd() { + Pointcut bigLongPC = getPointcut("cflow(this(Foo)) && @args(@X) && args(X) && @this(@Foo) && @target(@Boo) && this(Moo) && target(Boo) && @annotation(@Moo) && @withincode(@Boo) && withincode(new(..)) && set(* *)&& @within(@Foo) && within(Foo)"); + Pointcut rewritten = prw.rewrite(bigLongPC); + assertEquals("((((((((((((within(Foo) && @within(@Foo)) && set(* *)) && withincode(new(..))) && @withincode(@Boo)) && @annotation(@Moo)) && target(Boo)) && this(Moo)) && @target(@Boo)) && @this(@Foo)) && args(X)) && @args(@X)) && cflow(this(Foo)))",rewritten.toString()); + } + + public void testOrderingInSimpleOr() { + OrPointcut opc = (OrPointcut) getPointcut("execution(new(..)) || get(* *)"); + assertEquals("reordered","(get(* *) || execution(new(..)))",prw.rewrite(opc).toString()); + } + + public void testOrderingInNestedOrs() { + OrPointcut opc = (OrPointcut) getPointcut("(execution(new(..)) || get(* *)) || within(abc)"); + assertEquals("reordered","((within(abc) || get(* *)) || execution(new(..)))", + prw.rewrite(opc).toString()); + } + + public void testOrderingInOrsWithNestedAnds() { + OrPointcut opc = (OrPointcut) getPointcut("get(* *) || (execution(new(..)) && within(abc))"); + assertEquals("reordered","((within(abc) && execution(new(..))) || get(* *))", + prw.rewrite(opc).toString()); + } + + private Pointcut getPointcut(String s) { + return new PatternParser(s).parsePointcut(); + } + + /* + * @see TestCase#setUp() + */ + protected void setUp() throws Exception { + super.setUp(); + prw = new PointcutRewriter(); + } + +} diff --git a/weaver/testsrc/org/aspectj/weaver/patterns/PointcutTestCase.java b/weaver/testsrc/org/aspectj/weaver/patterns/PointcutTestCase.java index a640a07d5..039bf89d5 100644 --- a/weaver/testsrc/org/aspectj/weaver/patterns/PointcutTestCase.java +++ b/weaver/testsrc/org/aspectj/weaver/patterns/PointcutTestCase.java @@ -12,6 +12,7 @@ package org.aspectj.weaver.patterns; import java.io.DataOutputStream; import java.io.IOException; +import java.util.Set; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.Signature; @@ -30,11 +31,15 @@ public class PointcutTestCase extends TestCase { public void testMatchJP() { Pointcut p = new Pointcut() { + public Set couldMatchKinds() { + return null; + } + public FuzzyBoolean fastMatch(FastMatchInfo info) { return null; } - public FuzzyBoolean match(Shadow shadow) { + protected FuzzyBoolean matchInternal(Shadow shadow) { return null; } @@ -47,7 +52,7 @@ public class PointcutTestCase extends TestCase { return null; } - public Test findResidue(Shadow shadow, ExposedState state) { + protected Test findResidueInternal(Shadow shadow, ExposedState state) { return null; } |