All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.picocontainer.gems.behaviors.AsmHiddenImplementation Maven / Gradle / Ivy

/*****************************************************************************
 * Copyright (C) PicoContainer Organization. All rights reserved.            *
 * ------------------------------------------------------------------------- *
 * The software in this package is published under the terms of the BSD      *
 * style license a copy of which has been included with this distribution in *
 * the LICENSE.txt file.                                                     *
 *                                                                           *
 * Original code by                                                          *
 *****************************************************************************/
package org.picocontainer.gems.behaviors;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Future;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.picocontainer.ComponentAdapter;
import org.picocontainer.PicoContainer;
import org.picocontainer.behaviors.AbstractBehavior;
import org.picocontainer.behaviors.Cached;


/**
 * This component adapter makes it possible to hide the implementation of a real subject (behind a proxy).
 * The proxy will implement all the interfaces of the
 * underlying subject. If you want caching,
 * use a {@link Cached} around this one.
 *
 * @author Paul Hammant
 */
@SuppressWarnings("serial")
public class AsmHiddenImplementation extends AbstractBehavior implements Opcodes {


	public AsmHiddenImplementation(final ComponentAdapter delegate) {
        super(delegate);
    }

    @Override
	public T getComponentInstance(final PicoContainer container, final java.lang.reflect.Type into) {
        T o = getDelegate().getComponentInstance(container, into);
        Class[] interfaces = o.getClass().getInterfaces();
        if (interfaces.length != 0) {
            byte[] bytes = makeProxy("XX", interfaces, true);
            AsmClassLoader cl = new AsmClassLoader(HotSwappable.Swappable.class.getClassLoader());
            Class pClazz = cl.defineClass("XX", bytes);
            try {
                Constructor ctor = (Constructor) pClazz.getConstructor(HotSwappable.Swappable.class);
                final HotSwappable.Swappable swappable = getSwappable();
                swappable.swap(o);
                return ctor.newInstance(swappable);
            } catch (NoSuchMethodException e) {
            } catch (InstantiationException e) {
            } catch (IllegalAccessException e) {
            } catch (InvocationTargetException e) {
            }
        }
        return o;
    }

    public String getDescriptor() {
        return "Hidden";
    }

    protected HotSwappable.Swappable getSwappable() {
        return new HotSwappable.Swappable();
    }

    public byte[] makeProxy(final String proxyName, final Class[] interfaces, final boolean setter) {

        ClassWriter cw = new ClassWriter(0);
        FieldVisitor fv;

        Class superclass = Object.class;

        cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, proxyName, null, Type.getInternalName(superclass), getNames(interfaces));

        {
            fv = cw.visitField(ACC_PRIVATE + ACC_TRANSIENT, "swappable", encodedClassName(HotSwappable.Swappable.class), null, null);
            fv.visitEnd();
        }
        doConstructor(proxyName, cw);
        Set methodsDone = new HashSet();
        for (Class iface : interfaces) {
            Method[] meths = iface.getMethods();
            for (Method meth : meths) {
                if (!methodsDone.contains(meth.toString())) {
                    doMethod(proxyName, cw, iface, meth);
                    methodsDone.add(meth.toString());
                }
            }
        }

        cw.visitEnd();

        return cw.toByteArray();
    }

    private String[] getNames(final Class[] interfaces) {
        String[] retVal = new String[interfaces.length];
        for (int i = 0; i < interfaces.length; i++) {
            retVal[i] = Type.getInternalName(interfaces[i]);
        }
        return retVal;
    }

    private void doConstructor(final String proxyName, final ClassWriter cw) {
        MethodVisitor mv;
        mv = cw.visitMethod(ACC_PUBLIC, "", "(L"+ Type.getInternalName(HotSwappable.Swappable.class)+";)V", null, null);
        mv.visitCode();
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V");
        mv.visitVarInsn(ALOAD, 0);
        mv.visitVarInsn(ALOAD, 1);
        mv.visitFieldInsn(PUTFIELD, proxyName, "swappable", encodedClassName(HotSwappable.Swappable.class));
        mv.visitInsn(RETURN);
        mv.visitMaxs(2, 2);
        mv.visitEnd();
    }

    private void doMethod(final String proxyName, final ClassWriter cw, final Class iface, final Method meth) {

        String cn = Type.getInternalName(iface);
        String sig = Type.getMethodDescriptor(meth);

        String[] exceptions = encodedExceptionNames(meth.getExceptionTypes());
        MethodVisitor mv;
        mv = cw.visitMethod(ACC_PUBLIC, meth.getName(), sig, null, exceptions);
        mv.visitCode();
        mv.visitVarInsn(ALOAD, 0);
        mv.visitFieldInsn(GETFIELD, proxyName, "swappable", encodedClassName(HotSwappable.Swappable.class));
        mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(HotSwappable.Swappable.class), "getInstance", "()Ljava/lang/Object;");
        mv.visitTypeInsn(CHECKCAST, cn);
        Class[] types = meth.getParameterTypes();
        int ix = 1;
        for (Class type : types) {
            int load = whichLoad(type);
            mv.visitVarInsn(load, ix);
            ix = indexOf(ix, load);
        }
        mv.visitMethodInsn(INVOKEINTERFACE, cn, meth.getName(), sig);
        mv.visitInsn(whichReturn(meth.getReturnType()));
        mv.visitMaxs(ix, ix);
        mv.visitEnd();
    }

    private int indexOf(final int ix, final int loadType) {
        if (loadType == LLOAD) {
            return ix + 2;
        } else if (loadType == DLOAD) {
            return ix + 2;
        } else if (loadType == ILOAD) {
            return ix + 1;
        } else if (loadType == ALOAD) {
            return ix + 1;
        } else if (loadType == FLOAD) {
            return ix + 1;
        }
        return 0;
    }

    private String[] encodedExceptionNames(final Class[] exceptionTypes) {
        if (exceptionTypes.length == 0) {
            return null;
        }
        String[] retVal = new String[exceptionTypes.length];
        for (int i = 0; i < exceptionTypes.length; i++) {
            Class clazz = exceptionTypes[i];
            retVal[i] = Type.getInternalName(clazz);
        }
        return retVal;
    }

    private int whichReturn(final Class clazz) {
        if (!clazz.isPrimitive()) {
            return ARETURN;
        } else if (clazz.isArray()) {
            return ARETURN;
        } else if (clazz == int.class) {
            return IRETURN;
        } else if (clazz == long.class) {
            return LRETURN;
        } else if (clazz == byte.class) {
            return IRETURN;
        } else if (clazz == float.class) {
            return FRETURN;
        } else if (clazz == double.class) {
            return DRETURN;
        } else if (clazz == char.class) {
            return IRETURN;
        } else if (clazz == short.class) {
            return IRETURN;
        } else if (clazz == boolean.class) {
            return IRETURN;
        } else if (clazz == void.class) {
            return RETURN;
        } else {
            return 0;
        }
    }

    private int whichLoad(final Class clazz) {
        if (!clazz.isPrimitive()) {
            return ALOAD;
        } else if (clazz.isArray()) {
            return ALOAD;
        } else if (clazz == int.class) {
            return ILOAD;
        } else if (clazz == long.class) {
            return LLOAD;
        } else if (clazz == byte.class) {
            return ILOAD;
        } else if (clazz == float.class) {
            return FLOAD;
        } else if (clazz == double.class) {
            return DLOAD;
        } else if (clazz == char.class) {
            return ILOAD;
        } else if (clazz == short.class) {
            return ILOAD;
        } else if (clazz == boolean.class) {
            return ILOAD;
        } else {
            return 0;
        }
    }

    private String encodedClassName(final Class clazz) {
        if (clazz.getName().startsWith("[")) {
            return Type.getInternalName(clazz);
        } else if (!clazz.isPrimitive()) {
            return "L" + Type.getInternalName(clazz) + ";";
        } else if (clazz == int.class) {
            return "I";
        } else if (clazz == long.class) {
            return "J";
        } else if (clazz == byte.class) {
            return "B";
        } else if (clazz == float.class) {
            return "F";
        } else if (clazz == double.class) {
            return "D";
        } else if (clazz == char.class) {
            return "C";
        } else if (clazz == short.class) {
            return "S";
        } else if (clazz == boolean.class) {
            return "Z";
        } else if (clazz == void.class) {
            return "V";
        } else {
            return null;
        }
    }

    private static class AsmClassLoader extends ClassLoader {

        public AsmClassLoader(final ClassLoader parent) {
            super(parent);
        }

        public Class defineClass(final String name, final byte[] b) {
            return defineClass(name, b, 0, b.length);
        }
    }

}