org.aspectj.weaver.Shadow.java Source code

Java tutorial

Introduction

Here is the source code for org.aspectj.weaver.Shadow.java

Source

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

}