    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    }