/**************************************************************************************
* Copyright (c) Jonas Bon�r, Alexandre Vasseur. All rights reserved. *
* http://aspectwerkz.codehaus.org *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the LGPL license *
* a copy of which has been included with this distribution in the license.txt file. *
**************************************************************************************/
package org.codehaus.aspectwerkz.proxy;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Constructor;
import java.io.IOException;
import java.io.InputStream;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import org.codehaus.aspectwerkz.transform.TransformationConstants;
import org.codehaus.aspectwerkz.transform.inlining.AsmHelper;
import org.codehaus.aspectwerkz.reflect.ReflectHelper;
import org.codehaus.aspectwerkz.util.ContextClassLoader;
import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;
import org.codehaus.aspectwerkz.transform.inlining.AsmNullAdapter;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassVisitor;
/**
* Compiler for the AspectWerkz proxies.
* <p/>
* Creates a subclass of the target class and adds delegate methods to all the non-private and non-final
* methods/constructors which delegates to the super class.
* <p/>
* The annotations are copied.
*
* FIXME delete
*
* @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
* @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r</a>
*/
public class ProxyCompiler implements TransformationConstants {
/**
* Returns an InputStream that would be the one of the AWproxy for the given proxy class name
* Used to read annotations from proxy f.e.
*
* @param loader
* @param proxyClassName
* @return or null if not found
*/
public static InputStream getProxyResourceAsStream(final ClassLoader loader, final String proxyClassName) {
String className = null; //FIXME delete class - was - Proxy.getUniqueClassNameFromProxy(proxyClassName);
if (className != null) {
byte[] proxy = compileProxyFor(loader, className, proxyClassName);
return new BufferedInputStream(new ByteArrayInputStream(proxy));
} else {
return null;
}
}
/**
* Compiles a new proxy for the class specified.
*
* @param clazz
* @param proxyClassName
* @return the byte code
*/
public static byte[] compileProxyFor(final Class clazz, final String proxyClassName) {
return compileProxyFor(clazz.getClassLoader(), clazz.getName(), proxyClassName);
}
/**
* Compiles a new proxy for the class specified.
*
* @param loader
* @param className
* @param proxyClassName
* @return the byte code
*/
public static byte[] compileProxyFor(final ClassLoader loader, final String className, final String proxyClassName) {
final String targetClassName = className.replace('.', '/');
final ClassWriter proxyWriter = AsmHelper.newClassWriter(true);
InputStream in = null;
final ClassReader classReader;
try {
if (loader != null) {
in = loader.getResourceAsStream(targetClassName + ".class");
} else {
in = ClassLoader.getSystemClassLoader().getResourceAsStream(targetClassName + ".class");
}
classReader = new ClassReader(in);
} catch (IOException e) {
throw new WrappedRuntimeException("Cannot compile proxy for " + className, e);
} finally {
try {
in.close();
} catch (Throwable t) {
;
}
}
ClassVisitor createProxy = new ProxyCompilerClassVisitor(proxyWriter, proxyClassName.replace('.', '/'));
classReader.accept(createProxy, true);// no need for debug info
return proxyWriter.toByteArray();
}
/**
* Visit the class and create the proxy that delegates to super.
*
* @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
*/
private static class ProxyCompilerClassVisitor extends AsmNullAdapter.NullClassAdapter {
final private ClassVisitor m_proxyCv;
final private String m_proxyClassName;
private String m_className;
public ProxyCompilerClassVisitor(final ClassVisitor proxyCv, final String proxyClassName) {
m_proxyCv = proxyCv;
m_proxyClassName = proxyClassName;
}
/**
* Visits the class.
*
* @param access
* @param name
* @param signature
* @param superName
* @param interfaces
*/
public void visit(final int version,
final int access,
final String name,
final String signature,
final String superName,
final String[] interfaces) {
if (Modifier.isFinal(access)) {
throw new RuntimeException("Cannot create a proxy from final class " + name);
}
m_className = name;
m_proxyCv.visit(
version,
ACC_PUBLIC + ACC_SUPER + ACC_SYNTHETIC,
m_proxyClassName.replace('.', '/'),
signature,
name,
interfaces
);
}
/**
* Visits the methods.
*
* @param access
* @param name
* @param signature
* @param desc
* @param exceptions
* @return
*/
public MethodVisitor visitMethod(final int access,
final String name,
final String desc,
final String signature,
final String[] exceptions) {
if (Modifier.isFinal(access) || Modifier.isPrivate(access) || Modifier.isNative(access)) {
// skip final or private or native methods
// TODO we could proxy native methods but that would lead to difference with regular weaving
;
} else if (CLINIT_METHOD_NAME.equals(name)) {
// skip clinit
;
} else if (INIT_METHOD_NAME.equals(name)) {
// constructors
MethodVisitor proxyCode = m_proxyCv.visitMethod(
access + ACC_SYNTHETIC,
INIT_METHOD_NAME,
desc,
signature,
exceptions
);
proxyCode.visitVarInsn(ALOAD, 0);
AsmHelper.loadArgumentTypes(proxyCode, Type.getArgumentTypes(desc), false);
proxyCode.visitMethodInsn(INVOKESPECIAL, m_className, INIT_METHOD_NAME, desc);
proxyCode.visitInsn(RETURN);
proxyCode.visitMaxs(0, 0);
} else {
// method that can be proxied
MethodVisitor proxyCode = m_proxyCv.visitMethod(
access + ACC_SYNTHETIC,
name,
desc,
signature,
exceptions
);
if (Modifier.isStatic(access)) {
AsmHelper.loadArgumentTypes(proxyCode, Type.getArgumentTypes(desc), true);
proxyCode.visitMethodInsn(INVOKESTATIC, m_className, name, desc);
AsmHelper.addReturnStatement(proxyCode, Type.getReturnType(desc));
proxyCode.visitMaxs(0, 0);
} else {
proxyCode.visitVarInsn(ALOAD, 0);
AsmHelper.loadArgumentTypes(proxyCode, Type.getArgumentTypes(desc), false);
proxyCode.visitMethodInsn(INVOKESPECIAL, m_className, name, desc);
AsmHelper.addReturnStatement(proxyCode, Type.getReturnType(desc));
proxyCode.visitMaxs(0, 0);
}
}
return AsmNullAdapter.NullMethodAdapter.NULL_METHOD_ADAPTER;
}
}
}