001 /*****************************************************************************
002 * Copyright (C) PicoContainer Organization. All rights reserved. *
003 * ------------------------------------------------------------------------- *
004 * The software in this package is published under the terms of the BSD *
005 * style license a copy of which has been included with this distribution in *
006 * the LICENSE.txt file. *
007 * *
008 * Original code by *
009 *****************************************************************************/
010 package org.picocontainer.gems.behaviors;
011
012 import java.lang.reflect.Constructor;
013 import java.lang.reflect.InvocationTargetException;
014 import java.lang.reflect.Method;
015 import java.util.HashSet;
016 import java.util.Set;
017 import java.util.concurrent.Future;
018
019 import org.objectweb.asm.ClassWriter;
020 import org.objectweb.asm.FieldVisitor;
021 import org.objectweb.asm.MethodVisitor;
022 import org.objectweb.asm.Opcodes;
023 import org.objectweb.asm.Type;
024 import org.picocontainer.ComponentAdapter;
025 import org.picocontainer.PicoContainer;
026 import org.picocontainer.behaviors.AbstractBehavior;
027 import org.picocontainer.behaviors.Cached;
028
029
030 /**
031 * This component adapter makes it possible to hide the implementation of a real subject (behind a proxy).
032 * The proxy will implement all the interfaces of the
033 * underlying subject. If you want caching,
034 * use a {@link Cached} around this one.
035 *
036 * @author Paul Hammant
037 */
038 @SuppressWarnings("serial")
039 public class AsmHiddenImplementation<T> extends AbstractBehavior<T> implements Opcodes {
040
041
042 public AsmHiddenImplementation(final ComponentAdapter<T> delegate) {
043 super(delegate);
044 }
045
046 @Override
047 public T getComponentInstance(final PicoContainer container, final java.lang.reflect.Type into) {
048 T o = getDelegate().getComponentInstance(container, into);
049 Class[] interfaces = o.getClass().getInterfaces();
050 if (interfaces.length != 0) {
051 byte[] bytes = makeProxy("XX", interfaces, true);
052 AsmClassLoader cl = new AsmClassLoader(HotSwappable.Swappable.class.getClassLoader());
053 Class<?> pClazz = cl.defineClass("XX", bytes);
054 try {
055 Constructor<T> ctor = (Constructor<T>) pClazz.getConstructor(HotSwappable.Swappable.class);
056 final HotSwappable.Swappable swappable = getSwappable();
057 swappable.swap(o);
058 return ctor.newInstance(swappable);
059 } catch (NoSuchMethodException e) {
060 } catch (InstantiationException e) {
061 } catch (IllegalAccessException e) {
062 } catch (InvocationTargetException e) {
063 }
064 }
065 return o;
066 }
067
068 public String getDescriptor() {
069 return "Hidden";
070 }
071
072 protected HotSwappable.Swappable getSwappable() {
073 return new HotSwappable.Swappable();
074 }
075
076 public byte[] makeProxy(final String proxyName, final Class[] interfaces, final boolean setter) {
077
078 ClassWriter cw = new ClassWriter(0);
079 FieldVisitor fv;
080
081 Class<Object> superclass = Object.class;
082
083 cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, proxyName, null, Type.getInternalName(superclass), getNames(interfaces));
084
085 {
086 fv = cw.visitField(ACC_PRIVATE + ACC_TRANSIENT, "swappable", encodedClassName(HotSwappable.Swappable.class), null, null);
087 fv.visitEnd();
088 }
089 doConstructor(proxyName, cw);
090 Set<String> methodsDone = new HashSet<String>();
091 for (Class<?> iface : interfaces) {
092 Method[] meths = iface.getMethods();
093 for (Method meth : meths) {
094 if (!methodsDone.contains(meth.toString())) {
095 doMethod(proxyName, cw, iface, meth);
096 methodsDone.add(meth.toString());
097 }
098 }
099 }
100
101 cw.visitEnd();
102
103 return cw.toByteArray();
104 }
105
106 private String[] getNames(final Class[] interfaces) {
107 String[] retVal = new String[interfaces.length];
108 for (int i = 0; i < interfaces.length; i++) {
109 retVal[i] = Type.getInternalName(interfaces[i]);
110 }
111 return retVal;
112 }
113
114 private void doConstructor(final String proxyName, final ClassWriter cw) {
115 MethodVisitor mv;
116 mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(L"+ Type.getInternalName(HotSwappable.Swappable.class)+";)V", null, null);
117 mv.visitCode();
118 mv.visitVarInsn(ALOAD, 0);
119 mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
120 mv.visitVarInsn(ALOAD, 0);
121 mv.visitVarInsn(ALOAD, 1);
122 mv.visitFieldInsn(PUTFIELD, proxyName, "swappable", encodedClassName(HotSwappable.Swappable.class));
123 mv.visitInsn(RETURN);
124 mv.visitMaxs(2, 2);
125 mv.visitEnd();
126 }
127
128 private void doMethod(final String proxyName, final ClassWriter cw, final Class<?> iface, final Method meth) {
129
130 String cn = Type.getInternalName(iface);
131 String sig = Type.getMethodDescriptor(meth);
132
133 String[] exceptions = encodedExceptionNames(meth.getExceptionTypes());
134 MethodVisitor mv;
135 mv = cw.visitMethod(ACC_PUBLIC, meth.getName(), sig, null, exceptions);
136 mv.visitCode();
137 mv.visitVarInsn(ALOAD, 0);
138 mv.visitFieldInsn(GETFIELD, proxyName, "swappable", encodedClassName(HotSwappable.Swappable.class));
139 mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(HotSwappable.Swappable.class), "getInstance", "()Ljava/lang/Object;");
140 mv.visitTypeInsn(CHECKCAST, cn);
141 Class[] types = meth.getParameterTypes();
142 int ix = 1;
143 for (Class type : types) {
144 int load = whichLoad(type);
145 mv.visitVarInsn(load, ix);
146 ix = indexOf(ix, load);
147 }
148 mv.visitMethodInsn(INVOKEINTERFACE, cn, meth.getName(), sig);
149 mv.visitInsn(whichReturn(meth.getReturnType()));
150 mv.visitMaxs(ix, ix);
151 mv.visitEnd();
152 }
153
154 private int indexOf(final int ix, final int loadType) {
155 if (loadType == LLOAD) {
156 return ix + 2;
157 } else if (loadType == DLOAD) {
158 return ix + 2;
159 } else if (loadType == ILOAD) {
160 return ix + 1;
161 } else if (loadType == ALOAD) {
162 return ix + 1;
163 } else if (loadType == FLOAD) {
164 return ix + 1;
165 }
166 return 0;
167 }
168
169 private String[] encodedExceptionNames(final Class[] exceptionTypes) {
170 if (exceptionTypes.length == 0) {
171 return null;
172 }
173 String[] retVal = new String[exceptionTypes.length];
174 for (int i = 0; i < exceptionTypes.length; i++) {
175 Class clazz = exceptionTypes[i];
176 retVal[i] = Type.getInternalName(clazz);
177 }
178 return retVal;
179 }
180
181 private int whichReturn(final Class<?> clazz) {
182 if (!clazz.isPrimitive()) {
183 return ARETURN;
184 } else if (clazz.isArray()) {
185 return ARETURN;
186 } else if (clazz == int.class) {
187 return IRETURN;
188 } else if (clazz == long.class) {
189 return LRETURN;
190 } else if (clazz == byte.class) {
191 return IRETURN;
192 } else if (clazz == float.class) {
193 return FRETURN;
194 } else if (clazz == double.class) {
195 return DRETURN;
196 } else if (clazz == char.class) {
197 return IRETURN;
198 } else if (clazz == short.class) {
199 return IRETURN;
200 } else if (clazz == boolean.class) {
201 return IRETURN;
202 } else if (clazz == void.class) {
203 return RETURN;
204 } else {
205 return 0;
206 }
207 }
208
209 private int whichLoad(final Class<?> clazz) {
210 if (!clazz.isPrimitive()) {
211 return ALOAD;
212 } else if (clazz.isArray()) {
213 return ALOAD;
214 } else if (clazz == int.class) {
215 return ILOAD;
216 } else if (clazz == long.class) {
217 return LLOAD;
218 } else if (clazz == byte.class) {
219 return ILOAD;
220 } else if (clazz == float.class) {
221 return FLOAD;
222 } else if (clazz == double.class) {
223 return DLOAD;
224 } else if (clazz == char.class) {
225 return ILOAD;
226 } else if (clazz == short.class) {
227 return ILOAD;
228 } else if (clazz == boolean.class) {
229 return ILOAD;
230 } else {
231 return 0;
232 }
233 }
234
235 private String encodedClassName(final Class<?> clazz) {
236 if (clazz.getName().startsWith("[")) {
237 return Type.getInternalName(clazz);
238 } else if (!clazz.isPrimitive()) {
239 return "L" + Type.getInternalName(clazz) + ";";
240 } else if (clazz == int.class) {
241 return "I";
242 } else if (clazz == long.class) {
243 return "J";
244 } else if (clazz == byte.class) {
245 return "B";
246 } else if (clazz == float.class) {
247 return "F";
248 } else if (clazz == double.class) {
249 return "D";
250 } else if (clazz == char.class) {
251 return "C";
252 } else if (clazz == short.class) {
253 return "S";
254 } else if (clazz == boolean.class) {
255 return "Z";
256 } else if (clazz == void.class) {
257 return "V";
258 } else {
259 return null;
260 }
261 }
262
263 private static class AsmClassLoader extends ClassLoader {
264
265 public AsmClassLoader(final ClassLoader parent) {
266 super(parent);
267 }
268
269 public Class<?> defineClass(final String name, final byte[] b) {
270 return defineClass(name, b, 0, b.length);
271 }
272 }
273
274 }