/* ******************************************************************* * Copyright (c) 2002-2009 Contributors * 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.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.aspectj.bridge.IMessage; import org.aspectj.bridge.MessageUtil; import org.aspectj.weaver.patterns.Declare; import org.aspectj.weaver.patterns.DeclareAnnotation; import org.aspectj.weaver.patterns.DeclareParents; import org.aspectj.weaver.patterns.DeclareSoft; import org.aspectj.weaver.patterns.DeclareTypeErrorOrWarning; import org.aspectj.weaver.patterns.IVerificationRequired; /** * This holds on to all CrosscuttingMembers for a world. It handles management of change. * * @author Jim Hugunin * @author Andy Clement */ public class CrosscuttingMembersSet { private transient World world; // FIXME AV - ? we may need a sequencedHashMap there to ensure source based precedence for @AJ advice private final Map members = new HashMap<>(); // List of things to be verified once the type system is 'complete' private transient List verificationList = null; private List shadowMungers = null; private List typeMungers = null; private List lateTypeMungers = null; private List declareSofts = null; private List declareParents = null; private List declareAnnotationOnTypes = null; private List declareAnnotationOnFields = null; private List declareAnnotationOnMethods = null; // includes constructors private List declareTypeEows = null; private List declareDominates = null; private boolean changedSinceLastReset = false; public CrosscuttingMembersSet(World world) { this.world = world; } public boolean addOrReplaceAspect(ResolvedType aspectType) { return addOrReplaceAspect(aspectType, true); } /** * Check if any parent aspects of the supplied aspect have unresolved dependencies (and so * should cause this aspect to be turned off). * @param aspectType the aspect whose parents should be checked * @return true if this aspect should be excluded because of a parents' missing dependencies */ private boolean excludeDueToParentAspectHavingUnresolvedDependency(ResolvedType aspectType) { ResolvedType parent = aspectType.getSuperclass(); boolean excludeDueToParent = false; while (parent != null) { if (parent.isAspect() && parent.isAbstract() && world.hasUnsatisfiedDependency(parent)) { if (!world.getMessageHandler().isIgnoring(IMessage.INFO)) { world.getMessageHandler().handleMessage( MessageUtil.info("deactivating aspect '" + aspectType.getName() + "' as the parent aspect '"+parent.getName()+ "' has unsatisfied dependencies")); } excludeDueToParent = true; } parent = parent.getSuperclass(); } return excludeDueToParent; } /** * @return whether or not that was a change to the global signature XXX for efficiency we will need a richer representation than * this */ public boolean addOrReplaceAspect(ResolvedType aspectType, boolean inWeavingPhase) { if (!world.isAspectIncluded(aspectType)) { return false; } if (world.hasUnsatisfiedDependency(aspectType)) { return false; } // Abstract super aspects might have unsatisfied dependencies if (excludeDueToParentAspectHavingUnresolvedDependency(aspectType)) { return false; } boolean change = false; CrosscuttingMembers xcut = members.get(aspectType); if (xcut == null) { members.put(aspectType, aspectType.collectCrosscuttingMembers(inWeavingPhase)); clearCaches(); change = true; } else { if (xcut.replaceWith(aspectType.collectCrosscuttingMembers(inWeavingPhase), inWeavingPhase)) { clearCaches(); change = true; } else { if (inWeavingPhase) { // bug 134541 - even though we haven't changed we may have updated the // sourcelocation for the shadowMunger which we need to pick up shadowMungers = null; } change = false; } } if (aspectType.isAbstract()) { // we might have sub-aspects that need to re-collect their crosscutting members from us boolean ancestorChange = addOrReplaceDescendantsOf(aspectType, inWeavingPhase); change = change || ancestorChange; } changedSinceLastReset = changedSinceLastReset || change; return change; } private boolean addOrReplaceDescendantsOf(ResolvedType aspectType, boolean inWeavePhase) { // System.err.println("Looking at descendants of "+aspectType.getName()); Set knownAspects = members.keySet(); Set toBeReplaced = new HashSet<>(); for (ResolvedType candidateDescendant : knownAspects) { // allowMissing = true - if something is missing, it really probably is not a descendant if ((candidateDescendant != aspectType) && (aspectType.isAssignableFrom(candidateDescendant, true))) { toBeReplaced.add(candidateDescendant); } } boolean change = false; for (ResolvedType next : toBeReplaced) { boolean thisChange = addOrReplaceAspect(next, inWeavePhase); change = change || thisChange; } return change; } public void addAdviceLikeDeclares(ResolvedType aspectType) { if (!members.containsKey(aspectType)) { return; } CrosscuttingMembers xcut = members.get(aspectType); xcut.addDeclares(aspectType.collectDeclares(true)); } public boolean deleteAspect(UnresolvedType aspectType) { boolean isAspect = members.remove(aspectType) != null; clearCaches(); return isAspect; } public boolean containsAspect(UnresolvedType aspectType) { return members.containsKey(aspectType); } // XXX only for testing public void addFixedCrosscuttingMembers(ResolvedType aspectType) { members.put(aspectType, aspectType.crosscuttingMembers); clearCaches(); } private void clearCaches() { shadowMungers = null; typeMungers = null; lateTypeMungers = null; declareSofts = null; declareParents = null; declareAnnotationOnFields = null; declareAnnotationOnMethods = null; declareAnnotationOnTypes = null; declareDominates = null; } public List getShadowMungers() { if (shadowMungers == null) { List ret = new ArrayList<>(); for (CrosscuttingMembers crosscuttingMembers : members.values()) { ret.addAll(crosscuttingMembers.getShadowMungers()); } shadowMungers = ret; } return shadowMungers; } public List getTypeMungers() { if (typeMungers == null) { List ret = new ArrayList<>(); for (CrosscuttingMembers xmembers : members.values()) { // With 1.6.9 there is a change that enables use of more optimal accessors (accessors for private fields). // Here is where we determine if two aspects are asking for access to the same field. If they are // and // In the new style multiple aspects can share the same privileged accessors, so here we check if // two aspects are asking for access to the same field. If they are then we don't add a duplicate // accessor. for (ConcreteTypeMunger mungerToAdd : xmembers.getTypeMungers()) { ResolvedTypeMunger resolvedMungerToAdd = mungerToAdd.getMunger(); if (isNewStylePrivilegedAccessMunger(resolvedMungerToAdd)) { String newFieldName = resolvedMungerToAdd.getSignature().getName(); boolean alreadyExists = false; for (ConcreteTypeMunger existingMunger : ret) { ResolvedTypeMunger existing = existingMunger.getMunger(); if (isNewStylePrivilegedAccessMunger(existing)) { String existingFieldName = existing.getSignature().getName(); if (existingFieldName.equals(newFieldName) && existing.getSignature().getDeclaringType().equals( resolvedMungerToAdd.getSignature().getDeclaringType())) { alreadyExists = true; break; } } } if (!alreadyExists) { ret.add(mungerToAdd); } } else { ret.add(mungerToAdd); } } } typeMungers = ret; } return typeMungers; } /** * Retrieve a subset of all known mungers, those of a specific kind. * * @param kind the kind of munger requested * @return a list of those mungers (list is empty if none found) */ public List getTypeMungersOfKind(ResolvedTypeMunger.Kind kind) { List collected = null; for (ConcreteTypeMunger typeMunger : typeMungers) { if (typeMunger.getMunger() != null && typeMunger.getMunger().getKind() == kind) { if (collected == null) { collected = new ArrayList<>(); } collected.add(typeMunger); } } if (collected == null) { return Collections.emptyList(); } else { return collected; } } /** * Determine if the type munger is: (1) for privileged access (2) for a normally non visible field (3) is from an aspect wanting * 'old style' (ie. long) accessor names */ private boolean isNewStylePrivilegedAccessMunger(ResolvedTypeMunger typeMunger) { boolean b = (typeMunger != null && typeMunger.getKind() == ResolvedTypeMunger.PrivilegedAccess && typeMunger.getSignature() .getKind() == Member.FIELD); if (!b) { return b; } PrivilegedAccessMunger privAccessMunger = (PrivilegedAccessMunger) typeMunger; return privAccessMunger.shortSyntax; } public List getLateTypeMungers() { if (lateTypeMungers == null) { List ret = new ArrayList<>(); for (CrosscuttingMembers crosscuttingMembers : members.values()) { ret.addAll(crosscuttingMembers.getLateTypeMungers()); } lateTypeMungers = ret; } return lateTypeMungers; } public List getDeclareSofts() { if (declareSofts == null) { Set ret = new HashSet<>(); for (CrosscuttingMembers crosscuttingMembers : members.values()) { ret.addAll(crosscuttingMembers.getDeclareSofts()); } declareSofts = new ArrayList<>(); declareSofts.addAll(ret); } return declareSofts; } public List getDeclareParents() { if (declareParents == null) { Set ret = new HashSet<>(); for (CrosscuttingMembers crosscuttingMembers : members.values()) { ret.addAll(crosscuttingMembers.getDeclareParents()); } declareParents = new ArrayList<>(); declareParents.addAll(ret); } return declareParents; } /** * @return an amalgamation of the declare @type statements. */ public List getDeclareAnnotationOnTypes() { if (declareAnnotationOnTypes == null) { Set ret = new LinkedHashSet<>(); for (CrosscuttingMembers crosscuttingMembers : members.values()) { ret.addAll(crosscuttingMembers.getDeclareAnnotationOnTypes()); } declareAnnotationOnTypes = new ArrayList<>(); declareAnnotationOnTypes.addAll(ret); } return declareAnnotationOnTypes; } /** * @return an amalgamation of the declare @field statements. */ public List getDeclareAnnotationOnFields() { if (declareAnnotationOnFields == null) { Set ret = new LinkedHashSet<>(); for (CrosscuttingMembers crosscuttingMembers : members.values()) { ret.addAll(crosscuttingMembers.getDeclareAnnotationOnFields()); } declareAnnotationOnFields = new ArrayList<>(); declareAnnotationOnFields.addAll(ret); } return declareAnnotationOnFields; } /** * @return an amalgamation of the declare @method/@constructor statements. */ public List getDeclareAnnotationOnMethods() { if (declareAnnotationOnMethods == null) { Set ret = new LinkedHashSet<>(); for (CrosscuttingMembers crosscuttingMembers : members.values()) { ret.addAll(crosscuttingMembers.getDeclareAnnotationOnMethods()); } declareAnnotationOnMethods = new ArrayList<>(); declareAnnotationOnMethods.addAll(ret); // world.sortDeclareAnnotations(declareAnnotationOnMethods); } return declareAnnotationOnMethods; } /** * Return an amalgamation of the declare type eow statements */ public List getDeclareTypeEows() { if (declareTypeEows == null) { Set ret = new HashSet<>(); for (CrosscuttingMembers crosscuttingMembers : members.values()) { ret.addAll(crosscuttingMembers.getDeclareTypeErrorOrWarning()); } declareTypeEows = new ArrayList<>(); declareTypeEows.addAll(ret); } return declareTypeEows; } public List getDeclareDominates() { if (declareDominates == null) { List ret = new ArrayList<>(); for (CrosscuttingMembers crosscuttingMembers : members.values()) { ret.addAll(crosscuttingMembers.getDeclareDominates()); } declareDominates = ret; } return declareDominates; } public ResolvedType findAspectDeclaringParents(DeclareParents p) { Set keys = this.members.keySet(); for (ResolvedType element : keys) { for (DeclareParents dp : members.get(element).getDeclareParents()) { if (dp.equals(p)) { return element; } } } return null; } public void reset() { verificationList = null; changedSinceLastReset = false; } public boolean hasChangedSinceLastReset() { return changedSinceLastReset; } /** * Record something that needs verifying when we believe the type system is complete. Used for things that can't be verified as * we go along - for example some recursive type variable references (pr133307) */ public void recordNecessaryCheck(IVerificationRequired verification) { if (verificationList == null) { verificationList = new ArrayList<>(); } verificationList.add(verification); } /** * Called when type bindings are complete - calls all registered verification objects in turn. */ public void verify() { if (verificationList == null) { return; } for (IVerificationRequired element : verificationList) { element.verify(); } verificationList = null; } public int serializationVersion = 1; public void write(CompressingDataOutputStream stream) throws IOException { // stream.writeInt(serializationVersion); stream.writeInt(shadowMungers.size()); for (ShadowMunger shadowMunger : shadowMungers) { shadowMunger.write(stream); } // // private List /* ShadowMunger */shadowMungers = null; // // private List typeMungers = null; // // private List lateTypeMungers = null; // // private List declareSofts = null; // // private List declareParents = null; // // private List declareAnnotationOnTypes = null; // // private List declareAnnotationOnFields = null; // // private List declareAnnotationOnMethods = null; // includes constructors // // private List declareDominates = null; // // private boolean changedSinceLastReset = false; // } }