001: /*
002: * Copyright 2002-2006 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.ReflectionWorld;
035: import org.aspectj.weaver.reflect.ShadowMatchImpl;
036: import org.aspectj.weaver.tools.ShadowMatch;
037:
038: /**
039: * <p>This class encapsulates some AspectJ internal knowledge that should be
040: * pushed back into the AspectJ project in a future release.
041: *
042: * <p>It relies on implementation specific knowledge in AspectJ to break
043: * encapsulation and do something AspectJ was not designed to do :- query
044: * the types of runtime tests that will be performed. The code here should
045: * migrate to ShadowMatch.getVariablesInvolvedInRuntimeTest() or some similar
046: * operation.
047: *
048: * <p>See https://bugs.eclipse.org/bugs/show_bug.cgi?id=151593
049: *
050: * @author Adrian Colyer
051: * @author Ramnivas Laddad
052: * @since 2.0
053: */
054: public class RuntimeTestWalker {
055:
056: private Test runtimeTest;
057:
058: public RuntimeTestWalker(ShadowMatch shadowMatch) {
059: ShadowMatchImpl shadowMatchImplementation = (ShadowMatchImpl) shadowMatch;
060: try {
061: Field testField = shadowMatchImplementation.getClass()
062: .getDeclaredField("residualTest");
063: testField.setAccessible(true);
064: this .runtimeTest = (Test) testField.get(shadowMatch);
065: } catch (NoSuchFieldException noSuchFieldEx) {
066: throw new IllegalStateException(
067: "the version of aspectjtools.jar / aspectjweaver.jar "
068: + "on the classpath is incompatible with this version of Spring:- expected field "
069: + "'runtimeTest' is not present on ShadowMatchImpl class");
070: } catch (IllegalAccessException illegalAccessEx) {
071: // famous last words... but I don't see how this can happen given the
072: // setAccessible call above
073: throw new IllegalStateException(
074: "Unable to access ShadowMatchImpl.runtimeTest field.");
075: }
076: }
077:
078: /**
079: * If the test uses any of the this, target, at_this, at_target, and at_annotation vars,
080: * then it tests subtype sensitive vars.
081: */
082: public boolean testsSubtypeSensitiveVars() {
083: return new SubtypeSensitiveVarTypeTestVisitor()
084: .testsSubtypeSensitiveVars(this .runtimeTest);
085: }
086:
087: public boolean testThisInstanceOfResidue(Object thiz) {
088: return new ThisInstanceOfResidueTestVisitor(thiz)
089: .this InstanceOfMatches(this .runtimeTest);
090: }
091:
092: private static class TestVisitorAdapter implements ITestVisitor {
093: protected static final int THIS_VAR = 0;
094: protected static final int AT_THIS_VAR = 3;
095: protected static final int AT_TARGET_VAR = 4;
096: protected static final int AT_ANNOTATION_VAR = 8;
097:
098: public void visit(And e) {
099: e.getLeft().accept(this );
100: e.getRight().accept(this );
101:
102: }
103:
104: public void visit(Or e) {
105: e.getLeft().accept(this );
106: e.getRight().accept(this );
107: }
108:
109: public void visit(Not e) {
110: e.getBody().accept(this );
111: }
112:
113: public void visit(Instanceof i) {
114: }
115:
116: public void visit(Literal literal) {
117: }
118:
119: public void visit(Call call) {
120: }
121:
122: public void visit(FieldGetCall fieldGetCall) {
123: }
124:
125: public void visit(HasAnnotation hasAnnotation) {
126: }
127:
128: public void visit(MatchingContextBasedTest matchingContextTest) {
129: }
130:
131: protected int getVarType(ReflectionVar v) {
132: try {
133: Field varTypeField = ReflectionVar.class
134: .getDeclaredField("varType");
135: varTypeField.setAccessible(true);
136: Integer varTypeValue = (Integer) varTypeField.get(v);
137: int varType = varTypeValue.intValue();
138: return varType;
139: } catch (NoSuchFieldException noSuchFieldEx) {
140: throw new IllegalStateException(
141: "the version of aspectjtools.jar / aspectjweaver.jar "
142: + "on the classpath is incompatible with this version of Spring:- expected field "
143: + "'varType' is not present on ReflectionVar class");
144: } catch (IllegalAccessException illegalAccessEx) {
145: // famous last words... but I don't see how this can happen given the setAccessible call
146: // above
147: throw new IllegalStateException(
148: "Unable to access ReflectionVar.varType field.");
149: }
150: }
151: }
152:
153: /**
154: *
155: * Check if residue of this(TYPE) kind. See SPR-2979 for more details.
156: *
157: */
158: private static class ThisInstanceOfResidueTestVisitor extends
159: TestVisitorAdapter {
160: private Object thiz;
161: private boolean matches = true;
162:
163: public ThisInstanceOfResidueTestVisitor(Object thiz) {
164: this .thiz = thiz;
165: }
166:
167: public boolean this InstanceOfMatches(Test test) {
168: test.accept(this );
169: return matches;
170: }
171:
172: public void visit(Instanceof i) {
173: ResolvedType type = (ResolvedType) i.getType();
174: int varType = getVarType((ReflectionVar) i.getVar());
175: // We are concerned only about this() pointcut
176: // TODO: Optimization: Process only if this() specifies a type and not identifier
177: if (varType != THIS_VAR) {
178: return;
179: }
180:
181: try {
182: Class typeClass = Class.forName(type.getName());
183: // Don't use ReflectionType.isAssignableFrom() as it won't be aware of (Spring) mixins
184: if (!typeClass.isAssignableFrom(thiz.getClass())) {
185: matches = false;
186: }
187: } catch (ClassNotFoundException ex) {
188: matches = false;
189: }
190: }
191:
192: }
193:
194: private static class SubtypeSensitiveVarTypeTestVisitor extends
195: TestVisitorAdapter {
196: private final Object this Obj = new Object();
197: private final Object targetObj = new Object();
198: private final Object[] argsObjs = new Object[0];
199:
200: private boolean testsSubtypeSensitiveVars = false;
201:
202: public boolean testsSubtypeSensitiveVars(Test aTest) {
203: aTest.accept(this );
204: return this .testsSubtypeSensitiveVars;
205: }
206:
207: public void visit(Instanceof i) {
208: ReflectionVar v = (ReflectionVar) i.getVar();
209: Object varUnderTest = v.getBindingAtJoinPoint(this Obj,
210: targetObj, argsObjs);
211: if ((varUnderTest == this Obj)
212: || (varUnderTest == targetObj)) {
213: this .testsSubtypeSensitiveVars = true;
214: }
215: }
216:
217: public void visit(HasAnnotation hasAnn) {
218: // if you thought things were bad before, now we sink to new levels
219: // of horror...
220: ReflectionVar v = (ReflectionVar) hasAnn.getVar();
221: int varType = getVarType(v);
222: if ((varType == AT_THIS_VAR) || (varType == AT_TARGET_VAR)
223: || (varType == AT_ANNOTATION_VAR)) {
224: this .testsSubtypeSensitiveVars = true;
225: }
226: }
227: }
228: }
|