org.aspectj.weaver.Shadow Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aspectjweaver Show documentation
Show all versions of aspectjweaver Show documentation
The AspectJ weaver introduces advices to java classes
/* *******************************************************************
* 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 v 2.0
* which accompanies this distribution and is available at
* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
*
* 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.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 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 getMungers() {
return mungers;
}
/**
* 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
*/
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
*/
public abstract Var getThisVar();
/**
* 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
*/
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
*/
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;
}
ResolvedMember resolvedSig = getResolvedSignature();
if (getKind() == FieldSet) {
return new UnresolvedType[] { resolvedSig == null ? null : resolvedSig.getGenericReturnType() };
}
return resolvedSig == null ? null : resolvedSig.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
*/
public Kind getKind() {
return kind;
}
/**
* 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
*/
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
*
*/
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;
}
ResolvedMember resolvedSig = getResolvedSignature();
return resolvedSig == null ? null : resolvedSig.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
*/
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.
*/
public static int howMany(int i) {
int count = 0;
for (Kind shadowKind : SHADOW_KINDS) {
if ((i & shadowKind.bit) != 0) {
count++;
}
}
return count;
}
/**
* 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.
*
* @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.
*/
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)
*
* @param munger
* @return
*/
protected boolean checkMunger(ShadowMunger munger) {
if (munger.mustCheckExceptions()) {
for (ResolvedType resolvedType : munger.getThrownExceptions()) {
if (!checkCanThrow(munger, resolvedType)) {
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 (ResolvedType exc : excs) {
if (exc.isAssignableFrom(resolvedTypeX)) {
return true;
}
}
return false;
}
public void addMunger(ShadowMunger munger) {
if (checkMunger(munger)) {
if (mungers == Collections.EMPTY_LIST) {
mungers = new ArrayList<>();
}
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 clashingAspects = new HashSet<>();
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++) {
ShadowMunger a = mungers.get(i);
ShadowMunger 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(0)) {
String key = adviceA.getDeclaringAspect() + ":" + adviceB.getDeclaringAspect();
String possibleExistingKey = adviceB.getDeclaringAspect() + ":" + adviceA.getDeclaringAspect();
if (!clashingAspects.contains(possibleExistingKey)) {
clashingAspects.add(key);
}
}
}
}
}
}
}
for (String element : clashingAspects) {
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.
*/
protected void prepareForMungers() {
throw new RuntimeException("Generic shadows cannot be prepared");
}
/** 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) {
StringBuilder sb = new StringBuilder();
sb.append(getKind());
sb.append("(");
Member m = getSignature();
if (m == null) {
sb.append("<>");
} else {
ResolvedMember rm = world.resolve(m);
if (rm == null) {
sb.append("<>");
} else {
String genString = rm.toGenericString();
if (genString == null) {
sb.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!
*/
public static Set toSet(int i) {
Set results = new HashSet<>();
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;
}
}