001: /**************************************************************************************
002: * Copyright (c) Jonas Bonér, Alexandre Vasseur. All rights reserved. *
003: * http://aspectwerkz.codehaus.org *
004: * ---------------------------------------------------------------------------------- *
005: * The software in this package is published under the terms of the LGPL license *
006: * a copy of which has been included with this distribution in the license.txt file. *
007: **************************************************************************************/package org.codehaus.aspectwerkz.proxy;
008:
009: import java.lang.reflect.Method;
010: import java.lang.reflect.Modifier;
011: import java.lang.reflect.Constructor;
012: import java.io.IOException;
013: import java.io.InputStream;
014: import java.io.BufferedInputStream;
015: import java.io.ByteArrayInputStream;
016:
017: import org.codehaus.aspectwerkz.transform.TransformationConstants;
018: import org.codehaus.aspectwerkz.transform.inlining.AsmHelper;
019: import org.codehaus.aspectwerkz.reflect.ReflectHelper;
020: import org.codehaus.aspectwerkz.util.ContextClassLoader;
021: import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;
022: import org.codehaus.aspectwerkz.annotation.instrumentation.asm.AsmAnnotationHelper;
023: import org.objectweb.asm.ClassWriter;
024: import org.objectweb.asm.CodeVisitor;
025: import org.objectweb.asm.Type;
026: import org.objectweb.asm.ClassReader;
027: import org.objectweb.asm.ClassAdapter;
028: import org.objectweb.asm.ClassVisitor;
029: import org.objectweb.asm.Attribute;
030: import org.objectweb.asm.attrs.Attributes;
031:
032: /**
033: * Compiler for the AspectWerkz proxies.
034: * <p/>
035: * Creates a subclass of the target class and adds delegate methods to all the non-private and non-final
036: * methods/constructors which delegates to the super class.
037: * <p/>
038: * The annotations are copied.
039: *
040: * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
041: * @author <a href="mailto:jboner@codehaus.org">Jonas Bonr</a>
042: */
043: public class ProxyCompiler implements TransformationConstants {
044: private final static String[] EMPTY_STRING_ARRAY = new String[0];
045:
046: /**
047: * Returns an InputStream that would be the one of the AWproxy for the given proxy class name
048: * Used to read annotations from proxy f.e.
049: *
050: * @param loader
051: * @param proxyClassName
052: * @return or null if not found
053: */
054: public static InputStream getProxyResourceAsStream(
055: final ClassLoader loader, final String proxyClassName) {
056: String className = Proxy
057: .getUniqueClassNameFromProxy(proxyClassName);
058: if (className != null) {
059: byte[] proxy = compileProxyFor(loader, className,
060: proxyClassName);
061: return new BufferedInputStream(new ByteArrayInputStream(
062: proxy));
063: } else {
064: return null;
065: }
066: }
067:
068: /**
069: * Compiles a new proxy for the class specified.
070: *
071: * @param clazz
072: * @param proxyClassName
073: * @return the byte code
074: */
075: public static byte[] compileProxyFor(final Class clazz,
076: final String proxyClassName) {
077: return compileProxyFor(clazz.getClassLoader(), clazz.getName(),
078: proxyClassName);
079: }
080:
081: /**
082: * Compiles a new proxy for the class specified.
083: *
084: * @param loader
085: * @param className
086: * @param proxyClassName
087: * @return the byte code
088: */
089: public static byte[] compileProxyFor(final ClassLoader loader,
090: final String className, final String proxyClassName) {
091:
092: final String targetClassName = className.replace('.', '/');
093: final ClassWriter proxyWriter = AsmHelper.newClassWriter(true);
094:
095: InputStream in = null;
096: final ClassReader classReader;
097: try {
098: if (loader != null) {
099: in = loader.getResourceAsStream(targetClassName
100: + ".class");
101: } else {
102: in = ClassLoader
103: .getSystemClassLoader()
104: .getResourceAsStream(targetClassName + ".class");
105: }
106: classReader = new ClassReader(in);
107: } catch (IOException e) {
108: throw new WrappedRuntimeException(
109: "Cannot compile proxy for " + className, e);
110: } finally {
111: try {
112: in.close();
113: } catch (Throwable t) {
114: ;
115: }
116: }
117:
118: ClassVisitor createProxy = new ProxyCompilerClassVisitor(
119: proxyWriter, proxyClassName.replace('.', '/'));
120: classReader.accept(createProxy, Attributes
121: .getDefaultAttributes(), true);// no need for debug info
122: return proxyWriter.toByteArray();
123: }
124:
125: /**
126: * Visit the class and create the proxy that delegates to super.
127: *
128: * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
129: */
130: private static class ProxyCompilerClassVisitor extends
131: AsmAnnotationHelper.NullClassAdapter {
132:
133: final private ClassVisitor m_proxyCv;
134: final private String m_proxyClassName;
135: private String m_className;
136:
137: public ProxyCompilerClassVisitor(final ClassVisitor proxyCv,
138: final String proxyClassName) {
139: m_proxyCv = proxyCv;
140: m_proxyClassName = proxyClassName;
141: }
142:
143: /**
144: * Visits the class.
145: *
146: * @param access
147: * @param name
148: * @param superName
149: * @param interfaces
150: * @param sourceFile
151: */
152: public void visit(final int version, final int access,
153: final String name, final String super Name,
154: final String[] interfaces, final String sourceFile) {
155: if (Modifier.isFinal(access)) {
156: throw new RuntimeException(
157: "Cannot create a proxy from final class "
158: + name);
159: }
160: m_className = name;
161: m_proxyCv.visit(version, ACC_PUBLIC + ACC_SUPER
162: + ACC_SYNTHETIC,
163: m_proxyClassName.replace('.', '/'), name,
164: interfaces, null);
165: }
166:
167: /**
168: * Visits the methods.
169: *
170: * @param access
171: * @param name
172: * @param desc
173: * @param exceptions
174: * @param attrs
175: * @return
176: */
177: public CodeVisitor visitMethod(final int access,
178: final String name, final String desc,
179: final String[] exceptions, final Attribute attrs) {
180: if (Modifier.isFinal(access) || Modifier.isPrivate(access)
181: || Modifier.isNative(access)) {
182: // skip final or private or native methods
183: // TODO we could proxy native methods but that would lead to difference with regular weaving
184: ;
185: } else if (CLINIT_METHOD_NAME.equals(name)) {
186: // skip clinit
187: ;
188: } else if (INIT_METHOD_NAME.equals(name)) {
189: // constructors
190: CodeVisitor proxyCode = m_proxyCv.visitMethod(access
191: + ACC_SYNTHETIC, INIT_METHOD_NAME, desc,
192: exceptions, attrs);
193:
194: proxyCode.visitVarInsn(ALOAD, 0);
195: AsmHelper.loadArgumentTypes(proxyCode, Type
196: .getArgumentTypes(desc), false);
197: proxyCode.visitMethodInsn(INVOKESPECIAL, m_className,
198: INIT_METHOD_NAME, desc);
199: proxyCode.visitInsn(RETURN);
200: proxyCode.visitMaxs(0, 0);
201: } else {
202: // method that can be proxied
203: CodeVisitor proxyCode = m_proxyCv.visitMethod(access
204: + ACC_SYNTHETIC, name, desc, exceptions, attrs);
205:
206: if (Modifier.isStatic(access)) {
207: AsmHelper.loadArgumentTypes(proxyCode, Type
208: .getArgumentTypes(desc), true);
209: proxyCode.visitMethodInsn(INVOKESTATIC,
210: m_className, name, desc);
211: AsmHelper.addReturnStatement(proxyCode, Type
212: .getReturnType(desc));
213: proxyCode.visitMaxs(0, 0);
214: } else {
215: proxyCode.visitVarInsn(ALOAD, 0);
216: AsmHelper.loadArgumentTypes(proxyCode, Type
217: .getArgumentTypes(desc), false);
218: proxyCode.visitMethodInsn(INVOKESPECIAL,
219: m_className, name, desc);
220: AsmHelper.addReturnStatement(proxyCode, Type
221: .getReturnType(desc));
222: proxyCode.visitMaxs(0, 0);
223: }
224: }
225:
226: return AsmAnnotationHelper.NullCodeAdapter.NULL_CODE_ADAPTER;
227: }
228: }
229:
230: // /**
231: // * Creates constructors that delgates to the matching base class constructors.
232: // * Skips all private constructors.
233: // *
234: // * @param writer
235: // * @param clazz
236: // * @param targetClassName
237: // */
238: // private static void createConstructorDelegators(final ClassWriter writer,
239: // final Class clazz,
240: // final String targetClassName) {
241: // CodeVisitor cv;
242: // Constructor[] constructors = clazz.getDeclaredConstructors();
243: // for (int i = 0; i < constructors.length; i++) {
244: // Constructor constructor = constructors[i];
245: // int mods = constructor.getModifiers();
246: // if (!Modifier.isPrivate(mods) && !Modifier.isFinal(mods)) {
247: // Class[] exceptionClasses = constructor.getExceptionTypes();
248: // String[] exceptionTypeNames = new String[constructor.getExceptionTypes().length];
249: // for (int j = 0; j < exceptionTypeNames.length; j++) {
250: // exceptionTypeNames[j] = exceptionClasses[j].getName().replace('.', '/');
251: // }
252: // final String desc = ReflectHelper.getConstructorSignature(constructor);
253: // cv = writer.visitMethod(
254: // mods + ACC_SYNTHETIC,
255: // INIT_METHOD_NAME,
256: // desc,
257: // exceptionTypeNames,
258: // null
259: // );
260: //
261: // cv.visitVarInsn(ALOAD, 0);
262: // AsmHelper.loadArgumentTypes(cv, Type.getArgumentTypes(desc), false);
263: //
264: // cv.visitMethodInsn(INVOKESPECIAL, targetClassName, INIT_METHOD_NAME, desc);
265: //
266: // cv.visitInsn(RETURN);
267: // cv.visitMaxs(0, 0);
268: // }
269: // }
270: // }
271: //
272: // /**
273: // * Creates method methods that delgates to the base class method.
274: // * Skips all private and final methods.
275: // *
276: // * @param writer
277: // * @param clazz
278: // * @param targetClassName
279: // */
280: // private static void createMethodDelegators(final ClassWriter writer,
281: // final Class clazz,
282: // final String targetClassName) {
283: // CodeVisitor cv;
284: // Method[] methods = clazz.getDeclaredMethods();
285: // for (int i = 0; i < methods.length; i++) {
286: // Method method = methods[i];
287: // int mods = method.getModifiers();
288: // if (!Modifier.isPrivate(mods) && !Modifier.isFinal(mods)) {
289: //
290: // Class[] exceptionClasses = method.getExceptionTypes();
291: // String[] exceptionTypeNames = new String[method.getExceptionTypes().length];
292: // for (int j = 0; j < exceptionTypeNames.length; j++) {
293: // exceptionTypeNames[j] = exceptionClasses[j].getName().replace('.', '/');
294: // }
295: // final String methodName = method.getName();
296: // final String desc = Type.getMethodDescriptor(method);
297: //
298: // cv = writer.visitMethod(
299: // mods + ACC_SYNTHETIC,
300: // methodName,
301: // desc,
302: // exceptionTypeNames,
303: // null
304: // );
305: //
306: // if (Modifier.isStatic(mods)) {
307: // AsmHelper.loadArgumentTypes(cv, Type.getArgumentTypes(desc), true);
308: // cv.visitMethodInsn(INVOKESTATIC, targetClassName, methodName, desc);
309: // } else {
310: // cv.visitVarInsn(ALOAD, 0);
311: // AsmHelper.loadArgumentTypes(cv, Type.getArgumentTypes(desc), false);
312: // cv.visitMethodInsn(INVOKESPECIAL, targetClassName, methodName, desc);
313: // }
314: //
315: // AsmHelper.addReturnStatement(cv, Type.getReturnType(method));
316: // cv.visitMaxs(0, 0);
317: // }
318: // }
319: // }
320: }
|