Package org.aspectj.weaver.patterns

Source Code of org.aspectj.weaver.patterns.PointcutRewriter

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

/**
* Performs term rewriting for pointcut expressions.
*
* @author colyer
* @author clement
*/
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<Pointcut> nodes = new TreeSet<Pointcut>(new PointcutEvaluationExpenseComparator());
    collectAndNodes(apc, nodes);
    // look for A and !A, or IfFalse
    for (Iterator<Pointcut> iter = nodes.iterator(); iter.hasNext();) {
      Pointcut element = 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<Pointcut> iter = nodes.iterator();
    Pointcut result = iter.next();
    while (iter.hasNext()) {
      Pointcut right = iter.next();
      result = new AndPointcut(result, right);
    }
    return result;
  }

  private Pointcut sortOrs(Pointcut pc) {
    SortedSet<Pointcut> nodes = new TreeSet<Pointcut>(new PointcutEvaluationExpenseComparator());
    collectOrNodes(pc, nodes);
    // write out with cheapest on left
    Iterator<Pointcut> iter = nodes.iterator();
    Pointcut result = iter.next();
    while (iter.hasNext()) {
      Pointcut right = 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<Pointcut> 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<Pointcut> 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);
  }

}
TOP

Related Classes of org.aspectj.weaver.patterns.PointcutRewriter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.