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 Bonr</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 }