001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
003: */
004: package com.tc.aspectwerkz.proxy;
005:
006: import com.tc.asm.AnnotationVisitor;
007: import com.tc.asm.Attribute;
008: import com.tc.asm.ClassReader;
009: import com.tc.asm.ClassVisitor;
010: import com.tc.asm.ClassWriter;
011: import com.tc.asm.MethodVisitor;
012: import com.tc.asm.Type;
013: import com.tc.aspectwerkz.exception.WrappedRuntimeException;
014: import com.tc.aspectwerkz.transform.TransformationConstants;
015: import com.tc.aspectwerkz.transform.inlining.AsmCopyAdapter;
016: import com.tc.aspectwerkz.transform.inlining.AsmHelper;
017: import com.tc.aspectwerkz.transform.inlining.AsmNullAdapter;
018:
019: import java.io.IOException;
020: import java.io.InputStream;
021: import java.lang.reflect.Modifier;
022:
023: /**
024: * Compiler for the AspectWerkz proxies.
025: * <p/>
026: * Creates a subclass of the target class and adds delegate methods to all the non-private and non-final
027: * methods/constructors which delegates to the super class.
028: * <p/>
029: * The annotations are copied.
030: *
031: * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
032: * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr</a>
033: */
034: public class ProxySubclassingCompiler implements
035: TransformationConstants {
036:
037: /**
038: * Compiles a new proxy for the class specified.
039: *
040: * @param clazz
041: * @param proxyClassName
042: * @return the byte code
043: */
044: public static byte[] compileProxyFor(final Class clazz,
045: final String proxyClassName) {
046: return compileProxyFor(clazz.getClassLoader(), clazz.getName(),
047: proxyClassName);
048: }
049:
050: /**
051: * Compiles a new proxy for the class specified.
052: *
053: * @param loader
054: * @param className
055: * @param proxyClassName
056: * @return the byte code
057: */
058: public static byte[] compileProxyFor(final ClassLoader loader,
059: final String className, final String proxyClassName) {
060:
061: final String targetClassName = className.replace('.', '/');
062: final ClassWriter proxyWriter = AsmHelper.newClassWriter(true);
063:
064: InputStream in = null;
065: final ClassReader classReader;
066: try {
067: if (loader != null) {
068: in = loader.getResourceAsStream(targetClassName
069: + ".class");
070: } else {
071: in = ClassLoader
072: .getSystemClassLoader()
073: .getResourceAsStream(targetClassName + ".class");
074: }
075: classReader = new ClassReader(in);
076: } catch (IOException e) {
077: throw new WrappedRuntimeException(
078: "Cannot compile proxy for " + className, e);
079: } finally {
080: try {
081: in.close();
082: } catch (Throwable t) {
083: // ignore
084: }
085: }
086:
087: ClassVisitor createProxy = new ProxyCompilerClassVisitor(
088: proxyWriter, proxyClassName.replace('.', '/'));
089: classReader.accept(createProxy, ClassReader.SKIP_DEBUG
090: | ClassReader.SKIP_FRAMES);// no need for debug info
091: return proxyWriter.toByteArray();
092: }
093:
094: /**
095: * Visit the class and create the proxy that delegates to super.
096: *
097: * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
098: */
099: private static class ProxyCompilerClassVisitor extends
100: AsmNullAdapter.NullClassAdapter {
101:
102: final private ClassVisitor m_proxyCv;
103: final private String m_proxyClassName;
104: private String m_className;
105:
106: public ProxyCompilerClassVisitor(final ClassVisitor proxyCv,
107: final String proxyClassName) {
108: m_proxyCv = proxyCv;
109: m_proxyClassName = proxyClassName;
110: }
111:
112: /**
113: * Visits the class.
114: *
115: * @param access
116: * @param name
117: * @param signature
118: * @param superName
119: * @param interfaces
120: */
121: public void visit(final int version, final int access,
122: final String name, final String signature,
123: final String super Name, final String[] interfaces) {
124: if (Modifier.isFinal(access)) {
125: throw new RuntimeException(
126: "Cannot create a proxy from final class "
127: + name);
128: }
129: m_className = name;
130: m_proxyCv.visit(version, ACC_PUBLIC + ACC_SUPER
131: + ACC_SYNTHETIC,
132: m_proxyClassName.replace('.', '/'), signature,
133: name, interfaces);
134: }
135:
136: /**
137: * Visits the methods.
138: *
139: * @param access
140: * @param name
141: * @param desc
142: * @param signature
143: * @param exceptions
144: * @return
145: */
146: public MethodVisitor visitMethod(final int access,
147: final String name, final String desc,
148: final String signature, final String[] exceptions) {
149: final MethodVisitor proxyCode;
150: final boolean copyAnnotation;
151: if (Modifier.isFinal(access) || Modifier.isPrivate(access)
152: || Modifier.isNative(access)) {
153: // skip final or private or native methods
154: // TODO we could proxy native methods but that would lead to difference with regular weaving
155: proxyCode = AsmNullAdapter.NullMethodAdapter.NULL_METHOD_ADAPTER;
156: copyAnnotation = false;
157: } else if (CLINIT_METHOD_NAME.equals(name)) {
158: // skip clinit
159: proxyCode = AsmNullAdapter.NullMethodAdapter.NULL_METHOD_ADAPTER;
160: copyAnnotation = false;
161: } else if (INIT_METHOD_NAME.equals(name)) {
162: // constructors
163: proxyCode = m_proxyCv.visitMethod(access
164: + ACC_SYNTHETIC, INIT_METHOD_NAME, desc,
165: signature, exceptions);
166:
167: proxyCode.visitVarInsn(ALOAD, 0);
168: AsmHelper.loadArgumentTypes(proxyCode, Type
169: .getArgumentTypes(desc), false);
170: proxyCode.visitMethodInsn(INVOKESPECIAL, m_className,
171: INIT_METHOD_NAME, desc);
172: proxyCode.visitInsn(RETURN);
173: proxyCode.visitMaxs(0, 0);
174: copyAnnotation = true;
175: } else {
176: // method that can be proxied
177: proxyCode = m_proxyCv.visitMethod(access
178: + ACC_SYNTHETIC, name, desc, signature,
179: exceptions);
180:
181: if (Modifier.isStatic(access)) {
182: AsmHelper.loadArgumentTypes(proxyCode, Type
183: .getArgumentTypes(desc), true);
184: proxyCode.visitMethodInsn(INVOKESTATIC,
185: m_className, name, desc);
186: AsmHelper.addReturnStatement(proxyCode, Type
187: .getReturnType(desc));
188: proxyCode.visitMaxs(0, 0);
189: } else {
190: proxyCode.visitVarInsn(ALOAD, 0);
191: AsmHelper.loadArgumentTypes(proxyCode, Type
192: .getArgumentTypes(desc), false);
193: proxyCode.visitMethodInsn(INVOKESPECIAL,
194: m_className, name, desc);
195: AsmHelper.addReturnStatement(proxyCode, Type
196: .getReturnType(desc));
197: proxyCode.visitMaxs(0, 0);
198: }
199: copyAnnotation = true;
200: }
201:
202: // copy the annotation if necessary
203: if (copyAnnotation) {
204: return new AsmCopyAdapter.CopyMethodAnnotationElseNullAdapter(
205: proxyCode);
206: } else {
207: return AsmNullAdapter.NullMethodAdapter.NULL_METHOD_ADAPTER;
208: }
209: }
210:
211: /**
212: * Visit the class annotation (copy)
213: *
214: * @param desc
215: * @param visible
216: * @return
217: */
218: public AnnotationVisitor visitAnnotation(String desc,
219: boolean visible) {
220: return new AsmCopyAdapter.CopyAnnotationAdapter(super
221: .visitAnnotation(desc, visible), m_proxyCv
222: .visitAnnotation(desc, visible));
223: }
224:
225: /**
226: * Visit the custom attribute (copy)
227: *
228: * @param attribute
229: */
230: public void visitAttribute(Attribute attribute) {
231: m_proxyCv.visitAttribute(attribute);
232: }
233: }
234: }
|