/* *******************************************************************
 * 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.io.DataInputStream;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.ISourceLocation;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.util.PartialOrder;
import org.aspectj.util.TypeSafeEnum;
import org.aspectj.weaver.ast.Var;

/*
 * The superclass of anything representing a the shadow of a join point.  A shadow represents
 * some bit of code, and encompasses both entry and exit from that code.  All shadows have a kind
 * and a signature.
 */

public abstract class Shadow {

	// every Shadow has a unique id, doesn't matter if it wraps...
	private static int nextShadowID = 100; // easier to spot than zero. // OPTIMIZE is this a bug? static?

	private final Kind kind;
	private final Member signature;
	private Member matchingSignature;
	private ResolvedMember resolvedSignature;
	protected final Shadow enclosingShadow;
	protected List<ShadowMunger> mungers = Collections.emptyList();
	protected boolean needAroundClosureStacking = false;

	public int shadowId = nextShadowID++; // every time we build a shadow, it gets a new id

	// ----
	protected Shadow(Kind kind, Member signature, Shadow enclosingShadow) {
		this.kind = kind;
		this.signature = signature;
		this.enclosingShadow = enclosingShadow;
	}

	// ----

	public abstract World getIWorld();

	public List<ShadowMunger> getMungers() {
		return mungers;
	}

	
could this(*) pcd ever match
/** * could this(*) pcd ever match */
public final boolean hasThis() { if (getKind().neverHasThis()) { return false; } else if (getKind().isEnclosingKind()) { return !Modifier.isStatic(getSignature().getModifiers()); } else if (enclosingShadow == null) { return false; } else { return enclosingShadow.hasThis(); } }
the type of the this object here
Throws:
  • IllegalStateException – if there is no this here
/** * the type of the this object here * * @throws IllegalStateException if there is no this here */
public final UnresolvedType getThisType() { if (!hasThis()) { throw new IllegalStateException("no this"); } if (getKind().isEnclosingKind()) { return getSignature().getDeclaringType(); } else { return enclosingShadow.getThisType(); } }
a var referencing this
Throws:
  • IllegalStateException – if there is no target here
/** * a var referencing this * * @throws IllegalStateException if there is no target here */
public abstract Var getThisVar();
could target(*) pcd ever match
/** * could target(*) pcd ever match */
public final boolean hasTarget() { if (getKind().neverHasTarget()) { return false; } else if (getKind().isTargetSameAsThis()) { return hasThis(); } else { return !Modifier.isStatic(getSignature().getModifiers()); } }
the type of the target object here
Throws:
  • IllegalStateException – if there is no target here
/** * the type of the target object here * * @throws IllegalStateException if there is no target here */
public final UnresolvedType getTargetType() { if (!hasTarget()) { throw new IllegalStateException("no target"); } return getSignature().getDeclaringType(); }
a var referencing the target
Throws:
  • IllegalStateException – if there is no target here
/** * a var referencing the target * * @throws IllegalStateException if there is no target here */
public abstract Var getTargetVar(); public UnresolvedType[] getArgTypes() { if (getKind() == FieldSet) { return new UnresolvedType[] { getSignature().getReturnType() }; } return getSignature().getParameterTypes(); } public boolean isShadowForArrayConstructionJoinpoint() { return (getKind() == ConstructorCall && signature.getDeclaringType().isArray()); } public boolean isShadowForMonitor() { return (getKind() == SynchronizationLock || getKind() == SynchronizationUnlock); } // will return the right length array of ints depending on how many dimensions the array has public ResolvedType[] getArgumentTypesForArrayConstructionShadow() { String s = signature.getDeclaringType().getSignature(); int pos = s.indexOf("["); int dims = 1; while (pos < s.length()) { pos++; if (pos < s.length()) { dims += (s.charAt(pos) == '[' ? 1 : 0); } } ResolvedType intType = UnresolvedType.INT.resolve(this.getIWorld()); if (dims == 1) { return new ResolvedType[] { intType }; } ResolvedType[] someInts = new ResolvedType[dims]; for (int i = 0; i < dims; i++) { someInts[i] = intType; } return someInts; } public UnresolvedType[] getGenericArgTypes() { if (isShadowForArrayConstructionJoinpoint()) { return getArgumentTypesForArrayConstructionShadow(); } if (isShadowForMonitor()) { return UnresolvedType.ARRAY_WITH_JUST_OBJECT; } if (getKind() == FieldSet) { return new UnresolvedType[] { getResolvedSignature().getGenericReturnType() }; } return getResolvedSignature().getGenericParameterTypes(); } public UnresolvedType getArgType(int arg) { if (getKind() == FieldSet) { return getSignature().getReturnType(); } return getSignature().getParameterTypes()[arg]; } public int getArgCount() { if (getKind() == FieldSet) { return 1; } return getSignature().getParameterTypes().length; } // /** // * Return name of the argument at position 'i' at this shadow. This does not make sense for all shadows - but can be useful in // * the case of, for example, method-execution. // * // * @return null if it cannot be determined // */ // public String getArgName(int i, World w) { // String[] names = getSignature().getParameterNames(w); // if (names == null || i >= names.length) // return null; // return names[i]; // } public abstract UnresolvedType getEnclosingType(); public abstract Var getArgVar(int i); public abstract Var getThisJoinPointVar(); public abstract Var getThisJoinPointStaticPartVar(); public abstract Var getThisEnclosingJoinPointStaticPartVar(); public abstract Var getThisAspectInstanceVar(ResolvedType aspectType); // annotation variables public abstract Var getKindedAnnotationVar(UnresolvedType forAnnotationType); public abstract Var getWithinAnnotationVar(UnresolvedType forAnnotationType); public abstract Var getWithinCodeAnnotationVar(UnresolvedType forAnnotationType); public abstract Var getThisAnnotationVar(UnresolvedType forAnnotationType); public abstract Var getTargetAnnotationVar(UnresolvedType forAnnotationType); public abstract Var getArgAnnotationVar(int i, UnresolvedType forAnnotationType); public abstract Member getEnclosingCodeSignature();
returns the kind of shadow this is, representing what happens under this shadow
/** * returns the kind of shadow this is, representing what happens under this shadow */
public Kind getKind() { return kind; }
returns the signature of the thing under this shadow
/** * returns the signature of the thing under this shadow */
public Member getSignature() { return signature; }
returns the signature of the thing under this shadow, with any synthetic arguments removed
/** * returns the signature of the thing under this shadow, with any synthetic arguments removed */
public Member getMatchingSignature() { return matchingSignature != null ? matchingSignature : signature; } public void setMatchingSignature(Member member) { this.matchingSignature = member; }
returns the resolved signature of the thing under this shadow
/** * returns the resolved signature of the thing under this shadow * */
public ResolvedMember getResolvedSignature() { if (resolvedSignature == null) { resolvedSignature = signature.resolve(getIWorld()); } return resolvedSignature; } public UnresolvedType getReturnType() { if (kind == ConstructorCall) { return getSignature().getDeclaringType(); } else if (kind == FieldSet) { return UnresolvedType.VOID; } else if (kind == SynchronizationLock || kind == SynchronizationUnlock) { return UnresolvedType.VOID; } return getResolvedSignature().getGenericReturnType(); } public static String METHOD_EXECUTION = "method-execution"; public static String METHOD_CALL = "method-call"; public static String CONSTRUCTOR_EXECUTION = "constructor-execution"; public static String CONSTRUCTOR_CALL = "constructor-call"; public static String FIELD_GET = "field-get"; public static String FIELD_SET = "field-set"; public static String STATICINITIALIZATION = "staticinitialization"; public static String PREINITIALIZATION = "preinitialization"; public static String INITIALIZATION = "initialization"; public static String EXCEPTION_HANDLER = "exception-handler"; public static String SYNCHRONIZATION_LOCK = "lock"; public static String SYNCHRONIZATION_UNLOCK = "unlock"; public static String ADVICE_EXECUTION = "adviceexecution";
These names are the ones that will be returned by thisJoinPoint.getKind() Those need to be documented somewhere
/** * These names are the ones that will be returned by thisJoinPoint.getKind() Those need to be documented somewhere */
public static final Kind MethodCall = new Kind(METHOD_CALL, 1, true); public static final Kind ConstructorCall = new Kind(CONSTRUCTOR_CALL, 2, true); public static final Kind MethodExecution = new Kind(METHOD_EXECUTION, 3, false); public static final Kind ConstructorExecution = new Kind(CONSTRUCTOR_EXECUTION, 4, false); public static final Kind FieldGet = new Kind(FIELD_GET, 5, true); public static final Kind FieldSet = new Kind(FIELD_SET, 6, true); public static final Kind StaticInitialization = new Kind(STATICINITIALIZATION, 7, false); public static final Kind PreInitialization = new Kind(PREINITIALIZATION, 8, false); public static final Kind AdviceExecution = new Kind(ADVICE_EXECUTION, 9, false); public static final Kind Initialization = new Kind(INITIALIZATION, 10, false); public static final Kind ExceptionHandler = new Kind(EXCEPTION_HANDLER, 11, true); public static final Kind SynchronizationLock = new Kind(SYNCHRONIZATION_LOCK, 12, true); public static final Kind SynchronizationUnlock = new Kind(SYNCHRONIZATION_UNLOCK, 13, true); // Bits here are 1<<(Kind.getKey()) - and unfortunately keys didn't start at zero so bits here start at 2 public static final int MethodCallBit = 0x002; public static final int ConstructorCallBit = 0x004; public static final int MethodExecutionBit = 0x008; public static final int ConstructorExecutionBit = 0x010; public static final int FieldGetBit = 0x020; public static final int FieldSetBit = 0x040; public static final int StaticInitializationBit = 0x080; public static final int PreInitializationBit = 0x100; public static final int AdviceExecutionBit = 0x200; public static final int InitializationBit = 0x400; public static final int ExceptionHandlerBit = 0x800; public static final int SynchronizationLockBit = 0x1000; public static final int SynchronizationUnlockBit = 0x2000; public static final int MAX_SHADOW_KIND = 13; public static final Kind[] SHADOW_KINDS = new Kind[] { MethodCall, ConstructorCall, MethodExecution, ConstructorExecution, FieldGet, FieldSet, StaticInitialization, PreInitialization, AdviceExecution, Initialization, ExceptionHandler, SynchronizationLock, SynchronizationUnlock }; public static final int ALL_SHADOW_KINDS_BITS; public static final int NO_SHADOW_KINDS_BITS; static { ALL_SHADOW_KINDS_BITS = 0x3ffe; NO_SHADOW_KINDS_BITS = 0x0000; }
Return count of how many bits set in the supplied parameter.
/** * Return count of how many bits set in the supplied parameter. */
public static int howMany(int i) { int count = 0; for (int j = 0; j < SHADOW_KINDS.length; j++) { if ((i & SHADOW_KINDS[j].bit) != 0) { count++; } } return count; }
A type-safe enum representing the kind of shadows
/** * A type-safe enum representing the kind of shadows */
public static final class Kind extends TypeSafeEnum { // private boolean argsOnStack; //XXX unused public int bit; public Kind(String name, int key, boolean argsOnStack) { super(name, key); bit = 1 << key; // this.argsOnStack = argsOnStack; } public String toLegalJavaIdentifier() { return getName().replace('-', '_'); } public boolean argsOnStack() { return !isTargetSameAsThis(); } // false for handlers public boolean allowsExtraction() { return true; } public boolean isSet(int i) { return (i & bit) != 0; } // XXX revisit along with removal of priorities public boolean hasHighPriorityExceptions() { return !isTargetSameAsThis(); } private final static int hasReturnValueFlag = MethodCallBit | ConstructorCallBit | MethodExecutionBit | FieldGetBit | AdviceExecutionBit;
These shadow kinds have return values that can be bound in after returning(Dooberry doo) advice.
Returns:
/** * These shadow kinds have return values that can be bound in after returning(Dooberry doo) advice. * * @return */
public boolean hasReturnValue() { return (bit & hasReturnValueFlag) != 0; } private final static int isEnclosingKindFlag = MethodExecutionBit | ConstructorExecutionBit | AdviceExecutionBit | StaticInitializationBit | InitializationBit;
These are all the shadows that contains other shadows within them and are often directly associated with methods.
/** * These are all the shadows that contains other shadows within them and are often directly associated with methods. */
public boolean isEnclosingKind() { return (bit & isEnclosingKindFlag) != 0; } private final static int isTargetSameAsThisFlag = MethodExecutionBit | ConstructorExecutionBit | StaticInitializationBit | PreInitializationBit | AdviceExecutionBit | InitializationBit; public boolean isTargetSameAsThis() { return (bit & isTargetSameAsThisFlag) != 0; } private final static int neverHasTargetFlag = ConstructorCallBit | ExceptionHandlerBit | PreInitializationBit | StaticInitializationBit | SynchronizationLockBit | SynchronizationUnlockBit; public boolean neverHasTarget() { return (bit & neverHasTargetFlag) != 0; } private final static int neverHasThisFlag = PreInitializationBit | StaticInitializationBit; public boolean neverHasThis() { return (bit & neverHasThisFlag) != 0; } public String getSimpleName() { int dash = getName().lastIndexOf('-'); if (dash == -1) { return getName(); } else { return getName().substring(dash + 1); } } public static Kind read(DataInputStream s) throws IOException { int key = s.readByte(); switch (key) { case 1: return MethodCall; case 2: return ConstructorCall; case 3: return MethodExecution; case 4: return ConstructorExecution; case 5: return FieldGet; case 6: return FieldSet; case 7: return StaticInitialization; case 8: return PreInitialization; case 9: return AdviceExecution; case 10: return Initialization; case 11: return ExceptionHandler; case 12: return SynchronizationLock; case 13: return SynchronizationUnlock; } throw new BCException("unknown kind: " + key); } }
Only does the check if the munger requires it (@AJ aspects don't)
Params:
  • munger –
Returns:
/** * Only does the check if the munger requires it (@AJ aspects don't) * * @param munger * @return */
protected boolean checkMunger(ShadowMunger munger) { if (munger.mustCheckExceptions()) { for (Iterator<ResolvedType> i = munger.getThrownExceptions().iterator(); i.hasNext();) { if (!checkCanThrow(munger, i.next())) { return false; } } } return true; } protected boolean checkCanThrow(ShadowMunger munger, ResolvedType resolvedTypeX) { if (getKind() == ExceptionHandler) { // XXX much too lenient rules here, need to walk up exception handlers return true; } if (!isDeclaredException(resolvedTypeX, getSignature())) { getIWorld().showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.CANT_THROW_CHECKED, resolvedTypeX, this), // from // advice // in // \ // '" // + // munger // . // + // "\'" // , getSourceLocation(), munger.getSourceLocation()); } return true; } private boolean isDeclaredException(ResolvedType resolvedTypeX, Member member) { ResolvedType[] excs = getIWorld().resolve(member.getExceptions(getIWorld())); for (int i = 0, len = excs.length; i < len; i++) { if (excs[i].isAssignableFrom(resolvedTypeX)) { return true; } } return false; } public void addMunger(ShadowMunger munger) { if (checkMunger(munger)) { if (mungers == Collections.EMPTY_LIST) { mungers = new ArrayList<ShadowMunger>(); } this.mungers.add(munger); } } public final void implement() { sortMungers(); if (mungers == null) { return; } prepareForMungers(); implementMungers(); } private void sortMungers() { List sorted = PartialOrder.sort(mungers); // Bunch of code to work out whether to report xlints for advice that isn't ordered at this Joinpoint possiblyReportUnorderedAdvice(sorted); if (sorted == null) { // this means that we have circular dependencies for (ShadowMunger m : mungers) { getIWorld().getMessageHandler().handleMessage( MessageUtil.error(WeaverMessages.format(WeaverMessages.CIRCULAR_DEPENDENCY, this), m.getSourceLocation())); } } mungers = sorted; } // not quite optimal... but the xlint is ignore by default private void possiblyReportUnorderedAdvice(List sorted) { if (sorted != null && getIWorld().getLint().unorderedAdviceAtShadow.isEnabled() && mungers.size() > 1) { // Stores a set of strings of the form 'aspect1:aspect2' which indicates there is no // precedence specified between the two aspects at this shadow. Set<String> clashingAspects = new HashSet<String>(); int max = mungers.size(); // Compare every pair of advice mungers for (int i = max - 1; i >= 0; i--) { for (int j = 0; j < i; j++) { Object a = mungers.get(i); Object b = mungers.get(j); // Make sure they are the right type if (a instanceof Advice && b instanceof Advice) { Advice adviceA = (Advice) a; Advice adviceB = (Advice) b; if (!adviceA.concreteAspect.equals(adviceB.concreteAspect)) { AdviceKind adviceKindA = adviceA.getKind(); AdviceKind adviceKindB = adviceB.getKind(); // make sure they are the nice ones (<6) and not any synthetic advice ones we // create to support other features of the language. if (adviceKindA.getKey() < (byte) 6 && adviceKindB.getKey() < (byte) 6 && adviceKindA.getPrecedence() == adviceKindB.getPrecedence()) { // Ask the world if it knows about precedence between these Integer order = getIWorld().getPrecedenceIfAny(adviceA.concreteAspect, adviceB.concreteAspect); if (order != null && order.equals(new Integer(0))) { String key = adviceA.getDeclaringAspect() + ":" + adviceB.getDeclaringAspect(); String possibleExistingKey = adviceB.getDeclaringAspect() + ":" + adviceA.getDeclaringAspect(); if (!clashingAspects.contains(possibleExistingKey)) { clashingAspects.add(key); } } } } } } } for (Iterator<String> iter = clashingAspects.iterator(); iter.hasNext();) { String element = iter.next(); String aspect1 = element.substring(0, element.indexOf(":")); String aspect2 = element.substring(element.indexOf(":") + 1); getIWorld().getLint().unorderedAdviceAtShadow.signal(new String[] { this.toString(), aspect1, aspect2 }, this.getSourceLocation(), null); } } }
Prepare the shadow for implementation. After this is done, the shadow should be in such a position that each munger simply needs to be implemented.
/** * Prepare the shadow for implementation. After this is done, the shadow should be in such a position that each munger simply * needs to be implemented. */
protected void prepareForMungers() { throw new RuntimeException("Generic shadows cannot be prepared"); }
Actually implement the (non-empty) mungers associated with this shadow
/** Actually implement the (non-empty) mungers associated with this shadow */
private void implementMungers() { World world = getIWorld(); needAroundClosureStacking = false; int annotationStyleWithAroundAndProceedCount = 0; for (ShadowMunger munger: mungers) { if (munger.getDeclaringType()!= null && munger.getDeclaringType().isAnnotationStyleAspect() && munger.isAroundAdvice() && munger.bindsProceedingJoinPoint()) { annotationStyleWithAroundAndProceedCount++; if (annotationStyleWithAroundAndProceedCount>1) { needAroundClosureStacking = true; break; } } } for (ShadowMunger munger : mungers) { if (munger.implementOn(this)) { world.reportMatch(munger, this); } } } public abstract ISourceLocation getSourceLocation(); // ---- utility public String toString() { return getKind() + "(" + getSignature() + ")"; // + getSourceLines(); } public String toResolvedString(World world) { StringBuffer sb = new StringBuffer(); sb.append(getKind()); sb.append("("); Member m = getSignature(); if (m == null) { sb.append("<<missing signature>>"); } else { ResolvedMember rm = world.resolve(m); if (rm == null) { sb.append("<<unresolvableMember:").append(m).append(">>"); } else { String genString = rm.toGenericString(); if (genString == null) { sb.append("<<unableToGetGenericStringFor:").append(rm).append(">>"); } else { sb.append(genString); } } } sb.append(")"); return sb.toString(); // was: return getKind() + "(" + world.resolve(getSignature()).toGenericString() + ")"; }
Convert a bit array for the shadow kinds into a set of them... should only be used for testing - mainline code should do bit manipulation!
/** * Convert a bit array for the shadow kinds into a set of them... should only be used for testing - mainline code should do bit * manipulation! */
public static Set<Kind> toSet(int i) { Set<Kind> results = new HashSet<Kind>(); for (int j = 0; j < Shadow.SHADOW_KINDS.length; j++) { Kind k = Shadow.SHADOW_KINDS[j]; if (k.isSet(i)) { results.add(k); } } return results; } }