Logo Search packages:      
Sourcecode: aspectj version File versions  Download package

Shadow.java

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

      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 !getSignature().isStatic();
            } 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 !getSignature().isStatic();
            }
      }

      /**
       * 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);
            }
            if (dims == 1)
                  return new ResolvedType[] { ResolvedType.INT };
            ResolvedType[] someInts = new ResolvedType[dims];
            for (int i = 0; i < dims; i++)
                  someInts[i] = ResolvedType.INT;
            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();

      // 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 ResolvedType.VOID;
            else if (kind == SynchronizationLock || kind == SynchronizationUnlock)
                  return ResolvedType.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
       */
00348       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
             */
00389             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.
             */
00399             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 i = munger.getThrownExceptions().iterator(); i.hasNext();) {
                        if (!checkCanThrow(munger, (ResolvedType) 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();
                  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 (Iterator i = mungers.iterator(); i.hasNext();) {
                        ShadowMunger m = (ShadowMunger) i.next();
                        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++) {
                              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 iter = clashingAspects.iterator(); iter.hasNext();) {
                        String element = (String) 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();
            for (Iterator iter = mungers.iterator(); iter.hasNext();) {
                  ShadowMunger munger = (ShadowMunger) iter.next();
                  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 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;
      }

}

Generated by  Doxygen 1.6.0   Back to index