All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.aspectj.weaver.patterns.PointcutRewriter Maven / Gradle / Ivy

There is a newer version: 1.9.22.1
Show newest version
/* *******************************************************************
 * Copyright (c) 2004 IBM Corporation.
 * All rights reserved. 
 * This program and the accompanying materials are made available 
 * under the terms of the Eclipse Public License v1.0 
 * which accompanies this distribution and is available at 
 * http://www.eclipse.org/legal/epl-v10.html 
 *  
 * ******************************************************************/
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;
import org.aspectj.weaver.patterns.Pointcut.MatchesNothingPointcut;

/**
 * @author colyer
 * 
 * Performs term rewriting for pointcut expressions.
 * 
 */
public class PointcutRewriter {

	private static final boolean WATCH_PROGRESS = false;
	
	/**
	 * Set forcerewrite if you want to override the checking for something already in DNF (useful for
	 * some testing)
	 * Repeated processing of something already in DNF is expensive (it ends up being done for
	 * every pointcut on every incremental compile) - so let's not do it if we don't have to.
	 * See pr113257
	 */
	public Pointcut rewrite(Pointcut pc,boolean forceRewrite) {
		Pointcut result = pc;//checkPC(result);
		if (forceRewrite || !isDNF(pc)) {
			if (WATCH_PROGRESS) System.out.println("Initial pointcut is        ==> " + format(pc));
			result = distributeNot(result);//checkPC(result);
			if (WATCH_PROGRESS) System.out.println("Distributing NOT gives     ==> " + format(result));
			result = pullUpDisjunctions(result);//checkPC(result);
			if (WATCH_PROGRESS) System.out.println("Pull up disjunctions gives ==> " + format(result));		
	    } else {
			if (WATCH_PROGRESS) System.out.println("Not distributing NOTs or pulling up disjunctions, already DNF ==> "+format(pc));
	    }
		result = simplifyAnds(result);  //checkPC(result);
		if (WATCH_PROGRESS) System.out.println("Simplifying ANDs gives     ==> " + format(result));
		result = removeNothings(result); //checkPC(result);
	    if (WATCH_PROGRESS) System.out.println("Removing nothings gives    ==> " + format(result));
		result = sortOrs(result); //checkPC(result);
		if (WATCH_PROGRESS) System.out.println("Sorting ORs gives          ==> " + format(result));
		return result;
	}
	
//	/**
//	 * Checks pointcuts - used for debugging.
//	 * - this variant checks if the context has been lost, since
//	 *   that can indicate an NPE will happen later reporting a message (pr162657).
//	 *   Not finished, but helped locate the problem ;)
//	 */
//	private void checkPC(Pointcut pc) {
//		if (isNot(pc)) {
//			NotPointcut npc = (NotPointcut)pc;
//			checkPC(npc.getNegatedPointcut());
//			if (npc.getSourceContext()==null) {
//				System.out.println("Lost context for "+npc);
//				throw new RuntimeException("Lost context");
//			}
//		} else if (isOr(pc)) {
//			OrPointcut opc = (OrPointcut)pc;
//			checkPC(opc.getLeft());
//			checkPC(opc.getRight());
//			if (opc.getSourceContext()==null) {
//				System.out.println("Lost context for "+opc);
//				throw new RuntimeException("Lost context");
//			}
//		} else if (isAnd(pc)) {
//			AndPointcut apc = (AndPointcut)pc;
//			checkPC(apc.getLeft());
//			checkPC(apc.getRight());
//			if (apc.getSourceContext()==null) {
//				System.out.println("Lost context for "+apc);
//				throw new RuntimeException("Lost context");
//			}
//		} else {
//			if (pc.getSourceContext()==null) {
//				System.out.println("Lost context for "+pc);
//				throw new RuntimeException("Lost context");		
//			}
//		}
//	}
				
	public Pointcut rewrite(Pointcut pc) {
		return rewrite(pc,false);
	}
	
	/**
	 * Check if a pointcut is in DNF - if it is then it should be lots of 'ORs' up the 
	 * top with 'ANDs' beneath them.
	 */
	private boolean isDNF(Pointcut pc) {
		return isDNFHelper(pc,true);
	}
	
	/**
	 * Helper function for determining DNFness.  Records when we have crossed the point of allowing ORs.
	 */
	private boolean isDNFHelper(Pointcut pc,boolean canStillHaveOrs) {
		if (isAnd(pc)) {
			AndPointcut ap = (AndPointcut)pc;
			return isDNFHelper(ap.getLeft(),false) && isDNFHelper(ap.getRight(),false);
		} else if (isOr(pc)) {
			if (!canStillHaveOrs) return false;
			OrPointcut op = (OrPointcut)pc;
			return isDNFHelper(op.getLeft(),true) && isDNFHelper(op.getRight(),true);
		} else if (isNot(pc)) {
			return isDNFHelper(((NotPointcut)pc).getNegatedPointcut(),canStillHaveOrs);
		} else {
			return true;
		}
	}
	
	/** 
	 * Allows formatting of the output pointcut for debugging...
	 */
	public static String format(Pointcut p) {
		String s = p.toString();
		// Regex param needs '(' and '*' changing to '.'
//		s = s.replaceAll("persingleton.pkg1.monitoring.ErrorMonitoring.","M");
//		s = s.replaceAll("args.BindingTypePattern.java.lang.Throwable, 0.","Z");
//		s = s.replaceAll("within.pkg1.monitoring.DoMonitorErrors+.","X");
//		s=s.replaceAll("within.pkg1.monitoring....","Y");
//		s=s.replaceAll("if.true.","N");
		return s;
	}
	
	
	
	// !!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(),npc.getStart())); 
				Pointcut newRight = distributeNot(new NotPointcut(apc.getRight(),npc.getStart()));
				return new OrPointcut(newLeft,newRight);
			} else if (isOr(notBody)) {
				// !(X || Y) => !X && !Y
				OrPointcut opc = (OrPointcut) notBody;
				Pointcut newLeft = distributeNot(new NotPointcut(opc.getLeft(),npc.getStart())); 
				Pointcut newRight = distributeNot(new NotPointcut(opc.getRight(),npc.getStart()));
				return new AndPointcut(newLeft,newRight);				
			} else {
				return new NotPointcut(notBody,npc.getStart());
			}
		} 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 pullUpDisjunctions(
							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 pullUpDisjunctions(
							new OrPointcut(
									new AndPointcut(left,rightLeft),
									new AndPointcut(left,rightRight))
							);
			}  else if (isOr(right) && isOr(left)) {
				// (A || B) && (C || D) => (A && C) || (A && D) || (B && C) || (B && D)
				Pointcut A = pullUpDisjunctions(((OrPointcut)left).getLeft());
				Pointcut B = pullUpDisjunctions(((OrPointcut)left).getRight());
				Pointcut C = pullUpDisjunctions(((OrPointcut)right).getLeft());
				Pointcut D = pullUpDisjunctions(((OrPointcut)right).getRight());
				Pointcut newLeft = new OrPointcut(
						 				new AndPointcut(A,C),
						 				new AndPointcut(A,D)
									);
				Pointcut newRight = new OrPointcut(
						               new AndPointcut(B,C),
						               new AndPointcut(B,D)
						           );				
				return pullUpDisjunctions(new OrPointcut(newLeft,newRight));
			}	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;
		}
	}
	
	/**
	 * Returns a NOTted form of the pointcut p - we cope with already NOTted pointcuts.
	 */
	public Pointcut not(Pointcut p) {
		if (isNot(p)) {
			return ((NotPointcut)p).getNegatedPointcut();
		}
		return new NotPointcut(p);
	}
	
	/**
	 * Passed an array of pointcuts, returns an AND tree with them in.
	 */
	public Pointcut createAndsFor(Pointcut[] ps) {
		if (ps.length==1) return ps[0]; // dumb case
		if (ps.length==2) { // recursion exit case
			return new AndPointcut(ps[0],ps[1]);
		}
		// otherwise ...
		Pointcut[] subset = new Pointcut[ps.length-1];
		for (int i = 1; i < ps.length; i++) {
			subset[i-1]=ps[i];
		}
		return new AndPointcut(ps[0],createAndsFor(subset));
	}

	
	// 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;
			Pointcut notBody = npc.getNegatedPointcut();
			if (isNot(notBody)) {
				// !!X => X
				return simplifyAnds(((NotPointcut)notBody).getNegatedPointcut());
			} else {
							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 it can't match anything, the whole AND can't match anything
			if (element.couldMatchKinds()==Shadow.NO_SHADOW_KINDS_BITS) return element;
		}
		if (apc.couldMatchKinds()==Shadow.NO_SHADOW_KINDS_BITS) 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;
	}
	
	/**
	 * Removes MATCHES_NOTHING pointcuts
	 */
	private Pointcut removeNothings(Pointcut pc) {
		if (isAnd(pc)) {
			AndPointcut apc = (AndPointcut)pc;
			Pointcut right = removeNothings(apc.getRight());
			Pointcut left  = removeNothings(apc.getLeft());
			if (left instanceof MatchesNothingPointcut || right instanceof MatchesNothingPointcut) return new MatchesNothingPointcut();
			return new AndPointcut(left,right);
		} else if (isOr(pc)) {
			OrPointcut opc = (OrPointcut)pc;
			Pointcut right = removeNothings(opc.getRight());
			Pointcut left  = removeNothings(opc.getLeft());
			if (left instanceof MatchesNothingPointcut 
					&& !(right instanceof MatchesNothingPointcut)) {
				return right;
			} else if (right instanceof MatchesNothingPointcut 
					&& !(left instanceof MatchesNothingPointcut)) {
				return left;
			} else if (!(left instanceof MatchesNothingPointcut)
					&& !(right instanceof MatchesNothingPointcut)) {
				return new OrPointcut(left,right);
			} else if (left instanceof MatchesNothingPointcut
					&& right instanceof MatchesNothingPointcut) {
				return new MatchesNothingPointcut();	
			}			
		}
		return pc;
	}
	
	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);
	}
	
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy