001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.aop.aspectj;
018:
019: import java.lang.reflect.Field;
020:
021: import org.aspectj.weaver.ResolvedType;
022: import org.aspectj.weaver.ast.And;
023: import org.aspectj.weaver.ast.Call;
024: import org.aspectj.weaver.ast.FieldGetCall;
025: import org.aspectj.weaver.ast.HasAnnotation;
026: import org.aspectj.weaver.ast.ITestVisitor;
027: import org.aspectj.weaver.ast.Instanceof;
028: import org.aspectj.weaver.ast.Literal;
029: import org.aspectj.weaver.ast.Not;
030: import org.aspectj.weaver.ast.Or;
031: import org.aspectj.weaver.ast.Test;
032: import org.aspectj.weaver.internal.tools.MatchingContextBasedTest;
033: import org.aspectj.weaver.reflect.ReflectionVar;
034: import org.aspectj.weaver.reflect.ShadowMatchImpl;
035: import org.aspectj.weaver.tools.ShadowMatch;
036:
037: import org.springframework.util.ReflectionUtils;
038:
039: /**
040: * This class encapsulates some AspectJ internal knowledge that should be
041: * pushed back into the AspectJ project in a future release.
042: *
043: * <p>It relies on implementation specific knowledge in AspectJ to break
044: * encapsulation and do something AspectJ was not designed to do: query
045: * the types of runtime tests that will be performed. The code here should
046: * migrate to <code>ShadowMatch.getVariablesInvolvedInRuntimeTest()</code>
047: * or some similar operation.
048: *
049: * <p>See <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=151593"/>.
050: *
051: * @author Adrian Colyer
052: * @author Ramnivas Laddad
053: * @since 2.0
054: */
055: class RuntimeTestWalker {
056:
057: private final Test runtimeTest;
058:
059: public RuntimeTestWalker(ShadowMatch shadowMatch) {
060: ShadowMatchImpl shadowMatchImplementation = (ShadowMatchImpl) shadowMatch;
061: try {
062: Field testField = shadowMatchImplementation.getClass()
063: .getDeclaredField("residualTest");
064: ReflectionUtils.makeAccessible(testField);
065: this .runtimeTest = (Test) testField.get(shadowMatch);
066: } catch (NoSuchFieldException noSuchFieldEx) {
067: throw new IllegalStateException(
068: "The version of aspectjtools.jar / aspectjweaver.jar "
069: + "on the classpath is incompatible with this version of Spring: Expected field "
070: + "'runtimeTest' is not present on ShadowMatchImpl class.");
071: } catch (IllegalAccessException illegalAccessEx) {
072: // Famous last words... but I don't see how this can happen given the
073: // makeAccessible call above
074: throw new IllegalStateException(
075: "Unable to access ShadowMatchImpl.runtimeTest field.");
076: }
077: }
078:
079: /**
080: * If the test uses any of the this, target, at_this, at_target, and at_annotation vars,
081: * then it tests subtype sensitive vars.
082: */
083: public boolean testsSubtypeSensitiveVars() {
084: return new SubtypeSensitiveVarTypeTestVisitor()
085: .testsSubtypeSensitiveVars(this .runtimeTest);
086: }
087:
088: public boolean testThisInstanceOfResidue(Class this Class) {
089: return new ThisInstanceOfResidueTestVisitor(this Class)
090: .this InstanceOfMatches(this .runtimeTest);
091: }
092:
093: public boolean testTargetInstanceOfResidue(Class targetClass) {
094: return new TargetInstanceOfResidueTestVisitor(targetClass)
095: .targetInstanceOfMatches(this .runtimeTest);
096: }
097:
098: private static class TestVisitorAdapter implements ITestVisitor {
099:
100: protected static final int THIS_VAR = 0;
101: protected static final int TARGET_VAR = 1;
102: protected static final int AT_THIS_VAR = 3;
103: protected static final int AT_TARGET_VAR = 4;
104: protected static final int AT_ANNOTATION_VAR = 8;
105:
106: public void visit(And e) {
107: e.getLeft().accept(this );
108: e.getRight().accept(this );
109: }
110:
111: public void visit(Or e) {
112: e.getLeft().accept(this );
113: e.getRight().accept(this );
114: }
115:
116: public void visit(Not e) {
117: e.getBody().accept(this );
118: }
119:
120: public void visit(Instanceof i) {
121: }
122:
123: public void visit(Literal literal) {
124: }
125:
126: public void visit(Call call) {
127: }
128:
129: public void visit(FieldGetCall fieldGetCall) {
130: }
131:
132: public void visit(HasAnnotation hasAnnotation) {
133: }
134:
135: public void visit(MatchingContextBasedTest matchingContextTest) {
136: }
137:
138: protected int getVarType(ReflectionVar v) {
139: try {
140: Field varTypeField = ReflectionVar.class
141: .getDeclaredField("varType");
142: ReflectionUtils.makeAccessible(varTypeField);
143: Integer varTypeValue = (Integer) varTypeField.get(v);
144: return varTypeValue.intValue();
145: } catch (NoSuchFieldException noSuchFieldEx) {
146: throw new IllegalStateException(
147: "the version of aspectjtools.jar / aspectjweaver.jar "
148: + "on the classpath is incompatible with this version of Spring:- expected field "
149: + "'varType' is not present on ReflectionVar class");
150: } catch (IllegalAccessException illegalAccessEx) {
151: // Famous last words... but I don't see how this can happen given the
152: // makeAccessible call above
153: throw new IllegalStateException(
154: "Unable to access ReflectionVar.varType field.");
155: }
156: }
157: }
158:
159: private static abstract class InstanceOfResidueTestVisitor extends
160: TestVisitorAdapter {
161:
162: private Class matchClass;
163: private boolean matches;
164: private int matchVarType;
165:
166: public InstanceOfResidueTestVisitor(Class matchClass,
167: boolean defaultMatches, int matchVarType) {
168: this .matchClass = matchClass;
169: this .matches = defaultMatches;
170: this .matchVarType = matchVarType;
171: }
172:
173: public boolean instanceOfMatches(Test test) {
174: test.accept(this );
175: return matches;
176: }
177:
178: public void visit(Instanceof i) {
179: ResolvedType type = (ResolvedType) i.getType();
180: int varType = getVarType((ReflectionVar) i.getVar());
181: if (varType != matchVarType) {
182: return;
183: }
184: try {
185: Class typeClass = Class.forName(type.getName());
186: // Don't use ReflectionType.isAssignableFrom() as it won't be aware of (Spring) mixins
187: this .matches = typeClass.isAssignableFrom(matchClass);
188: } catch (ClassNotFoundException ex) {
189: this .matches = false;
190: }
191: }
192: }
193:
194: /**
195: * Check if residue of target(TYPE) kind. See SPR-3783 for more details.
196: */
197: private static class TargetInstanceOfResidueTestVisitor extends
198: InstanceOfResidueTestVisitor {
199:
200: public TargetInstanceOfResidueTestVisitor(Class targetClass) {
201: super (targetClass, false, TARGET_VAR);
202: }
203:
204: public boolean targetInstanceOfMatches(Test test) {
205: return instanceOfMatches(test);
206: }
207: }
208:
209: /**
210: * Check if residue of this(TYPE) kind. See SPR-2979 for more details.
211: */
212: private static class ThisInstanceOfResidueTestVisitor extends
213: InstanceOfResidueTestVisitor {
214:
215: public ThisInstanceOfResidueTestVisitor(Class this Class) {
216: super (this Class, true, THIS_VAR);
217: }
218:
219: // TODO: Optimization: Process only if this() specifies a type and not an identifier.
220: public boolean this InstanceOfMatches(Test test) {
221: return instanceOfMatches(test);
222: }
223: }
224:
225: private static class SubtypeSensitiveVarTypeTestVisitor extends
226: TestVisitorAdapter {
227:
228: private final Object this Obj = new Object();
229: private final Object targetObj = new Object();
230: private final Object[] argsObjs = new Object[0];
231: private boolean testsSubtypeSensitiveVars = false;
232:
233: public boolean testsSubtypeSensitiveVars(Test aTest) {
234: aTest.accept(this );
235: return this .testsSubtypeSensitiveVars;
236: }
237:
238: public void visit(Instanceof i) {
239: ReflectionVar v = (ReflectionVar) i.getVar();
240: Object varUnderTest = v.getBindingAtJoinPoint(this Obj,
241: targetObj, argsObjs);
242: if ((varUnderTest == this Obj)
243: || (varUnderTest == targetObj)) {
244: this .testsSubtypeSensitiveVars = true;
245: }
246: }
247:
248: public void visit(HasAnnotation hasAnn) {
249: // If you thought things were bad before, now we sink to new levels of horror...
250: ReflectionVar v = (ReflectionVar) hasAnn.getVar();
251: int varType = getVarType(v);
252: if ((varType == AT_THIS_VAR) || (varType == AT_TARGET_VAR)
253: || (varType == AT_ANNOTATION_VAR)) {
254: this .testsSubtypeSensitiveVars = true;
255: }
256: }
257: }
258:
259: }
|