KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > codehaus > aspectwerkz > transform > inlining > InliningWeavingStrategy


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 JavaDoc;
13 import java.util.HashSet JavaDoc;
14 import java.util.Iterator JavaDoc;
15 import java.util.List JavaDoc;
16 import java.util.Set JavaDoc;
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 JavaDoc className, final Context context) {
61         try {
62             final byte[] bytecode = context.getInitialBytecode();
63             final ClassLoader JavaDoc loader = context.getLoader();
64
65             ClassInfo classInfo = AsmClassInfo.getClassInfo(bytecode, loader);
66
67             // skip Java reflect proxies for which we cannot get the resource as a stream
68
// which leads to warnings when using annotation matching
69
// Note: we use an heuristic assuming JDK proxy are classes named "$..."
70
// to avoid to call getSuperClass everytime
71
if (classInfo.getName().startsWith("$") && classInfo.getSuperclass().getName().equals("java.lang.reflect.Proxy")) {
72                 context.setCurrentBytecode(context.getInitialBytecode());
73                 return;
74             }
75
76             //TODO:FIXME match on (within, null, classInfo) should be equivalent to those ones.
77
final Set JavaDoc 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             // build the ClassInfo from the bytecode to avoid loading it from the loader resource stream later
93
// to support stub weaving
94
//AsmClassInfo.getClassInfo(bytecode, loader);
95

96             // compute CALL + GET/SET early matching results to avoid registering useless visitors
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             );//FIXME - within make match all
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             );//FIXME - within make match all
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             );//FIXME - within make match all
116

117             // note: for staticinitialization we do an exact match right there
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             // prepare ctor call jp
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             // prepare handler jp, by gathering ALL catch blocks and their exception type
147
List JavaDoc catchLabels = new ArrayList JavaDoc();
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                 // we must visit exactly as we will do further on with debug info (that produces extra labels)
157
crLookahead2.accept(lookForCatches, Attributes.getDefaultAttributes(), false);
158             }
159
160             // gather wrapper methods to support multi-weaving
161
// skip annotations visit and debug info by using the lookahead read-only classreader
162
Set JavaDoc addedMethods = new HashSet JavaDoc();
163             crLookahead.accept(new AlreadyAddedMethodAdapter(addedMethods), true);
164
165             // -- Phase 1 -- type change
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             // update the class info
175
classInfo = AsmClassInfo.newClassInfo(bytesPhase1, loader);
176
177             // -- Phase 2 -- advices
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             // -- Phase 3 -- serialUID and JoinPoint initialization
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             // TODO: INNER CLASS OR NOT?
218
// loop over emitted jp and flag them as inner classes
219
// for (Iterator iterator = ((ContextImpl) context).getEmittedInlinedJoinPoint().iterator(); iterator.hasNext();) {
220
// String joinPointClassName = ((ContextImpl.EmittedJoinPoint) iterator.next()).joinPointClassName;
221
// int innerIndex = joinPointClassName.lastIndexOf('$');
222
// cw.visitInnerClass(joinPointClassName,
223
// joinPointClassName.substring(0, innerIndex),
224
// joinPointClassName.substring(innerIndex + 1, joinPointClassName.length()),
225
// Constants.ACC_PUBLIC + Constants.ACC_STATIC);
226
// }
227

228 // // resolve line numbers - debug only
229
// List ejp = ((ContextImpl)context).getEmittedJoinPoints();
230
// for (Iterator iterator = ejp.iterator(); iterator.hasNext();) {
231
// EmittedJoinPoint emittedJoinPoint = (EmittedJoinPoint) iterator.next();
232
// emittedJoinPoint.resolveLineNumber(context);
233
// System.out.println(emittedJoinPoint.toString());
234
// }
235

236             // NOTE: remove when in release time or in debugging trouble (;-) - Alex)
237
// FAKE multiweaving - which is a requirement
238
// Object multi = context.getMetaData("FAKE");
239
// if (multi == null) {
240
// context.addMetaData("FAKE", "FAKE");
241
// transform(className, context);
242
// }
243

244         } catch (Throwable JavaDoc 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 JavaDoc name, final byte[] bytecode, final ClassLoader JavaDoc 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 JavaDoc definitions,
271                                        final ExpressionContext[] ctxs,
272                                        final ClassInfo classInfo) {
273         if (classInfo.isInterface()) {
274             return true;
275         }
276         for (Iterator JavaDoc 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 JavaDoc 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 JavaDoc definitions,
325                                           final ExpressionContext[] ctxs) {
326         for (Iterator JavaDoc 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 JavaDoc definitions,
345                                        final ExpressionContext ctx) {
346         for (Iterator JavaDoc 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 }
Popular Tags