/* *******************************************************************
* 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();
}
}