View Javadoc
1   /*
2    * Copyright 2002-2013 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.aop.aspectj;
18  
19  import java.lang.reflect.Field;
20  
21  import org.aspectj.weaver.ReferenceType;
22  import org.aspectj.weaver.ReferenceTypeDelegate;
23  import org.aspectj.weaver.ResolvedType;
24  import org.aspectj.weaver.ast.And;
25  import org.aspectj.weaver.ast.Call;
26  import org.aspectj.weaver.ast.FieldGetCall;
27  import org.aspectj.weaver.ast.HasAnnotation;
28  import org.aspectj.weaver.ast.ITestVisitor;
29  import org.aspectj.weaver.ast.Instanceof;
30  import org.aspectj.weaver.ast.Literal;
31  import org.aspectj.weaver.ast.Not;
32  import org.aspectj.weaver.ast.Or;
33  import org.aspectj.weaver.ast.Test;
34  import org.aspectj.weaver.internal.tools.MatchingContextBasedTest;
35  import org.aspectj.weaver.reflect.ReflectionBasedReferenceTypeDelegate;
36  import org.aspectj.weaver.reflect.ReflectionVar;
37  import org.aspectj.weaver.reflect.ShadowMatchImpl;
38  import org.aspectj.weaver.tools.ShadowMatch;
39  
40  import org.springframework.util.ClassUtils;
41  import org.springframework.util.ReflectionUtils;
42  
43  /**
44   * This class encapsulates some AspectJ internal knowledge that should be
45   * pushed back into the AspectJ project in a future release.
46   *
47   * <p>It relies on implementation specific knowledge in AspectJ to break
48   * encapsulation and do something AspectJ was not designed to do: query
49   * the types of runtime tests that will be performed. The code here should
50   * migrate to {@code ShadowMatch.getVariablesInvolvedInRuntimeTest()}
51   * or some similar operation.
52   *
53   * <p>See <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=151593"/>Bug 151593</a>
54   *
55   * @author Adrian Colyer
56   * @author Ramnivas Laddad
57   * @since 2.0
58   */
59  class RuntimeTestWalker {
60  
61  	private static final Field residualTestField;
62  
63  	private static final Field varTypeField;
64  
65  	private static final Field myClassField;
66  
67  
68  	static {
69  		try {
70  			residualTestField = ShadowMatchImpl.class.getDeclaredField("residualTest");
71  			varTypeField = ReflectionVar.class.getDeclaredField("varType");
72  			myClassField = ReflectionBasedReferenceTypeDelegate.class.getDeclaredField("myClass");
73  		}
74  		catch (NoSuchFieldException ex) {
75  			throw new IllegalStateException("The version of aspectjtools.jar / aspectjweaver.jar " +
76  					"on the classpath is incompatible with this version of Spring: " + ex);
77  		}
78  	}
79  
80  
81  	private final Test runtimeTest;
82  
83  
84  	public RuntimeTestWalker(ShadowMatch shadowMatch) {
85  		try {
86  			ReflectionUtils.makeAccessible(residualTestField);
87  			this.runtimeTest = (Test) residualTestField.get(shadowMatch);
88  		}
89  		catch (IllegalAccessException ex) {
90  			throw new IllegalStateException(ex);
91  		}
92  	}
93  
94  
95  	/**
96  	 * If the test uses any of the this, target, at_this, at_target, and at_annotation vars,
97  	 * then it tests subtype sensitive vars.
98  	 */
99  	public boolean testsSubtypeSensitiveVars() {
100 		return (this.runtimeTest != null &&
101 				new SubtypeSensitiveVarTypeTestVisitor().testsSubtypeSensitiveVars(this.runtimeTest));
102 	}
103 
104 	public boolean testThisInstanceOfResidue(Class<?> thisClass) {
105 		return (this.runtimeTest != null &&
106 				new ThisInstanceOfResidueTestVisitor(thisClass).thisInstanceOfMatches(this.runtimeTest));
107 	}
108 
109 	public boolean testTargetInstanceOfResidue(Class<?> targetClass) {
110 		return (this.runtimeTest != null &&
111 				new TargetInstanceOfResidueTestVisitor(targetClass).targetInstanceOfMatches(this.runtimeTest));
112 	}
113 
114 
115 	private static class TestVisitorAdapter implements ITestVisitor {
116 
117 		protected static final int THIS_VAR = 0;
118 		protected static final int TARGET_VAR = 1;
119 		protected static final int AT_THIS_VAR = 3;
120 		protected static final int AT_TARGET_VAR = 4;
121 		protected static final int AT_ANNOTATION_VAR = 8;
122 
123 		@Override
124 		public void visit(And e) {
125 			e.getLeft().accept(this);
126 			e.getRight().accept(this);
127 		}
128 
129 		@Override
130 		public void visit(Or e) {
131 			e.getLeft().accept(this);
132 			e.getRight().accept(this);
133 		}
134 
135 		@Override
136 		public void visit(Not e) {
137 			e.getBody().accept(this);
138 		}
139 
140 		@Override
141 		public void visit(Instanceof i) {
142 		}
143 
144 		@Override
145 		public void visit(Literal literal) {
146 		}
147 
148 		@Override
149 		public void visit(Call call) {
150 		}
151 
152 		@Override
153 		public void visit(FieldGetCall fieldGetCall) {
154 		}
155 
156 		@Override
157 		public void visit(HasAnnotation hasAnnotation) {
158 		}
159 
160 		@Override
161 		public void visit(MatchingContextBasedTest matchingContextTest) {
162 		}
163 
164 		protected int getVarType(ReflectionVar v) {
165 			try {
166 				ReflectionUtils.makeAccessible(varTypeField);
167 				return (Integer) varTypeField.get(v);
168 			}
169 			catch (IllegalAccessException ex) {
170 				throw new IllegalStateException(ex);
171 			}
172 		}
173 	}
174 
175 
176 	private static abstract class InstanceOfResidueTestVisitor extends TestVisitorAdapter {
177 
178 		private final Class<?> matchClass;
179 
180 		private boolean matches;
181 
182 		private final int matchVarType;
183 
184 		public InstanceOfResidueTestVisitor(Class<?> matchClass, boolean defaultMatches, int matchVarType) {
185 			this.matchClass = matchClass;
186 			this.matches = defaultMatches;
187 			this.matchVarType = matchVarType;
188 		}
189 
190 		public boolean instanceOfMatches(Test test) {
191 			test.accept(this);
192 			return this.matches;
193 		}
194 
195 		@Override
196 		public void visit(Instanceof i) {
197 			int varType = getVarType((ReflectionVar) i.getVar());
198 			if (varType != this.matchVarType) {
199 				return;
200 			}
201 			Class<?> typeClass = null;
202 			ResolvedType type = (ResolvedType) i.getType();
203 			if (type instanceof ReferenceType) {
204 				ReferenceTypeDelegate delegate = ((ReferenceType) type).getDelegate();
205 				if (delegate instanceof ReflectionBasedReferenceTypeDelegate) {
206 					try {
207 						ReflectionUtils.makeAccessible(myClassField);
208 						typeClass = (Class<?>) myClassField.get(delegate);
209 					}
210 					catch (IllegalAccessException ex) {
211 						throw new IllegalStateException(ex);
212 					}
213 				}
214 			}
215 			try {
216 				// Don't use ResolvedType.isAssignableFrom() as it won't be aware of (Spring) mixins
217 				if (typeClass == null) {
218 					typeClass = ClassUtils.forName(type.getName(), this.matchClass.getClassLoader());
219 				}
220 				this.matches = typeClass.isAssignableFrom(this.matchClass);
221 			}
222 			catch (ClassNotFoundException ex) {
223 				this.matches = false;
224 			}
225 		}
226 	}
227 
228 
229 	/**
230 	 * Check if residue of target(TYPE) kind. See SPR-3783 for more details.
231 	 */
232 	private static class TargetInstanceOfResidueTestVisitor extends InstanceOfResidueTestVisitor {
233 
234 		public TargetInstanceOfResidueTestVisitor(Class<?> targetClass) {
235 			super(targetClass, false, TARGET_VAR);
236 		}
237 
238 		public boolean targetInstanceOfMatches(Test test) {
239 			return instanceOfMatches(test);
240 		}
241 	}
242 
243 
244 	/**
245 	 * Check if residue of this(TYPE) kind. See SPR-2979 for more details.
246 	 */
247 	private static class ThisInstanceOfResidueTestVisitor extends InstanceOfResidueTestVisitor {
248 
249 		public ThisInstanceOfResidueTestVisitor(Class<?> thisClass) {
250 			super(thisClass, true, THIS_VAR);
251 		}
252 
253 		// TODO: Optimization: Process only if this() specifies a type and not an identifier.
254 		public boolean thisInstanceOfMatches(Test test) {
255 			return instanceOfMatches(test);
256 		}
257 	}
258 
259 
260 	private static class SubtypeSensitiveVarTypeTestVisitor extends TestVisitorAdapter {
261 
262 		private final Object thisObj = new Object();
263 
264 		private final Object targetObj = new Object();
265 
266 		private final Object[] argsObjs = new Object[0];
267 
268 		private boolean testsSubtypeSensitiveVars = false;
269 
270 		public boolean testsSubtypeSensitiveVars(Test aTest) {
271 			aTest.accept(this);
272 			return this.testsSubtypeSensitiveVars;
273 		}
274 
275 		@Override
276 		public void visit(Instanceof i) {
277 			ReflectionVar v = (ReflectionVar) i.getVar();
278 			Object varUnderTest = v.getBindingAtJoinPoint(this.thisObj, this.targetObj, this.argsObjs);
279 			if (varUnderTest == this.thisObj || varUnderTest == this.targetObj) {
280 				this.testsSubtypeSensitiveVars = true;
281 			}
282 		}
283 
284 		@Override
285 		public void visit(HasAnnotation hasAnn) {
286 			// If you thought things were bad before, now we sink to new levels of horror...
287 			ReflectionVar v = (ReflectionVar) hasAnn.getVar();
288 			int varType = getVarType(v);
289 			if (varType == AT_THIS_VAR || varType == AT_TARGET_VAR || varType == AT_ANNOTATION_VAR) {
290 				this.testsSubtypeSensitiveVars = true;
291 			}
292 		}
293 	}
294 
295 }