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
75
76 public Object visit(ASTPointcutReference node, Object data) {
77
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
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
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
121 context.m_targetWithRuntimeCheck = targetWithRuntimeCheckSoFar;
122 }
123 if (context.m_exprIndexToTargetIndex.isEmpty()) {
124
125 context.m_exprIndexToTargetIndex = exprIndexToTargetIndexSoFar;
126 } else if (!exprIndexToTargetIndexSoFar.isEmpty()) {
127
128 throw new UnsupportedOperationException("should not occur");
129 }
130
131
132
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
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
172 ExpressionContext context = (ExpressionContext) data;
173
174
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
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
198 if (context.m_targetBoundedName == null) {
199 context.m_targetBoundedName = targetSoFar;
200 } else if (targetSoFar != null) {
201
202 }
203 if (context.m_thisBoundedName == null) {
204 context.m_thisBoundedName = thisSoFar;
205 } else if (thisSoFar != null) {
206
207 }
208 if (!context.m_targetWithRuntimeCheck) {
209
210 context.m_targetWithRuntimeCheck = targetWithRuntimeCheckSoFar;
211 }
212 if (context.m_exprIndexToTargetIndex.isEmpty()) {
213
214 context.m_exprIndexToTargetIndex = exprIndexToTargetIndexSoFar;
215 } else if (!exprIndexToTargetIndexSoFar.isEmpty()) {
216
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
231 Boolean match = (Boolean) super.visit(node, data);
232
233
234 int pointcutArgIndex = -1;
235 if (node.getTypePattern().getPattern().indexOf(".") < 0) {
236 pointcutArgIndex = m_expressionInfo.getArgumentIndex(node.getTypePattern().getPattern());
237 }
238
239
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
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
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
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 ;
302 } else {
303
304
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 }