View Javadoc

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 of 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.proxy;
9   
10  import java.lang.reflect.Method;
11  import java.lang.reflect.Modifier;
12  import java.lang.reflect.Constructor;
13  import java.io.IOException;
14  import java.io.InputStream;
15  import java.io.BufferedInputStream;
16  import java.io.ByteArrayInputStream;
17  
18  import org.codehaus.aspectwerkz.transform.TransformationConstants;
19  import org.codehaus.aspectwerkz.transform.inlining.AsmHelper;
20  import org.codehaus.aspectwerkz.reflect.ReflectHelper;
21  import org.codehaus.aspectwerkz.util.ContextClassLoader;
22  import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;
23  import org.codehaus.aspectwerkz.annotation.instrumentation.asm.AsmAnnotationHelper;
24  import org.objectweb.asm.ClassWriter;
25  import org.objectweb.asm.CodeVisitor;
26  import org.objectweb.asm.Type;
27  import org.objectweb.asm.ClassReader;
28  import org.objectweb.asm.ClassAdapter;
29  import org.objectweb.asm.ClassVisitor;
30  import org.objectweb.asm.Attribute;
31  import org.objectweb.asm.attrs.Attributes;
32  
33  /***
34   * Compiler for the AspectWerkz proxies.
35   * <p/>
36   * Creates a subclass of the target class and adds delegate methods to all the non-private and non-final
37   * methods/constructors which delegates to the super class.
38   * <p/>
39   * The annotations are copied.
40   *
41   * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
42   * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr</a>
43   */
44  public class ProxyCompiler implements TransformationConstants {
45      private final static String[] EMPTY_STRING_ARRAY = new String[0];
46  
47      /***
48       * Returns an InputStream that would be the one of the AWproxy for the given proxy class name
49       * Used to read annotations from proxy f.e.
50       *
51       * @param loader
52       * @param proxyClassName
53       * @return or null if not found
54       */
55      public static InputStream getProxyResourceAsStream(final ClassLoader loader, final String proxyClassName) {
56          String className = Proxy.getUniqueClassNameFromProxy(proxyClassName);
57          if (className != null) {
58              byte[] proxy = compileProxyFor(loader, className, proxyClassName);
59              return new BufferedInputStream(new ByteArrayInputStream(proxy));
60          } else {
61              return null;
62          }
63      }
64  
65      /***
66       * Compiles a new proxy for the class specified.
67       *
68       * @param clazz
69       * @param proxyClassName
70       * @return the byte code
71       */
72      public static byte[] compileProxyFor(final Class clazz, final String proxyClassName) {
73          return compileProxyFor(clazz.getClassLoader(), clazz.getName(), proxyClassName);
74      }
75  
76      /***
77       * Compiles a new proxy for the class specified.
78       *
79       * @param loader
80       * @param className
81       * @param proxyClassName
82       * @return the byte code
83       */
84      public static byte[] compileProxyFor(final ClassLoader loader, final String className, final String proxyClassName) {
85  
86          final String targetClassName = className.replace('.', '/');
87          final ClassWriter proxyWriter = AsmHelper.newClassWriter(true);
88  
89          InputStream in = null;
90          final ClassReader classReader;
91          try {
92              if (loader != null) {
93                  in = loader.getResourceAsStream(targetClassName + ".class");
94              } else {
95                  in = ClassLoader.getSystemClassLoader().getResourceAsStream(targetClassName + ".class");
96              }
97              classReader = new ClassReader(in);
98          } catch (IOException e) {
99              throw new WrappedRuntimeException("Cannot compile proxy for " + className, e);
100         } finally {
101             try {
102                 in.close();
103             } catch (Throwable t) {
104                 ;
105             }
106         }
107 
108         ClassVisitor createProxy = new ProxyCompilerClassVisitor(proxyWriter, proxyClassName.replace('.', '/'));
109         classReader.accept(createProxy, Attributes.getDefaultAttributes(), true);// no need for debug info
110         return proxyWriter.toByteArray();
111     }
112 
113     /***
114      * Visit the class and create the proxy that delegates to super.
115      *
116      * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
117      */
118     private static class ProxyCompilerClassVisitor extends AsmAnnotationHelper.NullClassAdapter {
119 
120         final private ClassVisitor m_proxyCv;
121         final private String m_proxyClassName;
122         private String m_className;
123 
124         public ProxyCompilerClassVisitor(final ClassVisitor proxyCv, final String proxyClassName) {
125             m_proxyCv = proxyCv;
126             m_proxyClassName = proxyClassName;
127         }
128 
129         /***
130          * Visits the class.
131          *
132          * @param access
133          * @param name
134          * @param superName
135          * @param interfaces
136          * @param sourceFile
137          */
138         public void visit(final int version,
139                           final int access,
140                           final String name,
141                           final String superName,
142                           final String[] interfaces,
143                           final String sourceFile) {
144             if (Modifier.isFinal(access)) {
145                 throw new RuntimeException("Cannot create a proxy from final class "  + name);
146             }
147             m_className = name;
148             m_proxyCv.visit(
149                     version,
150                     ACC_PUBLIC + ACC_SUPER + ACC_SYNTHETIC,
151                     m_proxyClassName.replace('.', '/'),
152                     name,
153                     interfaces,
154                     null
155             );
156         }
157 
158         /***
159          * Visits the methods.
160          *
161          * @param access
162          * @param name
163          * @param desc
164          * @param exceptions
165          * @param attrs
166          * @return
167          */
168         public CodeVisitor visitMethod(final int access,
169                                        final String name,
170                                        final String desc,
171                                        final String[] exceptions,
172                                        final Attribute attrs) {
173             if (Modifier.isFinal(access) || Modifier.isPrivate(access) || Modifier.isNative(access)) {
174                 // skip final or private or native methods
175                 // TODO we could proxy native methods but that would lead to difference with regular weaving
176                 ;
177             } else if (CLINIT_METHOD_NAME.equals(name)) {
178                 // skip clinit
179                 ;
180             } else if (INIT_METHOD_NAME.equals(name)) {
181                 // constructors
182                 CodeVisitor proxyCode = m_proxyCv.visitMethod(
183                         access + ACC_SYNTHETIC,
184                         INIT_METHOD_NAME,
185                         desc,
186                         exceptions,
187                         attrs
188                 );
189 
190                 proxyCode.visitVarInsn(ALOAD, 0);
191                 AsmHelper.loadArgumentTypes(proxyCode, Type.getArgumentTypes(desc), false);
192                 proxyCode.visitMethodInsn(INVOKESPECIAL, m_className, INIT_METHOD_NAME, desc);
193                 proxyCode.visitInsn(RETURN);
194                 proxyCode.visitMaxs(0, 0);
195             } else {
196                 // method that can be proxied
197                 CodeVisitor proxyCode = m_proxyCv.visitMethod(
198                         access + ACC_SYNTHETIC,
199                         name,
200                         desc,
201                         exceptions,
202                         attrs
203                 );
204 
205                 if (Modifier.isStatic(access)) {
206                     AsmHelper.loadArgumentTypes(proxyCode, Type.getArgumentTypes(desc), true);
207                     proxyCode.visitMethodInsn(INVOKESTATIC, m_className, name, desc);
208                     AsmHelper.addReturnStatement(proxyCode, Type.getReturnType(desc));
209                     proxyCode.visitMaxs(0, 0);
210                 } else {
211                     proxyCode.visitVarInsn(ALOAD, 0);
212                     AsmHelper.loadArgumentTypes(proxyCode, Type.getArgumentTypes(desc), false);
213                     proxyCode.visitMethodInsn(INVOKESPECIAL, m_className, name, desc);
214                     AsmHelper.addReturnStatement(proxyCode, Type.getReturnType(desc));
215                     proxyCode.visitMaxs(0, 0);
216                 }
217             }
218 
219             return AsmAnnotationHelper.NullCodeAdapter.NULL_CODE_ADAPTER;
220         }
221     }
222 
223 //    /***
224 //     * Creates constructors that delgates to the matching base class constructors.
225 //     * Skips all private constructors.
226 //     *
227 //     * @param writer
228 //     * @param clazz
229 //     * @param targetClassName
230 //     */
231 //    private static void createConstructorDelegators(final ClassWriter writer,
232 //                                                    final Class clazz,
233 //                                                    final String targetClassName) {
234 //        CodeVisitor cv;
235 //        Constructor[] constructors = clazz.getDeclaredConstructors();
236 //        for (int i = 0; i < constructors.length; i++) {
237 //            Constructor constructor = constructors[i];
238 //            int mods = constructor.getModifiers();
239 //            if (!Modifier.isPrivate(mods) && !Modifier.isFinal(mods)) {
240 //                Class[] exceptionClasses = constructor.getExceptionTypes();
241 //                String[] exceptionTypeNames = new String[constructor.getExceptionTypes().length];
242 //                for (int j = 0; j < exceptionTypeNames.length; j++) {
243 //                    exceptionTypeNames[j] = exceptionClasses[j].getName().replace('.', '/');
244 //                }
245 //                final String desc = ReflectHelper.getConstructorSignature(constructor);
246 //                cv = writer.visitMethod(
247 //                        mods + ACC_SYNTHETIC,
248 //                        INIT_METHOD_NAME,
249 //                        desc,
250 //                        exceptionTypeNames,
251 //                        null
252 //                );
253 //
254 //                cv.visitVarInsn(ALOAD, 0);
255 //                AsmHelper.loadArgumentTypes(cv, Type.getArgumentTypes(desc), false);
256 //
257 //                cv.visitMethodInsn(INVOKESPECIAL, targetClassName, INIT_METHOD_NAME, desc);
258 //
259 //                cv.visitInsn(RETURN);
260 //                cv.visitMaxs(0, 0);
261 //            }
262 //        }
263 //    }
264 //
265 //    /***
266 //     * Creates method methods that delgates to the base class method.
267 //     * Skips all private and final methods.
268 //     *
269 //     * @param writer
270 //     * @param clazz
271 //     * @param targetClassName
272 //     */
273 //    private static void createMethodDelegators(final ClassWriter writer,
274 //                                               final Class clazz,
275 //                                               final String targetClassName) {
276 //        CodeVisitor cv;
277 //        Method[] methods = clazz.getDeclaredMethods();
278 //        for (int i = 0; i < methods.length; i++) {
279 //            Method method = methods[i];
280 //            int mods = method.getModifiers();
281 //            if (!Modifier.isPrivate(mods) && !Modifier.isFinal(mods)) {
282 //
283 //                Class[] exceptionClasses = method.getExceptionTypes();
284 //                String[] exceptionTypeNames = new String[method.getExceptionTypes().length];
285 //                for (int j = 0; j < exceptionTypeNames.length; j++) {
286 //                    exceptionTypeNames[j] = exceptionClasses[j].getName().replace('.', '/');
287 //                }
288 //                final String methodName = method.getName();
289 //                final String desc = Type.getMethodDescriptor(method);
290 //
291 //                cv = writer.visitMethod(
292 //                        mods + ACC_SYNTHETIC,
293 //                        methodName,
294 //                        desc,
295 //                        exceptionTypeNames,
296 //                        null
297 //                );
298 //
299 //                if (Modifier.isStatic(mods)) {
300 //                    AsmHelper.loadArgumentTypes(cv, Type.getArgumentTypes(desc), true);
301 //                    cv.visitMethodInsn(INVOKESTATIC, targetClassName, methodName, desc);
302 //                } else {
303 //                    cv.visitVarInsn(ALOAD, 0);
304 //                    AsmHelper.loadArgumentTypes(cv, Type.getArgumentTypes(desc), false);
305 //                    cv.visitMethodInsn(INVOKESPECIAL, targetClassName, methodName, desc);
306 //                }
307 //
308 //                AsmHelper.addReturnStatement(cv, Type.getReturnType(method));
309 //                cv.visitMaxs(0, 0);
310 //            }
311 //        }
312 //    }
313 }