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);
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
175
176 ;
177 } else if (CLINIT_METHOD_NAME.equals(name)) {
178
179 ;
180 } else if (INIT_METHOD_NAME.equals(name)) {
181
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
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
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
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
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313 }