View Javadoc

    1   /***************************************************************************************
    2    * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved.                 *
    3    * http://aspectwerkz.codehaus.org                                                    *
    4    * ---------------------------------------------------------------------------------- *
    5    * The software in this package is published under the terms of the LGPL license      *
    6    * a copy of which has been included with this distribution in the license.txt file.  *
    7    **************************************************************************************/
    8   package org.codehaus.aspectwerkz.expression;
    9
    10  import org.codehaus.aspectwerkz.expression.ast.ASTRoot;
    11  import org.codehaus.aspectwerkz.expression.ast.ASTPointcutReference;
    12  import org.codehaus.aspectwerkz.expression.ast.ASTArgParameter;
    13  import org.codehaus.aspectwerkz.expression.ast.ASTArgs;
    14  import org.codehaus.aspectwerkz.expression.ast.ASTThis;
    15  import org.codehaus.aspectwerkz.expression.ast.ASTTarget;
    16  import org.codehaus.aspectwerkz.expression.ast.Node;
    17  import org.codehaus.aspectwerkz.expression.ast.ASTCflow;
    18  import org.codehaus.aspectwerkz.util.Strings;
    19  import org.codehaus.aspectwerkz.exception.DefinitionException;
    20  import org.codehaus.aspectwerkz.reflect.ClassInfo;
    21  import org.codehaus.aspectwerkz.reflect.ClassInfoHelper;
    22  import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo;
    23  import org.codehaus.aspectwerkz.util.ContextClassLoader;
    24
    25  import java.util.Iterator;
    26
    27  import gnu.trove.TIntIntHashMap;
    28  import gnu.trove.TObjectIntHashMap;
    29
    30  /***
    31   * A visitor to compute the args index of the target (matching) method/constructor which match the advice args. Note:
    32   * extends the ExpressionVisitor. We should allow for optimization (all=TRUE) by assuming that args(..) does not depends
    33   * of the matching context. The "(String a, String b):methodX && args(a,b) -OR- methodY && args(b,a)" expression should
    34   * not be allowed then.
    35   *
    36   * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
    37   */
    38  public class ArgsIndexVisitor extends ExpressionVisitor {
    39
    40      /***
    41       * Classloader used to perform type checks (for target / this bindings)
    42       * A strong reference is enough since this visitor is not be referenced.
    43       */
    44      private ClassLoader m_classLoader;
    45
    46      /***
    47       * Update the given context with its runtime information (this, target, args).
    48       * It should be called for each advice.
    49       *
    50       * @param expressionInfo
    51       * @param context
    52       */
    53      public static void updateContextForRuntimeInformation(final ExpressionInfo expressionInfo,
    54                                                            final ExpressionContext context,
    55                                                            final ClassLoader loader) {
    56          ArgsIndexVisitor visitor = new ArgsIndexVisitor(
    57                  expressionInfo, expressionInfo.toString(),
    58                  expressionInfo.getNamespace(),
    59                  expressionInfo.getExpression().getASTRoot(),
    60                  loader
    61          );
    62          visitor.match(context);
    63      }
    64
    65      private ArgsIndexVisitor(final ExpressionInfo expressionInfo,
    66                               final String expression,
    67                               final String namespace,
    68                               final Node root,
    69                               final ClassLoader loader) {
    70          super(expressionInfo, expression, namespace, root);
    71          m_classLoader = loader;
    72      }
    73
    74      //-- overrided methods to compute the args index mapping --//
    75
    76      public Object visit(ASTPointcutReference node, Object data) {
    77          // do the sub expression visit
    78          ExpressionContext context = (ExpressionContext) data;
    79          ExpressionNamespace namespace = ExpressionNamespace.getNamespace(m_namespace);
    80          ExpressionInfo expressionInfo = namespace.getExpressionInfo(node.getName());
    81
    82          ArgsIndexVisitor referenced = new ArgsIndexVisitor(
    83                  expressionInfo, expressionInfo.toString(),
    84                  expressionInfo.getNamespace(),
    85                  expressionInfo.getExpression().getASTRoot(),
    86                  m_classLoader
    87          );
    88
    89          // keep track of the state we already had
    90          String targetSoFar = context.m_targetBoundedName;
    91          String thisSoFar = context.m_thisBoundedName;
    92          boolean targetWithRuntimeCheckSoFar = context.m_targetWithRuntimeCheck;
    93          TObjectIntHashMap exprIndexToTargetIndexSoFar = (TObjectIntHashMap) context.m_exprIndexToTargetIndex.clone();
    94
    95          context.resetRuntimeState();
    96          Boolean match = referenced.matchUndeterministic(context);
    97
    98          // merge the state
    99          if (context.m_targetBoundedName == null) {
    100             context.m_targetBoundedName = targetSoFar;
    101         } else if (targetSoFar != null) {
    102             if (node.jjtGetNumChildren() == 1) {
    103                 String referenceCallArg = ((ASTArgParameter) node.jjtGetChild(0)).getTypePattern().getPattern();
    104                 if (!targetSoFar.equals(referenceCallArg)) {
    105                     throw new UnsupportedOperationException("should not occur");
    106                 }
    107             }
    108         }
    109         if (context.m_thisBoundedName == null) {
    110             context.m_thisBoundedName = thisSoFar;
    111         } else if (thisSoFar != null) {
    112             if (node.jjtGetNumChildren() == 1) {
    113                 String referenceCallArg = ((ASTArgParameter) node.jjtGetChild(0)).getTypePattern().getPattern();
    114                 if (!thisSoFar.equals(referenceCallArg)) {
    115                     throw new UnsupportedOperationException("should not occur");
    116                 }
    117             }
    118         }
    119         if (!context.m_targetWithRuntimeCheck) {
    120             // restore
    121             context.m_targetWithRuntimeCheck = targetWithRuntimeCheckSoFar;
    122         }
    123         if (context.m_exprIndexToTargetIndex.isEmpty()) {
    124             // restore
    125             context.m_exprIndexToTargetIndex = exprIndexToTargetIndexSoFar;
    126         } else if (!exprIndexToTargetIndexSoFar.isEmpty()) {
    127             //should merge ?
    128             throw new UnsupportedOperationException("should not occur");
    129         }
    130
    131
    132         // update the this and target bounded name from this last visit as well as args
    133         TObjectIntHashMap exprToTargetArgIndexes = new TObjectIntHashMap();
    134         for (int i = 0; i < node.jjtGetNumChildren(); i++) {
    135             String referenceCallArg = ((ASTArgParameter) node.jjtGetChild(i)).getTypePattern().getPattern();
    136             String referentArg = expressionInfo.getArgumentNameAtIndex(i);
    137             if (referentArg.equals(context.m_targetBoundedName)) {
    138                 context.m_targetBoundedName = referenceCallArg;
    139                 assertIsInstanceOf(
    140                         expressionInfo.getArgumentType(referentArg),
    141                         m_expressionInfo.getArgumentType(referenceCallArg)
    142                 );
    143             } else if (referentArg.equals(context.m_thisBoundedName)) {
    144                 context.m_thisBoundedName = referenceCallArg;
    145                 assertIsInstanceOf(
    146                         expressionInfo.getArgumentType(referentArg),
    147                         m_expressionInfo.getArgumentType(referenceCallArg)
    148                 );
    149             } else {
    150                 int adviceArgIndex = i;
    151                 if (context.m_exprIndexToTargetIndex.containsKey(referentArg)) {
    152                     int targetArgIndex = context.m_exprIndexToTargetIndex.get(referentArg);
    153                     exprToTargetArgIndexes.put(referenceCallArg, targetArgIndex);
    154                 }
    155
    156             }
    157         }
    158         // merge with index found so far (inlined args() f.e.)
    159         Object[] soFar = exprIndexToTargetIndexSoFar.keys();
    160         for (int i = 0; i < soFar.length; i++) {
    161             String name = (String) soFar[i];
    162             if (!exprToTargetArgIndexes.containsKey(name)) {
    163                 exprToTargetArgIndexes.put(name, exprIndexToTargetIndexSoFar.get(name));
    164             }
    165         }
    166         context.m_exprIndexToTargetIndex = exprToTargetArgIndexes;
    167         return match;
    168     }
    169
    170     public Object visit(ASTCflow node, Object data) {
    171         // do the sub expression visit
    172         ExpressionContext context = (ExpressionContext) data;
    173         //ExpressionNamespace namespace = ExpressionNamespace.getNamespace(m_namespace);
    174         //ExpressionInfo expressionInfo = namespace.getExpressionInfo(node.getName());
    175
    176         ExpressionInfo expressionInfo = new ExpressionInfo(
    177                 node.jjtGetChild(0), m_namespace
    178         );
    179         expressionInfo.inheritPossibleArgumentFrom(m_expressionInfo);
    180
    181         ArgsIndexVisitor referenced = new ArgsIndexVisitor(
    182                 expressionInfo, "N/A",
    183                 m_namespace,
    184                 node.jjtGetChild(0),
    185                 m_classLoader
    186         );
    187
    188         // keep track of the state we already had
    189         String targetSoFar = context.m_targetBoundedName;
    190         String thisSoFar = context.m_thisBoundedName;
    191         boolean targetWithRuntimeCheckSoFar = context.m_targetWithRuntimeCheck;
    192         TObjectIntHashMap exprIndexToTargetIndexSoFar = (TObjectIntHashMap) context.m_exprIndexToTargetIndex.clone();
    193
    194         context.resetRuntimeState();
    195         Boolean match = referenced.matchUndeterministic(context);
    196
    197         // TODO FIX ME merge the state
    198         if (context.m_targetBoundedName == null) {
    199             context.m_targetBoundedName = targetSoFar;
    200         } else if (targetSoFar != null) {
    201             // cflow target
    202         }
    203         if (context.m_thisBoundedName == null) {
    204             context.m_thisBoundedName = thisSoFar;
    205         } else if (thisSoFar != null) {
    206             // cflow this
    207         }
    208         if (!context.m_targetWithRuntimeCheck) {
    209             // restore
    210             context.m_targetWithRuntimeCheck = targetWithRuntimeCheckSoFar;
    211         }
    212         if (context.m_exprIndexToTargetIndex.isEmpty()) {
    213             // restore
    214             context.m_exprIndexToTargetIndex = exprIndexToTargetIndexSoFar;
    215         } else if (!exprIndexToTargetIndexSoFar.isEmpty()) {
    216             //should merge ?
    217             for (int i = 0; i < exprIndexToTargetIndexSoFar.keys().length; i++) {
    218                 Object o = exprIndexToTargetIndexSoFar.keys()[i];
    219                 context.m_exprIndexToTargetIndex.put(o, exprIndexToTargetIndexSoFar.get(o));
    220             }
    221         }
    222         return match;
    223     }
    224
    225     public Object visit(ASTArgs node, Object data) {
    226         return super.visit(node, data);
    227     }
    228
    229     public Object visit(ASTArgParameter node, Object data) {
    230         // do the visit
    231         Boolean match = (Boolean) super.visit(node, data);
    232
    233         // get the pointcut signature arg index of the arg we are visiting
    234         int pointcutArgIndex = -1;
    235         if (node.getTypePattern().getPattern().indexOf(".") < 0) {
    236             pointcutArgIndex = m_expressionInfo.getArgumentIndex(node.getTypePattern().getPattern());
    237         }
    238
    239         // if match and we are visiting a parameter binding (not a type matching)
    240         if (pointcutArgIndex >= 0 && Boolean.TRUE.equals(match)) {
    241             ExpressionContext ctx = (ExpressionContext) data;
    242             ctx.m_exprIndexToTargetIndex.put(
    243                     m_expressionInfo.getArgumentNameAtIndex(pointcutArgIndex), ctx.getCurrentTargetArgsIndex()
    244             );
    245         }
    246         return match;
    247     }
    248
    249     public Object visit(ASTThis node, Object data) {
    250         // if the this(..) node identifier appears in the pointcut signature, we have a bounded type
    251         if (m_expressionInfo.getArgumentType(node.getIdentifier()) != null) {
    252             ExpressionContext ctx = (ExpressionContext) data;
    253             if (ctx.m_thisBoundedName == null) {
    254                 ctx.m_thisBoundedName = node.getIdentifier();
    255             } else if (ctx.m_thisBoundedName != node.getIdentifier()) {
    256                 throw new DefinitionException(
    257                         "this(..) seems to be bounded to different bounded entities in \""
    258                         + m_expressionInfo.toString() + "\" in " +
    259                         m_expressionInfo.getNamespace()
    260                         + " : found " + ctx.m_targetBoundedName + " and " +
    261                         node.getIdentifier()
    262                 );
    263             }
    264         }
    265         return super.visit(node, data);
    266     }
    267
    268     public Object visit(ASTTarget node, Object data) {
    269         // if the target(..) node identifier appears in the pointcut signature, we have a bounded type
    270         if (m_expressionInfo.getArgumentType(node.getIdentifier()) != null) {
    271             ExpressionContext ctx = (ExpressionContext) data;
    272             if (ctx.m_targetBoundedName == null) {
    273                 ctx.m_targetBoundedName = node.getIdentifier();
    274             } else if (ctx.m_targetBoundedName != node.getIdentifier()) {
    275                 throw new DefinitionException(
    276                         "target(..) seems to be bounded to different bounded entities in \""
    277                         + m_expressionInfo.toString() + "\" in " +
    278                         m_expressionInfo.getNamespace()
    279                         + " : found " + ctx.m_targetBoundedName + " and " +
    280                         node.getIdentifier()
    281                 );
    282             }
    283         }
    284         // keep track if the result was undetermined: we will need a runtime check
    285         Object match = super.visit(node, data);
    286         if (match == null) {
    287             ((ExpressionContext) data).m_targetWithRuntimeCheck = true;
    288         }
    289         return match;
    290     }
    291
    292     /***
    293      * Ensure className is an instance of superClass name (both super class / interface just like "instanceof")
    294      * Or throw an exception
    295      *
    296      * @param className
    297      * @param superClassName
    298      */
    299     private void assertIsInstanceOf(String className, String superClassName) {
    300         if (className.equals(superClassName)) {
    301             ;//fine
    302         } else {
    303             // advice(Foo f) for pc(f) with pc(Object o) for example
    304             // we need to ensure that Foo is an instance of Object
    305             ClassInfo classInfo = AsmClassInfo.getClassInfo(className, m_classLoader);
    306             boolean instanceOf = ClassInfoHelper.instanceOf(classInfo, superClassName);
    307             if (!instanceOf) {
    308                 throw new DefinitionException(
    309                         "Attempt to reference a pointcut with incompatible object type: for \""
    310                         + m_expression + "\" , " + className + " is not an instance of " +
    311                         superClassName +
    312                         "."
    313                         + " Error occured in " + m_namespace
    314                 );
    315             }
    316         }
    317     }
    318 }