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 }