001: /**************************************************************************************
002: * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved. *
003: * http://aspectwerkz.codehaus.org *
004: * ---------------------------------------------------------------------------------- *
005: * The software in this package is published under the terms of the LGPL license *
006: * a copy of which has been included with this distribution in the license.txt file. *
007: **************************************************************************************/package org.codehaus.aspectwerkz.expression;
008:
009: import org.codehaus.aspectwerkz.expression.ast.ASTRoot;
010: import org.codehaus.aspectwerkz.expression.ast.ASTPointcutReference;
011: import org.codehaus.aspectwerkz.expression.ast.ASTArgParameter;
012: import org.codehaus.aspectwerkz.expression.ast.ASTArgs;
013: import org.codehaus.aspectwerkz.expression.ast.ASTThis;
014: import org.codehaus.aspectwerkz.expression.ast.ASTTarget;
015: import org.codehaus.aspectwerkz.expression.ast.Node;
016: import org.codehaus.aspectwerkz.expression.ast.ASTCflow;
017: import org.codehaus.aspectwerkz.util.Strings;
018: import org.codehaus.aspectwerkz.exception.DefinitionException;
019: import org.codehaus.aspectwerkz.reflect.ClassInfo;
020: import org.codehaus.aspectwerkz.reflect.ClassInfoHelper;
021: import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo;
022: import org.codehaus.aspectwerkz.util.ContextClassLoader;
023:
024: import java.util.Iterator;
025:
026: import gnu.trove.TIntIntHashMap;
027: import gnu.trove.TObjectIntHashMap;
028:
029: /**
030: * A visitor to compute the args index of the target (matching) method/constructor which match the advice args. Note:
031: * extends the ExpressionVisitor. We should allow for optimization (all=TRUE) by assuming that args(..) does not depends
032: * of the matching context. The "(String a, String b):methodX && args(a,b) -OR- methodY && args(b,a)" expression should
033: * not be allowed then.
034: *
035: * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
036: */
037: public class ArgsIndexVisitor extends ExpressionVisitor {
038:
039: /**
040: * Classloader used to perform type checks (for target / this bindings)
041: * A strong reference is enough since this visitor is not be referenced.
042: */
043: private ClassLoader m_classLoader;
044:
045: /**
046: * Update the given context with its runtime information (this, target, args).
047: * It should be called for each advice.
048: *
049: * @param expressionInfo
050: * @param context
051: */
052: public static void updateContextForRuntimeInformation(
053: final ExpressionInfo expressionInfo,
054: final ExpressionContext context, final ClassLoader loader) {
055: ArgsIndexVisitor visitor = new ArgsIndexVisitor(expressionInfo,
056: expressionInfo.toString(), expressionInfo
057: .getNamespace(), expressionInfo.getExpression()
058: .getASTRoot(), loader);
059: visitor.match(context);
060: }
061:
062: private ArgsIndexVisitor(final ExpressionInfo expressionInfo,
063: final String expression, final String namespace,
064: final Node root, final ClassLoader loader) {
065: super (expressionInfo, expression, namespace, root);
066: m_classLoader = loader;
067: }
068:
069: //-- overrided methods to compute the args index mapping --//
070:
071: public Object visit(ASTPointcutReference node, Object data) {
072: // do the sub expression visit
073: ExpressionContext context = (ExpressionContext) data;
074: ExpressionNamespace namespace = ExpressionNamespace
075: .getNamespace(m_namespace);
076: ExpressionInfo expressionInfo = namespace
077: .getExpressionInfo(node.getName());
078:
079: ArgsIndexVisitor referenced = new ArgsIndexVisitor(
080: expressionInfo, expressionInfo.toString(),
081: expressionInfo.getNamespace(), expressionInfo
082: .getExpression().getASTRoot(), m_classLoader);
083:
084: // keep track of the state we already had
085: String targetSoFar = context.m_targetBoundedName;
086: String this SoFar = context.m_this BoundedName;
087: boolean targetWithRuntimeCheckSoFar = context.m_targetWithRuntimeCheck;
088: TObjectIntHashMap exprIndexToTargetIndexSoFar = (TObjectIntHashMap) context.m_exprIndexToTargetIndex
089: .clone();
090:
091: context.resetRuntimeState();
092: Boolean match = referenced.matchUndeterministic(context);
093:
094: // merge the state
095: if (context.m_targetBoundedName == null) {
096: context.m_targetBoundedName = targetSoFar;
097: } else if (targetSoFar != null) {
098: if (node.jjtGetNumChildren() == 1) {
099: String referenceCallArg = ((ASTArgParameter) node
100: .jjtGetChild(0)).getTypePattern().getPattern();
101: if (!targetSoFar.equals(referenceCallArg)) {
102: throw new UnsupportedOperationException(
103: "should not occur");
104: }
105: }
106: }
107: if (context.m_this BoundedName == null) {
108: context.m_this BoundedName = this SoFar;
109: } else if (this SoFar != null) {
110: if (node.jjtGetNumChildren() == 1) {
111: String referenceCallArg = ((ASTArgParameter) node
112: .jjtGetChild(0)).getTypePattern().getPattern();
113: if (!this SoFar.equals(referenceCallArg)) {
114: throw new UnsupportedOperationException(
115: "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: // update the this and target bounded name from this last visit as well as args
132: TObjectIntHashMap exprToTargetArgIndexes = new TObjectIntHashMap();
133: for (int i = 0; i < node.jjtGetNumChildren(); i++) {
134: String referenceCallArg = ((ASTArgParameter) node
135: .jjtGetChild(i)).getTypePattern().getPattern();
136: String referentArg = expressionInfo
137: .getArgumentNameAtIndex(i);
138: if (referentArg.equals(context.m_targetBoundedName)) {
139: context.m_targetBoundedName = referenceCallArg;
140: assertIsInstanceOf(expressionInfo
141: .getArgumentType(referentArg), m_expressionInfo
142: .getArgumentType(referenceCallArg));
143: } else if (referentArg.equals(context.m_this BoundedName)) {
144: context.m_this BoundedName = referenceCallArg;
145: assertIsInstanceOf(expressionInfo
146: .getArgumentType(referentArg), m_expressionInfo
147: .getArgumentType(referenceCallArg));
148: } else {
149: int adviceArgIndex = i;
150: if (context.m_exprIndexToTargetIndex
151: .containsKey(referentArg)) {
152: int targetArgIndex = context.m_exprIndexToTargetIndex
153: .get(referentArg);
154: exprToTargetArgIndexes.put(referenceCallArg,
155: targetArgIndex);
156: }
157:
158: }
159: }
160: // merge with index found so far (inlined args() f.e.)
161: Object[] soFar = exprIndexToTargetIndexSoFar.keys();
162: for (int i = 0; i < soFar.length; i++) {
163: String name = (String) soFar[i];
164: if (!exprToTargetArgIndexes.containsKey(name)) {
165: exprToTargetArgIndexes.put(name,
166: exprIndexToTargetIndexSoFar.get(name));
167: }
168: }
169: context.m_exprIndexToTargetIndex = exprToTargetArgIndexes;
170: return match;
171: }
172:
173: public Object visit(ASTCflow node, Object data) {
174: // do the sub expression visit
175: ExpressionContext context = (ExpressionContext) data;
176: //ExpressionNamespace namespace = ExpressionNamespace.getNamespace(m_namespace);
177: //ExpressionInfo expressionInfo = namespace.getExpressionInfo(node.getName());
178:
179: ExpressionInfo expressionInfo = new ExpressionInfo(node
180: .jjtGetChild(0), m_namespace);
181: expressionInfo.inheritPossibleArgumentFrom(m_expressionInfo);
182:
183: ArgsIndexVisitor referenced = new ArgsIndexVisitor(
184: expressionInfo, "N/A", m_namespace,
185: node.jjtGetChild(0), m_classLoader);
186:
187: // keep track of the state we already had
188: String targetSoFar = context.m_targetBoundedName;
189: String this SoFar = context.m_this BoundedName;
190: boolean targetWithRuntimeCheckSoFar = context.m_targetWithRuntimeCheck;
191: TObjectIntHashMap exprIndexToTargetIndexSoFar = (TObjectIntHashMap) context.m_exprIndexToTargetIndex
192: .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_this BoundedName == null) {
204: context.m_this BoundedName = this SoFar;
205: } else if (this SoFar != 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,
220: exprIndexToTargetIndexSoFar.get(o));
221: }
222: }
223: return match;
224: }
225:
226: public Object visit(ASTArgs node, Object data) {
227: return super .visit(node, data);
228: }
229:
230: public Object visit(ASTArgParameter node, Object data) {
231: // do the visit
232: Boolean match = (Boolean) super .visit(node, data);
233:
234: // get the pointcut signature arg index of the arg we are visiting
235: int pointcutArgIndex = -1;
236: if (node.getTypePattern().getPattern().indexOf(".") < 0) {
237: pointcutArgIndex = m_expressionInfo.getArgumentIndex(node
238: .getTypePattern().getPattern());
239: }
240:
241: // if match and we are visiting a parameter binding (not a type matching)
242: if (pointcutArgIndex >= 0 && Boolean.TRUE.equals(match)) {
243: ExpressionContext ctx = (ExpressionContext) data;
244: ctx.m_exprIndexToTargetIndex.put(m_expressionInfo
245: .getArgumentNameAtIndex(pointcutArgIndex), ctx
246: .getCurrentTargetArgsIndex());
247: }
248: return match;
249: }
250:
251: public Object visit(ASTThis node, Object data) {
252: // if the this(..) node identifier appears in the pointcut signature, we have a bounded type
253: if (m_expressionInfo.getArgumentType(node.getIdentifier()) != null) {
254: ExpressionContext ctx = (ExpressionContext) data;
255: if (ctx.m_this BoundedName == null) {
256: ctx.m_this BoundedName = node.getIdentifier();
257: } else if (ctx.m_this BoundedName != node.getIdentifier()) {
258: throw new DefinitionException(
259: "this(..) seems to be bounded to different bounded entities in \""
260: + m_expressionInfo.toString()
261: + "\" in "
262: + m_expressionInfo.getNamespace()
263: + " : found " + ctx.m_targetBoundedName
264: + " and " + node.getIdentifier());
265: }
266: }
267: return super .visit(node, data);
268: }
269:
270: public Object visit(ASTTarget node, Object data) {
271: // if the target(..) node identifier appears in the pointcut signature, we have a bounded type
272: if (m_expressionInfo.getArgumentType(node.getIdentifier()) != null) {
273: ExpressionContext ctx = (ExpressionContext) data;
274: if (ctx.m_targetBoundedName == null) {
275: ctx.m_targetBoundedName = node.getIdentifier();
276: } else if (ctx.m_targetBoundedName != node.getIdentifier()) {
277: throw new DefinitionException(
278: "target(..) seems to be bounded to different bounded entities in \""
279: + m_expressionInfo.toString()
280: + "\" in "
281: + m_expressionInfo.getNamespace()
282: + " : found " + ctx.m_targetBoundedName
283: + " and " + node.getIdentifier());
284: }
285: }
286: // keep track if the result was undetermined: we will need a runtime check
287: Object match = super .visit(node, data);
288: if (match == null) {
289: ((ExpressionContext) data).m_targetWithRuntimeCheck = true;
290: }
291: return match;
292: }
293:
294: /**
295: * Ensure className is an instance of superClass name (both super class / interface just like "instanceof")
296: * Or throw an exception
297: *
298: * @param className
299: * @param superClassName
300: */
301: private void assertIsInstanceOf(String className,
302: String super ClassName) {
303: if (className.equals(super ClassName)) {
304: ;//fine
305: } else {
306: // advice(Foo f) for pc(f) with pc(Object o) for example
307: // we need to ensure that Foo is an instance of Object
308: ClassInfo classInfo = AsmClassInfo.getClassInfo(className,
309: m_classLoader);
310: boolean instanceOf = ClassInfoHelper.instanceOf(classInfo,
311: super ClassName);
312: if (!instanceOf) {
313: throw new DefinitionException(
314: "Attempt to reference a pointcut with incompatible object type: for \""
315: + m_expression + "\" , " + className
316: + " is not an instance of "
317: + super ClassName + "."
318: + " Error occured in " + m_namespace);
319: }
320: }
321: }
322: }
|