View Javadoc
1 /* 2 * The Apache Software License, Version 1.1 3 * 4 * Copyright (c) 2002 The Apache Software Foundation. All rights 5 * reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in 16 * the documentation and/or other materials provided with the 17 * distribution. 18 * 19 * 3. The end-user documentation included with the redistribution, 20 * if any, must include the following acknowledgment: 21 * "This product includes software developed by the 22 * Apache Software Foundation (http://www.apache.org/)." 23 * Alternately, this acknowledgment may appear in the software itself, 24 * if and wherever such third-party acknowledgments normally appear. 25 * 26 * 4. The names "Apache" and "Apache Software Foundation" must 27 * not be used to endorse or promote products derived from this 28 * software without prior written permission. For written 29 * permission, please contact apache@apache.org. 30 * 31 * 5. Products derived from this software may not be called "Apache", 32 * nor may "Apache" appear in their name, without prior written 33 * permission of the Apache Software Foundation. 34 * 35 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 36 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 37 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 38 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 39 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 42 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 43 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 44 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 45 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 46 * SUCH DAMAGE. 47 * ==================================================================== 48 * 49 * This software consists of voluntary contributions made by many 50 * individuals on behalf of the Apache Software Foundation. For more 51 * information on the Apache Software Foundation, please see 52 * <http://www.apache.org/>;. 53 */ 54 package net.sf.cglib.proxy; 55 56 import java.io.ObjectStreamException; 57 import java.lang.reflect.*; 58 import java.util.*; 59 import net.sf.cglib.core.*; 60 import org.objectweb.asm.ClassVisitor; 61 import org.objectweb.asm.Label; 62 import org.objectweb.asm.Type; 63 64 class EnhancerEmitter extends ClassEmitter { 65 private static final String CONSTRUCTED_FIELD = "CGLIB$CONSTRUCTED"; 66 private static final String THREAD_CALLBACKS_FIELD = "CGLIB$THREAD_CALLBACKS"; 67 68 private static final String SET_THREAD_CALLBACKS_NAME = "CGLIB$SET_THREAD_CALLBACKS"; 69 70 private static final Type ILLEGAL_STATE_EXCEPTION = 71 TypeUtils.parseType("IllegalStateException"); 72 private static final Type ILLEGAL_ARGUMENT_EXCEPTION = 73 TypeUtils.parseType("IllegalArgumentException"); 74 private static final Type THREAD_LOCAL = 75 TypeUtils.parseType("ThreadLocal"); 76 private static final Type FACTORY = 77 TypeUtils.parseType("net.sf.cglib.proxy.Factory"); 78 private static final Type CALLBACK = 79 TypeUtils.parseType("net.sf.cglib.proxy.Callback"); 80 private static final Type CALLBACK_ARRAY = 81 TypeUtils.parseType("net.sf.cglib.proxy.Callback[]"); 82 83 private static final Signature CSTRUCT_NULL = 84 TypeUtils.parseConstructor(""); 85 private static final Signature SET_THREAD_CALLBACKS = 86 TypeUtils.parseSignature("void " + SET_THREAD_CALLBACKS_NAME + "(net.sf.cglib.proxy.Callback[])"); 87 private static final Signature NEW_INSTANCE = 88 TypeUtils.parseSignature("Object newInstance(net.sf.cglib.proxy.Callback[])"); 89 private static final Signature MULTIARG_NEW_INSTANCE = 90 TypeUtils.parseSignature("Object newInstance(Class[], Object[], net.sf.cglib.proxy.Callback[])"); 91 private static final Signature SINGLE_NEW_INSTANCE = 92 TypeUtils.parseSignature("Object newInstance(net.sf.cglib.proxy.Callback)"); 93 private static final Signature SET_CALLBACK = 94 TypeUtils.parseSignature("void setCallback(int, net.sf.cglib.proxy.Callback)"); 95 private static final Signature GET_CALLBACK = 96 TypeUtils.parseSignature("net.sf.cglib.proxy.Callback getCallback(int)"); 97 private static final Signature SET_CALLBACKS = 98 TypeUtils.parseSignature("void setCallbacks(net.sf.cglib.proxy.Callback[])"); 99 private static final Signature GET_CALLBACKS = 100 TypeUtils.parseSignature("net.sf.cglib.proxy.Callback[] getCallbacks()"); 101 102 private static final Signature THREAD_LOCAL_GET = 103 TypeUtils.parseSignature("Object get()"); 104 private static final Signature THREAD_LOCAL_SET = 105 TypeUtils.parseSignature("void set(Object)"); 106 107 private Class[] callbackTypes; 108 109 public EnhancerEmitter(ClassVisitor v, 110 String className, 111 Class superclass, 112 Class[] interfaces, 113 CallbackFilter filter, 114 Class[] callbackTypes, 115 boolean useFactory) throws Exception { 116 super(v); 117 if (superclass == null) { 118 superclass = Object.class; 119 } 120 this.callbackTypes = callbackTypes; 121 122 begin_class(Constants.ACC_PUBLIC, 123 className, 124 Type.getType(superclass), 125 (useFactory ? 126 TypeUtils.add(TypeUtils.getTypes(interfaces), FACTORY) : 127 TypeUtils.getTypes(interfaces)), 128 Constants.SOURCE_FILE); 129 130 if (Modifier.isFinal(superclass.getModifiers())) 131 throw new IllegalArgumentException("Cannot subclass final class " + superclass); 132 133 List clist = new ArrayList(Arrays.asList(superclass.getDeclaredConstructors())); 134 CollectionUtils.filter(clist, new VisibilityPredicate(superclass, true)); 135 if (clist.size() == 0) { 136 throw new IllegalArgumentException("No visible constructors in " + superclass); 137 } 138 Constructor[] constructors = (Constructor[])clist.toArray(new Constructor[clist.size()]); 139 140 // Order is very important: must add superclass, then 141 // its superclass chain, then each interface and 142 // its superinterfaces. 143 List methods = new ArrayList(); 144 ReflectUtils.addAllMethods(superclass, methods); 145 146 List interfaceMethods = new ArrayList(); 147 if (interfaces != null) { 148 for (int i = 0; i < interfaces.length; i++) { 149 if (interfaces[i] != Factory.class) { 150 ReflectUtils.addAllMethods(interfaces[i], interfaceMethods); 151 } 152 } 153 } 154 Set forcePublic = MethodWrapper.createSet(interfaceMethods); 155 methods.addAll(interfaceMethods); 156 CollectionUtils.filter(methods, new RejectModifierPredicate(Modifier.STATIC)); 157 CollectionUtils.filter(methods, new VisibilityPredicate(superclass, true)); 158 CollectionUtils.filter(methods, new DuplicatesPredicate()); 159 CollectionUtils.filter(methods, new RejectModifierPredicate(Modifier.FINAL)); 160 161 Map groups = new HashMap(); 162 Map indexes = new HashMap(); 163 for (Iterator it = methods.iterator(); it.hasNext();) { 164 Method method = (Method)it.next(); 165 int index = filter.accept(method); 166 if (index >= callbackTypes.length) { 167 throw new IllegalArgumentException("Callback filter returned an index that is too large: " + index); 168 } 169 indexes.put(method, new Integer(index)); 170 Object gen = CallbackUtils.getGenerator(callbackTypes[index]); 171 List group = (List)groups.get(gen); 172 if (group == null) { 173 groups.put(gen, group = new ArrayList(methods.size())); 174 } 175 group.add(method); 176 } 177 178 emitMethods(groups, indexes, forcePublic); 179 emitConstructors(constructors); 180 emitSetThreadCallbacks(); 181 182 if (useFactory) { 183 int[] keys = getCallbackKeys(); 184 emitNewInstanceCallbacks(); 185 emitNewInstanceCallback(); 186 emitNewInstanceMultiarg(constructors); 187 emitGetCallback(keys); 188 emitSetCallback(keys); 189 emitGetCallbacks(); 190 emitSetCallbacks(); 191 } 192 end_class(); 193 } 194 195 static void setThreadCallbacks(Class type, Callback[] callbacks) { 196 // TODO: optimize 197 try { 198 Method setter = type.getDeclaredMethod(SET_THREAD_CALLBACKS_NAME, new Class[]{ Callback[].class }); 199 setter.invoke(null, new Object[]{ callbacks }); 200 } catch (NoSuchMethodException e) { 201 throw new IllegalArgumentException(type + " is not an enhanced class"); 202 } catch (IllegalAccessException e) { 203 throw new CodeGenerationException(e); 204 } catch (InvocationTargetException e) { 205 throw new CodeGenerationException(e); 206 } 207 } 208 209 private void emitConstructors(Constructor[] constructors) { 210 for (int i = 0; i < constructors.length; i++) { 211 Signature sig = ReflectUtils.getSignature(constructors[i]); 212 CodeEmitter e = begin_method(Constants.ACC_PUBLIC, 213 sig, 214 ReflectUtils.getExceptionTypes(constructors[i]), 215 null); 216 e.load_this(); 217 e.dup(); 218 e.load_args(); 219 e.super_invoke_constructor(sig); 220 e.push(1); 221 e.putfield(CONSTRUCTED_FIELD); 222 223 e.load_this(); 224 e.getfield(THREAD_CALLBACKS_FIELD); 225 e.invoke_virtual(THREAD_LOCAL, THREAD_LOCAL_GET); 226 e.dup(); 227 Label skip = e.make_label(); 228 e.ifnull(skip); 229 230 e.checkcast(CALLBACK_ARRAY); 231 for (int j = 0; j < callbackTypes.length; j++) { 232 e.dup2(); 233 e.aaload(j); 234 e.checkcast(Type.getType(callbackTypes[j])); 235 e.putfield(getCallbackField(j)); 236 } 237 e.mark(skip); 238 239 // clear thread-locals 240 e.getfield(THREAD_CALLBACKS_FIELD); 241 e.aconst_null(); 242 e.invoke_virtual(THREAD_LOCAL, THREAD_LOCAL_SET); 243 244 e.return_value(); 245 e.end_method(); 246 } 247 } 248 249 private int[] getCallbackKeys() { 250 int[] keys = new int[callbackTypes.length]; 251 for (int i = 0; i < callbackTypes.length; i++) { 252 keys[i] = i; 253 } 254 return keys; 255 } 256 257 private void emitGetCallback(int[] keys) { 258 final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, GET_CALLBACK, null, null); 259 e.load_this(); 260 e.load_arg(0); 261 e.process_switch(keys, new ProcessSwitchCallback() { 262 public void processCase(int key, Label end) { 263 e.getfield(getCallbackField(key)); 264 e.goTo(end); 265 } 266 public void processDefault() { 267 e.pop(); // stack height 268 e.aconst_null(); 269 } 270 }); 271 e.return_value(); 272 e.end_method(); 273 } 274 275 private void emitSetCallback(int[] keys) { 276 final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, SET_CALLBACK, null, null); 277 e.load_this(); 278 e.load_arg(1); 279 e.load_arg(0); 280 e.process_switch(keys, new ProcessSwitchCallback() { 281 public void processCase(int key, Label end) { 282 e.checkcast(Type.getType(callbackTypes[key])); 283 e.putfield(getCallbackField(key)); 284 e.goTo(end); 285 } 286 public void processDefault() { 287 // TODO: error? 288 // stack height 289 e.pop2(); 290 } 291 }); 292 e.return_value(); 293 e.end_method(); 294 } 295 296 private void emitSetCallbacks() { 297 CodeEmitter e = begin_method(Constants.ACC_PUBLIC, SET_CALLBACKS, null, null); 298 e.load_this(); 299 e.load_arg(0); 300 for (int i = 0; i < callbackTypes.length; i++) { 301 e.dup2(); 302 e.aaload(i); 303 e.checkcast(Type.getType(callbackTypes[i])); 304 e.putfield(getCallbackField(i)); 305 } 306 e.return_value(); 307 e.end_method(); 308 } 309 310 private void emitGetCallbacks() { 311 CodeEmitter e = begin_method(Constants.ACC_PUBLIC, GET_CALLBACKS, null, null); 312 e.load_this(); 313 e.push(callbackTypes.length); 314 e.newarray(CALLBACK); 315 for (int i = 0; i < callbackTypes.length; i++) { 316 e.dup(); 317 e.push(i); 318 e.load_this(); 319 e.getfield(getCallbackField(i)); 320 e.aastore(); 321 } 322 e.return_value(); 323 e.end_method(); 324 } 325 326 private void emitNewInstanceCallbacks() { 327 CodeEmitter e = begin_method(Constants.ACC_PUBLIC, NEW_INSTANCE, null, null); 328 e.load_arg(0); 329 e.invoke_static_this(SET_THREAD_CALLBACKS); 330 emitCommonNewInstance(e); 331 } 332 333 private void emitCommonNewInstance(CodeEmitter e) { 334 e.new_instance_this(); 335 e.dup(); 336 e.invoke_constructor_this(); 337 e.return_value(); 338 e.end_method(); 339 } 340 341 private void emitNewInstanceCallback() { 342 CodeEmitter e = begin_method(Constants.ACC_PUBLIC, SINGLE_NEW_INSTANCE, null, null); 343 switch (callbackTypes.length) { 344 case 0: 345 // TODO: make sure Callback is null 346 break; 347 case 1: 348 // for now just make a new array; TODO: optimize 349 e.push(1); 350 e.newarray(CALLBACK); 351 e.dup(); 352 e.push(0); 353 e.load_arg(0); 354 e.aastore(); 355 e.invoke_static_this(SET_THREAD_CALLBACKS); 356 break; 357 default: 358 e.throw_exception(ILLEGAL_STATE_EXCEPTION, "More than one callback object required"); 359 } 360 emitCommonNewInstance(e); 361 } 362 363 private void emitNewInstanceMultiarg(Constructor[] constructors) { 364 CodeEmitter e = begin_method(Constants.ACC_PUBLIC, MULTIARG_NEW_INSTANCE, null, null); 365 e.load_arg(2); 366 e.invoke_static_this(SET_THREAD_CALLBACKS); 367 emitCommonMultiarg(constructors, e); 368 } 369 370 private void emitCommonMultiarg(Constructor[] constructors, final CodeEmitter e) { 371 e.new_instance_this(); 372 e.dup(); 373 e.load_arg(0); 374 EmitUtils.constructor_switch(e, constructors, new ObjectSwitchCallback() { 375 public void processCase(Object key, Label end) { 376 Constructor constructor = (Constructor)key; 377 Type types[] = TypeUtils.getTypes(constructor.getParameterTypes()); 378 for (int i = 0; i < types.length; i++) { 379 e.load_arg(1); 380 e.push(i); 381 e.aaload(); 382 e.unbox(types[i]); 383 } 384 e.invoke_constructor_this(ReflectUtils.getSignature(constructor)); 385 e.goTo(end); 386 } 387 public void processDefault() { 388 e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION, "Constructor not found"); 389 } 390 }); 391 e.return_value(); 392 e.end_method(); 393 } 394 395 private void emitMethods(Map groups, final Map indexes, final Set forcePublic) throws Exception { 396 declare_field(Constants.ACC_PRIVATE, CONSTRUCTED_FIELD, Type.BOOLEAN_TYPE, null, null); 397 declare_field(Constants.PRIVATE_FINAL_STATIC, THREAD_CALLBACKS_FIELD, THREAD_LOCAL, null, null); 398 399 for (int i = 0; i < callbackTypes.length; i++) { 400 declare_field(Constants.ACC_PRIVATE, getCallbackField(i), Type.getType(callbackTypes[i]), null, null); 401 } 402 403 Set seenGen = new HashSet(); 404 CodeEmitter e = begin_static(); 405 e.new_instance(THREAD_LOCAL); 406 e.dup(); 407 e.invoke_constructor(THREAD_LOCAL, CSTRUCT_NULL); 408 e.putfield(THREAD_CALLBACKS_FIELD); 409 410 for (int i = 0; i < callbackTypes.length; i++) { 411 CallbackGenerator gen = CallbackUtils.getGenerator(callbackTypes[i]); 412 if (!seenGen.contains(gen)) { 413 seenGen.add(gen); 414 final List fmethods = (List)groups.get(gen); 415 if (fmethods != null) { 416 CallbackGenerator.Context context = new CallbackGenerator.Context() { 417 public Iterator getMethods() { 418 return fmethods.iterator(); 419 } 420 public int getIndex(Method method) { 421 return ((Integer)indexes.get(method)).intValue(); 422 } 423 public void emitCallback(CodeEmitter e, int index) { 424 emitCurrentCallback(e, index); 425 } 426 public int getModifiers(Method method) { 427 int modifiers = Constants.ACC_FINAL 428 | (method.getModifiers() 429 & ~Constants.ACC_ABSTRACT 430 & ~Constants.ACC_NATIVE 431 & ~Constants.ACC_SYNCHRONIZED); 432 if (forcePublic.contains(MethodWrapper.create(method))) { 433 modifiers = (modifiers & ~Constants.ACC_PROTECTED) | Constants.ACC_PUBLIC; 434 } 435 return modifiers; 436 } 437 // TODO: this is probably slow 438 public String getUniqueName(Method method) { 439 return method.getName() + "_" + fmethods.indexOf(method); 440 } 441 }; 442 gen.generate(this, context); 443 gen.generateStatic(e, context); 444 } 445 } 446 } 447 e.return_value(); 448 e.end_method(); 449 } 450 451 private void emitSetThreadCallbacks() { 452 CodeEmitter e = begin_method(Constants.ACC_PUBLIC | Constants.ACC_STATIC, 453 SET_THREAD_CALLBACKS, 454 null, 455 null); 456 e.getfield(THREAD_CALLBACKS_FIELD); 457 e.load_arg(0); 458 e.invoke_virtual(THREAD_LOCAL, THREAD_LOCAL_SET); 459 e.return_value(); 460 e.end_method(); 461 } 462 463 private void emitCurrentCallback(CodeEmitter e, int index) { 464 e.load_this(); 465 e.getfield(getCallbackField(index)); 466 e.dup(); 467 Label end = e.make_label(); 468 e.ifnonnull(end); 469 e.load_this(); 470 e.getfield(CONSTRUCTED_FIELD); 471 e.if_jump(e.NE, end); 472 e.pop(); 473 e.getfield(THREAD_CALLBACKS_FIELD); 474 e.invoke_virtual(THREAD_LOCAL, THREAD_LOCAL_GET); 475 e.checkcast(CALLBACK_ARRAY); 476 e.push(index); 477 e.aaload(); 478 e.checkcast(Type.getType(callbackTypes[index])); 479 e.mark(end); 480 } 481 482 private static String getCallbackField(int index) { 483 return "CGLIB$CALLBACK_" + index; 484 } 485 }

This page was automatically generated by Maven