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