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.ClassReader;
007: import com.tc.asm.ClassVisitor;
008: import com.tc.asm.ClassWriter;
009: import com.tc.asm.MethodVisitor;
010: import com.tc.asm.Opcodes;
011: import com.tc.asm.Type;
012:
013: import com.tc.aspectwerkz.exception.WrappedRuntimeException;
014: import com.tc.aspectwerkz.transform.TransformationConstants;
015: import com.tc.aspectwerkz.transform.inlining.AsmHelper;
016: import com.tc.aspectwerkz.transform.inlining.AsmNullAdapter;
017:
018: import java.io.IOException;
019: import java.io.InputStream;
020: import java.util.HashSet;
021: import java.util.Set;
022:
023: /**
024: * Compile a proxy class for the delegation strategy.
025: * <p/>
026: * All interfaces methods are taken in the given interface order and implemented using delegation.
027: * A single constructor is compiled wich accept each interface as argument
028: *
029: * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
030: */
031: public class ProxyDelegationCompiler implements TransformationConstants {
032:
033: /**
034: * Compile the proxy
035: *
036: * @param loader
037: * @param interfaces
038: * @param proxyClassName
039: * @return
040: */
041: public static byte[] compileProxyFor(final ClassLoader loader,
042: final Class[] interfaces, final String proxyClassName) {
043: final ClassWriter proxyWriter = AsmHelper.newClassWriter(true);
044:
045: final Set methodSignatures = new HashSet();
046: final String[] interfaceClassNames = new String[interfaces.length];
047: final String[] interfaceSignatures = new String[interfaces.length];
048: for (int i = 0; i < interfaces.length; i++) {
049: interfaceClassNames[i] = interfaces[i].getName().replace(
050: '.', '/');
051: interfaceSignatures[i] = 'L' + interfaceClassNames[i] + ';';
052: }
053:
054: //FIXME copy interfaces class level annotations, and make sure we ignore doublons if any
055: ProxyCompilerClassVisitor createProxy = new ProxyDelegationCompiler.ProxyCompilerClassVisitor(
056: proxyWriter, proxyClassName.replace('.', '/'),
057: methodSignatures, interfaceClassNames,
058: interfaceSignatures);
059:
060: // visit each interface
061: for (int i = 0; i < interfaces.length; i++) {
062: Class anInterface = interfaces[i];
063: final String interfaceClassName = anInterface.getName()
064: .replace('.', '/');
065:
066: InputStream in = null;
067: final ClassReader classReader;
068: try {
069: if (loader != null) {
070: in = loader.getResourceAsStream(interfaceClassName
071: + ".class");
072: } else {
073: in = ClassLoader.getSystemClassLoader()
074: .getResourceAsStream(
075: interfaceClassName + ".class");
076: }
077: classReader = new ClassReader(in);
078: } catch (IOException e) {
079: throw new WrappedRuntimeException(
080: "Cannot compile proxy for " + anInterface, e);
081: } finally {
082: try {
083: in.close();
084: } catch (Throwable t) {
085: // ignore
086: }
087: }
088: classReader.accept(createProxy, ClassReader.SKIP_DEBUG
089: | ClassReader.SKIP_FRAMES); // no need for debug info
090: }
091: return proxyWriter.toByteArray();
092: }
093:
094: /**
095: * Proxy compiler. Ones can call accept as many times as needed.
096: * visitEnd allow to track the index of the visited interface
097: */
098: public static class ProxyCompilerClassVisitor extends
099: AsmNullAdapter.NullClassAdapter implements Opcodes,
100: TransformationConstants {
101: final ClassVisitor m_proxyCv;
102: final String m_proxyClassName;
103: final Set m_signatures;
104: private int currentInterfaceIndex = 0;
105: final String[] m_interfaceClassNames;
106: final String[] m_interfaceSignatures;
107:
108: /**
109: * Create the class, a field per interface, and the single constructor
110: *
111: * @param proxyCv
112: * @param proxyClassName
113: * @param signatures
114: * @param interfaceClassNames
115: */
116: public ProxyCompilerClassVisitor(final ClassVisitor proxyCv,
117: final String proxyClassName, final Set signatures,
118: final String[] interfaceClassNames,
119: final String[] interfaceSignatures) {
120: m_proxyCv = proxyCv;
121: m_proxyClassName = proxyClassName;
122: m_signatures = signatures;
123: m_interfaceClassNames = interfaceClassNames;
124: m_interfaceSignatures = interfaceSignatures;
125:
126: m_proxyCv.visit(AsmHelper.JAVA_VERSION, ACC_PUBLIC
127: + ACC_SYNTHETIC + ACC_SUPER, m_proxyClassName,
128: null, OBJECT_CLASS_NAME, interfaceClassNames);
129:
130: // create one field per implemented interface
131: for (int i = 0; i < interfaceClassNames.length; i++) {
132: m_interfaceSignatures[i] = 'L' + interfaceClassNames[i] + ';';
133: m_proxyCv.visitField(ACC_PRIVATE + ACC_SYNTHETIC
134: + ACC_FINAL, "DELEGATE_" + i,
135: m_interfaceSignatures[i], null, null);
136: }
137:
138: // create ctor
139: StringBuffer ctorDesc = new StringBuffer("(");
140: for (int i = 0; i < interfaceClassNames.length; i++) {
141: ctorDesc.append(m_interfaceSignatures[i]);
142: }
143: ctorDesc.append(")V");
144: MethodVisitor init = m_proxyCv.visitMethod(ACC_PUBLIC
145: + ACC_SYNTHETIC, INIT_METHOD_NAME, ctorDesc
146: .toString(), null, null);
147: init.visitVarInsn(ALOAD, 0);
148: init.visitMethodInsn(INVOKESPECIAL, OBJECT_CLASS_NAME,
149: INIT_METHOD_NAME, NO_PARAM_RETURN_VOID_SIGNATURE);
150: for (int i = 0; i < interfaceClassNames.length; i++) {
151: init.visitVarInsn(ALOAD, 0);
152: init.visitVarInsn(ALOAD, 1 + i);
153: init.visitFieldInsn(PUTFIELD, m_proxyClassName,
154: "DELEGATE_" + i, m_interfaceSignatures[i]);
155: }
156: init.visitInsn(RETURN);
157: init.visitMaxs(0, 0);
158: }
159:
160: /**
161: * Implement the interface method by delegating to the corresponding field
162: *
163: * @param access
164: * @param name
165: * @param desc
166: * @param signature
167: * @param exceptions
168: * @return
169: */
170: public MethodVisitor visitMethod(int access, String name,
171: String desc, String signature, String[] exceptions) {
172: if (m_signatures.contains(name + desc)) {
173: return super .visitMethod(access, name, desc, signature,
174: exceptions);
175: }
176: m_signatures.add(name + desc);
177:
178: MethodVisitor cv = m_proxyCv.visitMethod(access
179: & ~ACC_ABSTRACT, name, desc, signature, exceptions);
180:
181: cv.visitVarInsn(ALOAD, 0);
182: cv.visitFieldInsn(GETFIELD, m_proxyClassName, "DELEGATE_"
183: + currentInterfaceIndex,
184: m_interfaceSignatures[currentInterfaceIndex]);
185: AsmHelper.loadArgumentTypes(cv,
186: Type.getArgumentTypes(desc), false);
187: cv.visitMethodInsn(INVOKEINTERFACE,
188: m_interfaceClassNames[currentInterfaceIndex], name,
189: desc);
190: AsmHelper.addReturnStatement(cv, Type.getReturnType(desc));
191: cv.visitMaxs(0, 0);
192:
193: // as we return cv we will copy the interface[currentInterfaceIndex] current method annotations
194: // which is what we want
195: return cv;
196: }
197:
198: /**
199: * Update the interface index for the next accept()
200: */
201: public void visitEnd() {
202: currentInterfaceIndex++;
203: super.visitEnd();
204: }
205: }
206:
207: }
|