org.aspectj.weaver.CrosscuttingMembers Maven / Gradle / Ivy
The newest version!
/* *******************************************************************
* 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.LinkedHashSet;
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 shadowMungers = new ArrayList(4);
private List typeMungers = new ArrayList(4);
private List lateTypeMungers = new ArrayList(0);
private Set declareParents = new HashSet();
private Set declareSofts = new HashSet();
private List declareDominates = new ArrayList(4);
// These are like declare parents type mungers
private Set declareAnnotationsOnType = new LinkedHashSet();
private Set declareAnnotationsOnField = new LinkedHashSet();
private Set declareAnnotationsOnMethods = new LinkedHashSet();
// declareAnnotationsOnMethods includes constructors too
private Set declareTypeEow = new HashSet();
private boolean shouldConcretizeIfNeeded = true;
public CrosscuttingMembers(ResolvedType inAspect, boolean shouldConcretizeIfNeeded) {
this.inAspect = inAspect;
world = inAspect.getWorld();
this.shouldConcretizeIfNeeded = shouldConcretizeIfNeeded;
}
private final Hashtable cflowFields = new Hashtable();
private final Hashtable cflowBelowFields = new Hashtable();
// public void addConcreteShadowMungers(Collection c) {
// shadowMungers.addAll(c);
// }
public void addConcreteShadowMunger(ShadowMunger m) {
// assert m is concrete
shadowMungers.add(m);
}
public void addShadowMungers(Collection 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 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 c) {
lateTypeMungers.addAll(c);
}
public void addLateTypeMunger(ConcreteTypeMunger m) {
lateTypeMungers.add(m);
}
public void addDeclares(Collection 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 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 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, UnresolvedType.VOID,
// "", UnresolvedType.NONE);
// addTypeMunger(world.concreteTypeMunger(
// new PrivilegedAccessMunger(member), inAspect));
}
public void addPrivilegedAccesses(Collection 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 and the ITD is on MyList - after resolution it may be:
// ArrayList.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 getCflowEntries() {
List ret = new ArrayList();
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 theseShadowMungers = new HashSet();
Set theseInlinedAroundMungers = new HashSet();
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 tempSet = new HashSet();
tempSet.addAll(other.shadowMungers);
Set otherShadowMungers = new HashSet();
Set otherInlinedAroundMungers = new HashSet();
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