Package org.aspectj.weaver

Source Code of org.aspectj.weaver.CrosscuttingMembers

/* *******************************************************************
* Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/epl-v10.html
* Contributors:
*     PARC     initial implementation
* ******************************************************************/
package org.aspectj.weaver;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.aspectj.weaver.AjAttribute.WeaverVersionInfo;
import org.aspectj.weaver.patterns.Declare;
import org.aspectj.weaver.patterns.DeclareAnnotation;
import org.aspectj.weaver.patterns.DeclareErrorOrWarning;
import org.aspectj.weaver.patterns.DeclareParents;
import org.aspectj.weaver.patterns.DeclarePrecedence;
import org.aspectj.weaver.patterns.DeclareSoft;
import org.aspectj.weaver.patterns.DeclareTypeErrorOrWarning;
import org.aspectj.weaver.patterns.PerClause;
import org.aspectj.weaver.patterns.Pointcut;
import org.aspectj.weaver.patterns.PointcutRewriter;

/**
* This holds on to all members that have an invasive effect outside of there own compilation unit. These members need to be all
* gathered up and in a world before any weaving can take place.
*
* They are also important in the compilation process and need to be gathered up before the inter-type declaration weaving stage
* (unsurprisingly).
*
* All members are concrete.
*
* @author Jim Hugunin
*/
public class CrosscuttingMembers {
  private final ResolvedType inAspect;
  private final World world;

  private PerClause perClause;

  private List<ShadowMunger> shadowMungers = new ArrayList<ShadowMunger>(4);
  private List<ConcreteTypeMunger> typeMungers = new ArrayList<ConcreteTypeMunger>(4);
  private List<ConcreteTypeMunger> lateTypeMungers = new ArrayList<ConcreteTypeMunger>(0);

  private Set<DeclareParents> declareParents = new HashSet<DeclareParents>();
  private Set<DeclareSoft> declareSofts = new HashSet<DeclareSoft>();
  private List<Declare> declareDominates = new ArrayList<Declare>(4);

  // These are like declare parents type mungers
  private Set<DeclareAnnotation> declareAnnotationsOnType = new HashSet<DeclareAnnotation>();
  private Set<DeclareAnnotation> declareAnnotationsOnField = new HashSet<DeclareAnnotation>();
  private Set<DeclareAnnotation> declareAnnotationsOnMethods = new HashSet<DeclareAnnotation>();
  // declareAnnotationsOnMethods includes constructors too

  private Set<DeclareTypeErrorOrWarning> declareTypeEow = new HashSet<DeclareTypeErrorOrWarning>();

  private boolean shouldConcretizeIfNeeded = true;

  public CrosscuttingMembers(ResolvedType inAspect, boolean shouldConcretizeIfNeeded) {
    this.inAspect = inAspect;
    world = inAspect.getWorld();
    this.shouldConcretizeIfNeeded = shouldConcretizeIfNeeded;
  }

  private final Hashtable<String, Object> cflowFields = new Hashtable<String, Object>();
  private final Hashtable<String, Object> cflowBelowFields = new Hashtable<String, Object>();

  // public void addConcreteShadowMungers(Collection c) {
  // shadowMungers.addAll(c);
  // }

  public void addConcreteShadowMunger(ShadowMunger m) {
    // assert m is concrete
    shadowMungers.add(m);
  }

  public void addShadowMungers(Collection<ShadowMunger> c) {
    for (ShadowMunger munger : c) {
      addShadowMunger(munger);
    }
  }

  private void addShadowMunger(ShadowMunger m) {
    if (inAspect.isAbstract()) {
      return; // mungers for abstract aspects are not added
    }
    addConcreteShadowMunger(m.concretize(inAspect, world, perClause));
  }

  public void addTypeMungers(Collection<ConcreteTypeMunger> c) {
    typeMungers.addAll(c);
  }

  public void addTypeMunger(ConcreteTypeMunger m) {
    if (m == null) {
      throw new Error("FIXME AV - should not happen or what ?");// return;
    }
    typeMungers.add(m);
  }

  public void addLateTypeMungers(Collection<ConcreteTypeMunger> c) {
    lateTypeMungers.addAll(c);
  }

  public void addLateTypeMunger(ConcreteTypeMunger m) {
    lateTypeMungers.add(m);
  }

  public void addDeclares(Collection<Declare> declares) {
    for (Declare declare : declares) {
      addDeclare(declare);
    }
  }

  public void addDeclare(Declare declare) {
    // this is not extensible, oh well
    if (declare instanceof DeclareErrorOrWarning) {
      ShadowMunger m = new Checker((DeclareErrorOrWarning) declare);
      m.setDeclaringType(declare.getDeclaringType());
      addShadowMunger(m);
    } else if (declare instanceof DeclarePrecedence) {
      declareDominates.add(declare);
    } else if (declare instanceof DeclareParents) {
      DeclareParents dp = (DeclareParents) declare;
      exposeTypes(dp.getParents().getExactTypes());
      declareParents.add(dp);
    } else if (declare instanceof DeclareSoft) {
      DeclareSoft d = (DeclareSoft) declare;
      // Ordered so that during concretization we can check the related
      // munger
      ShadowMunger m = Advice.makeSoftener(world, d.getPointcut(), d.getException(), inAspect, d);
      m.setDeclaringType(d.getDeclaringType());
      Pointcut concretePointcut = d.getPointcut().concretize(inAspect, d.getDeclaringType(), 0, m);
      m.pointcut = concretePointcut;
      declareSofts.add(new DeclareSoft(d.getException(), concretePointcut));
      addConcreteShadowMunger(m);
    } else if (declare instanceof DeclareAnnotation) {
      // FIXME asc perf Possible Improvement. Investigate why this is
      // called twice in a weave ?
      DeclareAnnotation da = (DeclareAnnotation) declare;
      if (da.getAspect() == null) {
        da.setAspect(inAspect);
      }
      if (da.isDeclareAtType()) {
        declareAnnotationsOnType.add(da);
      } else if (da.isDeclareAtField()) {
        declareAnnotationsOnField.add(da);
      } else if (da.isDeclareAtMethod() || da.isDeclareAtConstuctor()) {
        declareAnnotationsOnMethods.add(da);
      }
    } else if (declare instanceof DeclareTypeErrorOrWarning) {
      declareTypeEow.add((DeclareTypeErrorOrWarning) declare);
    } else {
      throw new RuntimeException("unimplemented");
    }
  }

  public void exposeTypes(List<UnresolvedType> typesToExpose) {
    for (UnresolvedType typeToExpose : typesToExpose) {
      exposeType(typeToExpose);
    }
  }

  public void exposeType(UnresolvedType typeToExpose) {
    if (ResolvedType.isMissing(typeToExpose)) {
      return;
    }
    if (typeToExpose.isParameterizedType() || typeToExpose.isRawType()) {
      if (typeToExpose instanceof ResolvedType) {
        typeToExpose = ((ResolvedType) typeToExpose).getGenericType();
      } else {
        typeToExpose = UnresolvedType.forSignature(typeToExpose.getErasureSignature());
      }
    }
    // Check we haven't already got a munger for this:
    String signatureToLookFor = typeToExpose.getSignature();
    for (Iterator<ConcreteTypeMunger> iterator = typeMungers.iterator(); iterator.hasNext();) {
      ConcreteTypeMunger cTM = iterator.next();
      ResolvedTypeMunger rTM = cTM.getMunger();
      if (rTM != null && rTM instanceof ExposeTypeMunger) {
        String exposedType = ((ExposeTypeMunger) rTM).getExposedTypeSignature();
        if (exposedType.equals(signatureToLookFor)) {
          return; // dont need to bother
        }
      }
    }
    addTypeMunger(world.getWeavingSupport().concreteTypeMunger(new ExposeTypeMunger(typeToExpose), inAspect));
    // ResolvedMember member = new ResolvedMemberImpl(
    // Member.STATIC_INITIALIZATION, typeToExpose, 0, ResolvedType.VOID,
    // "<clinit>", UnresolvedType.NONE);
    // addTypeMunger(world.concreteTypeMunger(
    // new PrivilegedAccessMunger(member), inAspect));
  }

  public void addPrivilegedAccesses(Collection<ResolvedMember> accessedMembers) {
    int version = inAspect.getCompilerVersion();
    for (ResolvedMember member : accessedMembers) {
      // Looking it up ensures we get the annotations - the accessedMembers are just retrieved from the attribute and
      // don't have that information
      ResolvedMember resolvedMember = world.resolve(member);

      // pr333469
      // If the member is for an ITD (e.g. serialVersionUID) then during resolution we may resolve it on
      // a supertype because it doesn't yet exist on the target.
      // For example: MyList extends ArrayList<String> and the ITD is on MyList - after resolution it may be:
      // ArrayList<String>.serialVersionUID, we need to avoid that happening

      if (resolvedMember == null) {
        // can happen for ITDs - are there many privileged access ITDs??
        resolvedMember = member;
        if (resolvedMember.hasBackingGenericMember()) {
          resolvedMember = resolvedMember.getBackingGenericMember();
        }
      } else {
        UnresolvedType unresolvedDeclaringType = member.getDeclaringType().getRawType();
        UnresolvedType resolvedDeclaringType = resolvedMember.getDeclaringType().getRawType();
        if (!unresolvedDeclaringType.equals(resolvedDeclaringType)) {
          resolvedMember = member;
        }
      }
      PrivilegedAccessMunger privilegedAccessMunger = new PrivilegedAccessMunger(resolvedMember,
          version >= WeaverVersionInfo.WEAVER_VERSION_AJ169);
      ConcreteTypeMunger concreteTypeMunger = world.getWeavingSupport().concreteTypeMunger(privilegedAccessMunger, inAspect);
      addTypeMunger(concreteTypeMunger);
    }
  }

  public Collection<ShadowMunger> getCflowEntries() {
    List<ShadowMunger> ret = new ArrayList<ShadowMunger>();
    for (ShadowMunger m : shadowMungers) {
      if (m instanceof Advice) {
        Advice a = (Advice) m;
        if (a.getKind().isCflow()) {
          ret.add(a);
        }
      }
    }
    return ret;
  }

  /**
   * Updates the records if something has changed. This is called at most twice, firstly whilst collecting ITDs and declares. At
   * this point the CrosscuttingMembers we're comparing ourselves with doesn't know about shadowmungers. Therefore a straight
   * comparison with the existing list of shadowmungers would return that something has changed even though it might not have, so
   * in this first round we ignore the shadowMungers. The second time this is called is whilst we're preparing to weave. At this
   * point we know everything in the system and so we're able to compare the shadowMunger list. (see bug 129163)
   *
   * @param other
   * @param careAboutShadowMungers
   * @return true if something has changed since the last time this method was called, false otherwise
   */
  public boolean replaceWith(CrosscuttingMembers other, boolean careAboutShadowMungers) {
    boolean changed = false;

    if (careAboutShadowMungers) {
      if (perClause == null || !perClause.equals(other.perClause)) {
        changed = true;
        perClause = other.perClause;
      }
    }

    // XXX all of the below should be set equality rather than list equality
    // System.err.println("old: " + shadowMungers + " new: " +
    // other.shadowMungers);

    if (careAboutShadowMungers) {
      // bug 129163: use set equality rather than list equality
      Set<ShadowMunger> theseShadowMungers = new HashSet<ShadowMunger>();
      Set<ShadowMunger> theseInlinedAroundMungers = new HashSet<ShadowMunger>();
      for (ShadowMunger munger : shadowMungers) {
        if (munger instanceof Advice) {
          Advice adviceMunger = (Advice) munger;
          // bug 154054: if we're around advice that has been inlined
          // then we need to do more checking than existing equals
          // methods allow
          if (!world.isXnoInline() && adviceMunger.getKind().equals(AdviceKind.Around)) {
            theseInlinedAroundMungers.add(adviceMunger);
          } else {
            theseShadowMungers.add(adviceMunger);
          }
        } else {
          theseShadowMungers.add(munger);
        }
      }
      Set<ShadowMunger> tempSet = new HashSet<ShadowMunger>();
      tempSet.addAll(other.shadowMungers);
      Set<ShadowMunger> otherShadowMungers = new HashSet<ShadowMunger>();
      Set<ShadowMunger> otherInlinedAroundMungers = new HashSet<ShadowMunger>();
      for (ShadowMunger munger : tempSet) {
        if (munger instanceof Advice) {
          Advice adviceMunger = (Advice) munger;
          // bug 154054: if we're around advice that has been inlined
          // then we need to do more checking than existing equals
          // methods allow
          if (!world.isXnoInline() && adviceMunger.getKind().equals(AdviceKind.Around)) {
            otherInlinedAroundMungers.add(rewritePointcutInMunger(adviceMunger));
          } else {
            otherShadowMungers.add(rewritePointcutInMunger(adviceMunger));
          }
        } else {
          otherShadowMungers.add(rewritePointcutInMunger(munger));
        }
      }
      if (!theseShadowMungers.equals(otherShadowMungers)) {
        changed = true;
      }
      if (!equivalent(theseInlinedAroundMungers, otherInlinedAroundMungers)) {
        changed = true;
      }

      // bug 158573 - if there are no changes then preserve whether
      // or not a particular shadowMunger has matched something.
      if (!changed) {
        for (ShadowMunger munger : shadowMungers) {
          int i = other.shadowMungers.indexOf(munger);
          ShadowMunger otherMunger = other.shadowMungers.get(i);
          if (munger instanceof Advice) {
            ((Advice) otherMunger).setHasMatchedSomething(((Advice) munger).hasMatchedSomething());
          }
        }
      }
      // replace the existing list of shadowmungers with the
      // new ones in case anything like the sourcelocation has
      // changed, however, don't want this flagged as a change
      // which will force a full build - bug 134541
      shadowMungers = other.shadowMungers;
    }

    // bug 129163: use set equality rather than list equality and
    // if we dont care about shadow mungers then ignore those
    // typeMungers which are created to help with the implementation
    // of shadowMungers
    Set<Object> theseTypeMungers = new HashSet<Object>();
    Set<Object> otherTypeMungers = new HashSet<Object>();
    if (!careAboutShadowMungers) {
      for (Iterator<ConcreteTypeMunger> iter = typeMungers.iterator(); iter.hasNext();) {
        Object o = iter.next();
        if (o instanceof ConcreteTypeMunger) {
          ConcreteTypeMunger typeMunger = (ConcreteTypeMunger) o;
          if (!typeMunger.existsToSupportShadowMunging()) {
            theseTypeMungers.add(typeMunger);
          }
        } else {
          theseTypeMungers.add(o);
        }
      }

      for (Iterator<ConcreteTypeMunger> iter = other.typeMungers.iterator(); iter.hasNext();) {
        Object o = iter.next();
        if (o instanceof ConcreteTypeMunger) {
          ConcreteTypeMunger typeMunger = (ConcreteTypeMunger) o;
          if (!typeMunger.existsToSupportShadowMunging()) {
            otherTypeMungers.add(typeMunger);
          }
        } else {
          otherTypeMungers.add(o);
        }
      }
    } else {
      theseTypeMungers.addAll(typeMungers);
      otherTypeMungers.addAll(other.typeMungers);
    }

    // initial go at equivalence logic rather than set compare (see
    // pr133532)
    if (theseTypeMungers.size() != otherTypeMungers.size()) {
      changed = true;
      typeMungers = other.typeMungers;
    } else {
      boolean shouldOverwriteThis = false;
      boolean foundInequality = false;
      for (Iterator<Object> iter = theseTypeMungers.iterator(); iter.hasNext() && !foundInequality;) {
        Object thisOne = iter.next();
        boolean foundInOtherSet = false;
        for (Object otherOne : otherTypeMungers) {
          if (thisOne instanceof ConcreteTypeMunger) {
            if (((ConcreteTypeMunger) thisOne).shouldOverwrite()) {
              shouldOverwriteThis = true;
            }
          }
          if (thisOne instanceof ConcreteTypeMunger && otherOne instanceof ConcreteTypeMunger) {
            if (((ConcreteTypeMunger) thisOne).equivalentTo(otherOne)) {
              foundInOtherSet = true;
            } else if (thisOne.equals(otherOne)) {
              foundInOtherSet = true;
            }
          } else {
            if (thisOne.equals(otherOne)) {
              foundInOtherSet = true;
            }
          }
        }
        if (!foundInOtherSet) {
          foundInequality = true;
        }
      }
      if (foundInequality) {
        // System.out.println("type munger change");
        changed = true;
      }
      if (shouldOverwriteThis) {
        typeMungers = other.typeMungers;
      }
    }
    // if (!theseTypeMungers.equals(otherTypeMungers)) {
    // changed = true;
    // typeMungers = other.typeMungers;
    // }

    if (!lateTypeMungers.equals(other.lateTypeMungers)) {
      changed = true;
      lateTypeMungers = other.lateTypeMungers;
    }

    if (!declareDominates.equals(other.declareDominates)) {
      changed = true;
      declareDominates = other.declareDominates;
    }

    if (!declareParents.equals(other.declareParents)) {
      // Are the differences just because of a mixin? These are not created until weave time so should be gotten rid of for
      // the up front comparison
      if (!careAboutShadowMungers) {
        // this means we are in front end compilation and if the differences are purely mixin parents, we can continue OK
        Set<DeclareParents> trimmedThis = new HashSet<DeclareParents>();
        for (Iterator<DeclareParents> iterator = declareParents.iterator(); iterator.hasNext();) {
          DeclareParents decp = iterator.next();
          if (!decp.isMixin()) {
            trimmedThis.add(decp);
          }
        }
        Set<DeclareParents> trimmedOther = new HashSet<DeclareParents>();
        for (Iterator<DeclareParents> iterator = other.declareParents.iterator(); iterator.hasNext();) {
          DeclareParents decp = iterator.next();
          if (!decp.isMixin()) {
            trimmedOther.add(decp);
          }
        }
        if (!trimmedThis.equals(trimmedOther)) {
          changed = true;
          declareParents = other.declareParents;
        }
      } else {
        changed = true;
        declareParents = other.declareParents;
      }
    }

    if (!declareSofts.equals(other.declareSofts)) {
      changed = true;
      declareSofts = other.declareSofts;
    }

    // DECAT for when attempting to replace an aspect
    if (!declareAnnotationsOnType.equals(other.declareAnnotationsOnType)) {
      changed = true;
      declareAnnotationsOnType = other.declareAnnotationsOnType;
    }

    if (!declareAnnotationsOnField.equals(other.declareAnnotationsOnField)) {
      changed = true;
      declareAnnotationsOnField = other.declareAnnotationsOnField;
    }

    if (!declareAnnotationsOnMethods.equals(other.declareAnnotationsOnMethods)) {
      changed = true;
      declareAnnotationsOnMethods = other.declareAnnotationsOnMethods;
    }
    if (!declareTypeEow.equals(other.declareTypeEow)) {
      changed = true;
      declareTypeEow = other.declareTypeEow;
    }

    return changed;
  }

  private boolean equivalent(Set<ShadowMunger> theseInlinedAroundMungers, Set<ShadowMunger> otherInlinedAroundMungers) {
    if (theseInlinedAroundMungers.size() != otherInlinedAroundMungers.size()) {
      return false;
    }
    for (Iterator<ShadowMunger> iter = theseInlinedAroundMungers.iterator(); iter.hasNext();) {
      Advice thisAdvice = (Advice) iter.next();
      boolean foundIt = false;
      for (Iterator<ShadowMunger> iterator = otherInlinedAroundMungers.iterator(); iterator.hasNext();) {
        Advice otherAdvice = (Advice) iterator.next();
        if (thisAdvice.equals(otherAdvice)) {
          if (thisAdvice.getSignature() instanceof ResolvedMemberImpl) {
            if (((ResolvedMemberImpl) thisAdvice.getSignature()).isEquivalentTo(otherAdvice.getSignature())) {
              foundIt = true;
              continue;
            }
          }
          return false;
        }
      }
      if (!foundIt) {
        return false;
      }
    }
    return true;
  }

  private ShadowMunger rewritePointcutInMunger(ShadowMunger munger) {
    PointcutRewriter pr = new PointcutRewriter();
    Pointcut p = munger.getPointcut();
    Pointcut newP = pr.rewrite(p);
    if (p.m_ignoreUnboundBindingForNames.length != 0) {
      // *sigh* dirty fix for dirty hacky implementation pr149305
      newP.m_ignoreUnboundBindingForNames = p.m_ignoreUnboundBindingForNames;
    }
    munger.setPointcut(newP);
    return munger;
  }

  public void setPerClause(PerClause perClause) {
    if (shouldConcretizeIfNeeded) {
      this.perClause = perClause.concretize(inAspect);
    } else {
      this.perClause = perClause;
    }
  }

  public List<Declare> getDeclareDominates() {
    return declareDominates;
  }

  public Collection<DeclareParents> getDeclareParents() {
    return declareParents;
  }

  public Collection<DeclareSoft> getDeclareSofts() {
    return declareSofts;
  }

  public List<ShadowMunger> getShadowMungers() {
    return shadowMungers;
  }

  public List<ConcreteTypeMunger> getTypeMungers() {
    return typeMungers;
  }

  public List<ConcreteTypeMunger> getLateTypeMungers() {
    return lateTypeMungers;
  }

  public Collection<DeclareAnnotation> getDeclareAnnotationOnTypes() {
    return declareAnnotationsOnType;
  }

  public Collection<DeclareAnnotation> getDeclareAnnotationOnFields() {
    return declareAnnotationsOnField;
  }

  /**
   * includes declare @method and @constructor
   */
  public Collection<DeclareAnnotation> getDeclareAnnotationOnMethods() {
    return declareAnnotationsOnMethods;
  }

  public Collection<DeclareTypeErrorOrWarning> getDeclareTypeErrorOrWarning() {
    return declareTypeEow;
  }

  public Map<String, Object> getCflowBelowFields() {
    return cflowBelowFields;
  }

  public Map<String, Object> getCflowFields() {
    return cflowFields;
  }

  public void clearCaches() {
    cflowFields.clear();
    cflowBelowFields.clear();
  }

}
TOP

Related Classes of org.aspectj.weaver.CrosscuttingMembers

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.