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 8of 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.transform.inlining;
9
10 import gnu.trove.TLongObjectHashMap;
11
12 import java.util.ArrayList;
13 import java.util.HashSet;
14 import java.util.Iterator;
15 import java.util.List;
16 import java.util.Set;
17
18 import org.codehaus.aspectwerkz.definition.SystemDefinition;
19 import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;
20 import org.codehaus.aspectwerkz.expression.ExpressionContext;
21 import org.codehaus.aspectwerkz.expression.PointcutType;
22 import org.codehaus.aspectwerkz.reflect.ClassInfo;
23 import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo;
24 import org.codehaus.aspectwerkz.transform.Context;
25 import org.codehaus.aspectwerkz.transform.WeavingStrategy;
26 import org.codehaus.aspectwerkz.transform.inlining.weaver.AddInterfaceVisitor;
27 import org.codehaus.aspectwerkz.transform.inlining.weaver.AddMixinMethodsVisitor;
28 import org.codehaus.aspectwerkz.transform.inlining.weaver.AlreadyAddedMethodAdapter;
29 import org.codehaus.aspectwerkz.transform.inlining.weaver.ConstructorBodyVisitor;
30 import org.codehaus.aspectwerkz.transform.inlining.weaver.ConstructorCallVisitor;
31 import org.codehaus.aspectwerkz.transform.inlining.weaver.FieldSetFieldGetVisitor;
32 import org.codehaus.aspectwerkz.transform.inlining.weaver.HandlerVisitor;
33 import org.codehaus.aspectwerkz.transform.inlining.weaver.InstanceLevelAspectVisitor;
34 import org.codehaus.aspectwerkz.transform.inlining.weaver.JoinPointInitVisitor;
35 import org.codehaus.aspectwerkz.transform.inlining.weaver.LabelToLineNumberVisitor;
36 import org.codehaus.aspectwerkz.transform.inlining.weaver.MethodCallVisitor;
37 import org.codehaus.aspectwerkz.transform.inlining.weaver.MethodExecutionVisitor;
38 import org.codehaus.aspectwerkz.transform.inlining.weaver.StaticInitializationVisitor;
39 import org.codehaus.aspectwerkz.transform.inlining.weaver.SerialVersionUidVisitor;
40 import org.codehaus.aspectwerkz.transform.inlining.weaver.AddWrapperVisitor;
41 import org.objectweb.asm.ClassReader;
42 import org.objectweb.asm.ClassVisitor;
43 import org.objectweb.asm.ClassWriter;
44 import org.objectweb.asm.attrs.Attributes;
45
46 /***
47 * A weaving strategy implementing a weaving scheme based on statical compilation, and no reflection.
48 *
49 * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
50 * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
51 */
52 public class InliningWeavingStrategy implements WeavingStrategy {
53
54 /***
55 * Performs the weaving of the target class.
56 *
57 * @param className
58 * @param context
59 */
60 public void transform(String className, final Context context) {
61 try {
62 final byte[] bytecode = context.getInitialBytecode();
63 final ClassLoader loader = context.getLoader();
64
65 ClassInfo classInfo = AsmClassInfo.getClassInfo(bytecode, loader);
66
67
68
69
70
71 if (classInfo.getName().startsWith("$") && classInfo.getSuperclass().getName().equals("java.lang.reflect.Proxy")) {
72 context.setCurrentBytecode(context.getInitialBytecode());
73 return;
74 }
75
76
77 final Set definitions = context.getDefinitions();
78 final ExpressionContext[] ctxs = new ExpressionContext[]{
79 new ExpressionContext(PointcutType.EXECUTION, classInfo, classInfo),
80 new ExpressionContext(PointcutType.CALL, null, classInfo),
81 new ExpressionContext(PointcutType.GET, null, classInfo),
82 new ExpressionContext(PointcutType.SET, null, classInfo),
83 new ExpressionContext(PointcutType.HANDLER, null, classInfo),
84 new ExpressionContext(PointcutType.STATIC_INITIALIZATION, classInfo, classInfo),
85 new ExpressionContext(PointcutType.WITHIN, classInfo, classInfo)
86 };
87
88 if (classFilter(definitions, ctxs, classInfo)) {
89 return;
90 }
91
92
93
94
95
96
97 final boolean filterForCall = classFilterFor(
98 definitions, new ExpressionContext[]{
99 new ExpressionContext(PointcutType.CALL, null, classInfo),
100 new ExpressionContext(PointcutType.WITHIN, classInfo, classInfo)
101 }
102 );
103 final boolean filterForGetSet = classFilterFor(
104 definitions, new ExpressionContext[]{
105 new ExpressionContext(PointcutType.GET, null, classInfo),
106 new ExpressionContext(PointcutType.SET, null, classInfo),
107 new ExpressionContext(PointcutType.WITHIN, classInfo, classInfo)
108 }
109 );
110 final boolean filterForHandler = classFilterFor(
111 definitions, new ExpressionContext[]{
112 new ExpressionContext(PointcutType.HANDLER, null, classInfo),
113 new ExpressionContext(PointcutType.WITHIN, classInfo, classInfo)
114 }
115 );
116
117
118 boolean filterForStaticinitialization = !classInfo.hasStaticInitializer()
119 || classFilterFor(definitions, new ExpressionContext[] {
120 new ExpressionContext(PointcutType.STATIC_INITIALIZATION,
121 classInfo.staticInitializer(),
122 classInfo)
123 }
124 );
125 if (!filterForStaticinitialization) {
126 filterForStaticinitialization = !hasPointcut(definitions, new ExpressionContext(
127 PointcutType.STATIC_INITIALIZATION,
128 classInfo.staticInitializer(),
129 classInfo
130 ));
131 }
132
133
134 final ClassReader crLookahead = new ClassReader(bytecode);
135 TLongObjectHashMap newInvocationsByCallerMemberHash = null;
136 if (!filterForCall) {
137 newInvocationsByCallerMemberHash = new TLongObjectHashMap();
138 crLookahead.accept(
139 new ConstructorCallVisitor.LookaheadNewDupInvokeSpecialInstructionClassAdapter(
140 newInvocationsByCallerMemberHash
141 ),
142 true
143 );
144 }
145
146
147 List catchLabels = new ArrayList();
148 if (!filterForHandler) {
149 final ClassReader crLookahead2 = new ClassReader(bytecode);
150 final ClassWriter cw2 = AsmHelper.newClassWriter(true);
151
152 HandlerVisitor.LookaheadCatchLabelsClassAdapter lookForCatches =
153 new HandlerVisitor.LookaheadCatchLabelsClassAdapter(
154 cw2, loader, classInfo, context, catchLabels
155 );
156
157 crLookahead2.accept(lookForCatches, Attributes.getDefaultAttributes(), false);
158 }
159
160
161
162 Set addedMethods = new HashSet();
163 crLookahead.accept(new AlreadyAddedMethodAdapter(addedMethods), true);
164
165
166 final ClassWriter writerPhase1 = AsmHelper.newClassWriter(true);
167 final ClassReader readerPhase1 = new ClassReader(bytecode);
168 ClassVisitor reversedChainPhase1 = writerPhase1;
169 reversedChainPhase1 = new AddMixinMethodsVisitor(reversedChainPhase1, classInfo, context, addedMethods);
170 reversedChainPhase1 = new AddInterfaceVisitor(reversedChainPhase1, classInfo, context);
171 readerPhase1.accept(reversedChainPhase1, Attributes.getDefaultAttributes(), false);
172 final byte[] bytesPhase1 = writerPhase1.toByteArray();
173
174
175 classInfo = AsmClassInfo.newClassInfo(bytesPhase1, loader);
176
177
178 final ClassWriter writerPhase2 = AsmHelper.newClassWriter(true);
179 final ClassReader readerPhase2 = new ClassReader(bytesPhase1);
180 ClassVisitor reversedChainPhase2 = writerPhase2;
181 reversedChainPhase2 = new InstanceLevelAspectVisitor(reversedChainPhase2, classInfo, context);
182 reversedChainPhase2 = new MethodExecutionVisitor(reversedChainPhase2, classInfo, context, addedMethods);
183 reversedChainPhase2 = new ConstructorBodyVisitor(reversedChainPhase2, classInfo, context, addedMethods);
184 if(!filterForStaticinitialization) {
185 reversedChainPhase2 = new StaticInitializationVisitor(reversedChainPhase2, context, addedMethods);
186 }
187 reversedChainPhase2 = new HandlerVisitor(reversedChainPhase2, context, catchLabels);
188 if (!filterForCall) {
189 reversedChainPhase2 = new MethodCallVisitor(reversedChainPhase2, loader, classInfo, context);
190 reversedChainPhase2 = new ConstructorCallVisitor(
191 reversedChainPhase2, loader, classInfo, context, newInvocationsByCallerMemberHash
192 );
193 }
194 if (!filterForGetSet) {
195 reversedChainPhase2 = new FieldSetFieldGetVisitor(reversedChainPhase2, loader, classInfo, context);
196 }
197 reversedChainPhase2 = new LabelToLineNumberVisitor(reversedChainPhase2, context);
198 readerPhase2.accept(reversedChainPhase2, Attributes.getDefaultAttributes(), false);
199 final byte[] bytesPhase2 = writerPhase2.toByteArray();
200
201 context.setCurrentBytecode(bytesPhase2);
202
203
204 if (context.isAdvised()) {
205 final ClassWriter writerPhase3 = AsmHelper.newClassWriter(true);
206 ClassReader readerPhase3 = new ClassReader(bytesPhase2);
207 ClassVisitor reversedChainPhase3 = writerPhase3;
208 reversedChainPhase3 = new SerialVersionUidVisitor.Add(reversedChainPhase3, context, classInfo);
209 reversedChainPhase3 = new AddWrapperVisitor(reversedChainPhase3, context, addedMethods);
210 reversedChainPhase3 = new JoinPointInitVisitor(reversedChainPhase3, context);
211 readerPhase3.accept(reversedChainPhase3, Attributes.getDefaultAttributes(), false);
212 final byte[] bytesPhase3 = writerPhase3.toByteArray();
213
214 context.setCurrentBytecode(bytesPhase3);
215 }
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244 } catch (Throwable t) {
245 t.printStackTrace();
246 throw new WrappedRuntimeException(t);
247 }
248 }
249
250 /***
251 * Creates a new transformation context.
252 *
253 * @param name
254 * @param bytecode
255 * @param loader
256 * @return
257 */
258 public Context newContext(final String name, final byte[] bytecode, final ClassLoader loader) {
259 return new ContextImpl(name, bytecode, loader);
260 }
261
262 /***
263 * Filters out the classes that are not eligible for transformation.
264 *
265 * @param definitions the definitions
266 * @param ctxs an array with the contexts
267 * @param classInfo the class to filter
268 * @return boolean true if the class should be filtered out
269 */
270 private static boolean classFilter(final Set definitions,
271 final ExpressionContext[] ctxs,
272 final ClassInfo classInfo) {
273 if (classInfo.isInterface()) {
274 return true;
275 }
276 for (Iterator defs = definitions.iterator(); defs.hasNext();) {
277 if (classFilter((SystemDefinition) defs.next(), ctxs, classInfo)) {
278 continue;
279 } else {
280 return false;
281 }
282 }
283 return true;
284 }
285
286 /***
287 * Filters out the classes that are not eligible for transformation.
288 *
289 * @param definition the definition
290 * @param ctxs an array with the contexts
291 * @param classInfo the class to filter
292 * @return boolean true if the class should be filtered out
293 * @TODO: when a class had execution pointcut that were removed it must be unweaved, thus not filtered out How to
294 * handle that? cache lookup? or custom class level attribute ?
295 */
296 private static boolean classFilter(final SystemDefinition definition,
297 final ExpressionContext[] ctxs,
298 final ClassInfo classInfo) {
299 if (classInfo.isInterface()) {
300 return true;
301 }
302 String className = classInfo.getName();
303 if (definition.inExcludePackage(className)) {
304 return true;
305 }
306 if (!definition.inIncludePackage(className)) {
307 return true;
308 }
309 if (definition.isAdvised(ctxs)) {
310 return false;
311 }
312 if (definition.hasMixin(ctxs)) {
313 return false;
314 }
315 if (definition.hasIntroducedInterface(ctxs)) {
316 return false;
317 }
318 if (definition.inPreparePackage(className)) {
319 return false;
320 }
321 return true;
322 }
323
324 private static boolean classFilterFor(final Set definitions,
325 final ExpressionContext[] ctxs) {
326 for (Iterator defs = definitions.iterator(); defs.hasNext();) {
327 if (classFilterFor((SystemDefinition) defs.next(), ctxs)) {
328 continue;
329 } else {
330 return false;
331 }
332 }
333 return true;
334 }
335
336 private static boolean classFilterFor(final SystemDefinition definition,
337 final ExpressionContext[] ctxs) {
338 if (definition.isAdvised(ctxs)) {
339 return false;
340 }
341 return true;
342 }
343
344 private static boolean hasPointcut(final Set definitions,
345 final ExpressionContext ctx) {
346 for (Iterator defs = definitions.iterator(); defs.hasNext();) {
347 if (hasPointcut((SystemDefinition) defs.next(), ctx)) {
348 return true;
349 } else {
350 continue;
351 }
352 }
353 return false;
354 }
355
356 private static boolean hasPointcut(final SystemDefinition definition,
357 final ExpressionContext ctx) {
358 if (definition.hasPointcut(ctx)) {
359 return true;
360 }
361 return false;
362 }
363 }