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    }