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 *****************************************************************************/
010package org.picocontainer.gems.behaviors;
011
012import java.lang.reflect.Constructor;
013import java.lang.reflect.InvocationTargetException;
014import java.lang.reflect.Method;
015import java.util.HashSet;
016import java.util.Set;
017import java.util.concurrent.Future;
018
019import org.objectweb.asm.ClassWriter;
020import org.objectweb.asm.FieldVisitor;
021import org.objectweb.asm.MethodVisitor;
022import org.objectweb.asm.Opcodes;
023import org.objectweb.asm.Type;
024import org.picocontainer.ComponentAdapter;
025import org.picocontainer.PicoContainer;
026import org.picocontainer.behaviors.AbstractBehavior;
027import 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")
039public 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}