1 /* 2 * Javassist, a Java-bytecode translator toolkit. 3 * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. 4 * 5 * The contents of this file are subject to the Mozilla Public License Version 6 * 1.1 (the "License"); you may not use this file except in compliance with 7 * the License. Alternatively, the contents of this file may be used under 8 * the terms of the GNU Lesser General Public License Version 2.1 or later. 9 * 10 * Software distributed under the License is distributed on an "AS IS" basis, 11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 12 * for the specific language governing rights and limitations under the 13 * License. 14 */ 15 16 package javassist.util.proxy; 17 18 import java.lang.reflect.Field; 19 import java.lang.reflect.InvocationTargetException; 20 import java.lang.reflect.Method; 21 import java.lang.reflect.Constructor; 22 import java.lang.reflect.Member; 23 import java.lang.reflect.Modifier; 24 import java.security.ProtectionDomain; 25 import java.util.*; 26 import java.lang.ref.WeakReference; 27 28 import javassist.CannotCompileException; 29 import javassist.bytecode.*; 30 31 /* 32 * This class is implemented only with the lower-level API of Javassist. 33 * This design decision is for maximizing performance. 34 */ 35 36 /** 37 * Factory of dynamic proxy classes. 38 * 39 * <p>This factory generates a class that extends the given super class and implements 40 * the given interfaces. The calls of the methods inherited from the super class are 41 * forwarded and then <code>invoke()</code> is called on the method handler 42 * associated with instances of the generated class. The calls of the methods from 43 * the interfaces are also forwarded to the method handler. 44 * 45 * <p>For example, if the following code is executed, 46 * 47 * <ul><pre> 48 * ProxyFactory f = new ProxyFactory(); 49 * f.setSuperclass(Foo.class); 50 * f.setFilter(new MethodFilter() { 51 * public boolean isHandled(Method m) { 52 * // ignore finalize() 53 * return !m.getName().equals("finalize"); 54 * } 55 * }); 56 * Class c = f.createClass(); 57 * MethodHandler mi = new MethodHandler() { 58 * public Object invoke(Object self, Method m, Method proceed, 59 * Object[] args) throws Throwable { 60 * System.out.println("Name: " + m.getName()); 61 * return proceed.invoke(self, args); // execute the original method. 62 * } 63 * }; 64 * Foo foo = (Foo)c.newInstance(); 65 * ((ProxyObject)foo).setHandler(mi); 66 * </pre></ul> 67 * 68 * <p>Then, the following method call will be forwarded to MethodHandler 69 * <code>mi</code> and prints a message before executing the originally called method 70 * <code>bar()</code> in <code>Foo</code>. 71 * 72 * <ul><pre> 73 * foo.bar(); 74 * </pre></ul> 75 * 76 * <p>The last three lines of the code shown above can be replaced with a call to 77 * the helper method <code>create</code>, which generates a proxy class, instantiates 78 * it, and sets the method handler of the instance: 79 * 80 * <ul><pre> 81 * : 82 * Foo foo = (Foo)f.create(new Class[0], new Object[0], mi); 83 * </pre></ul> 84 * 85 * <p>To change the method handler during runtime, 86 * execute the following code: 87 * 88 * <ul><pre> 89 * MethodHandler mi = ... ; // alternative handler 90 * ((ProxyObject)foo).setHandler(mi); 91 * </pre></ul> 92 * 93 * <p> If setHandler is never called for a proxy instance then it will 94 * employ the default handler which proceeds by invoking the original method. 95 * The behaviour of the default handler is identical to the following 96 * handler: 97 * 98 * <ul><pre> 99 * class EmptyHandler implements MethodHandler { 100 * public Object invoke(Object self, Method m, 101 * Method proceed, Object[] args) throws Exception { 102 * return proceed.invoke(self, args); 103 * } 104 * } 105 * </pre></ul> 106 * 107 * <p>A proxy factory caches and reuses proxy classes by default. It is possible to reset 108 * this default globally by setting static field {@link ProxyFactory#useCache} to false. 109 * Caching may also be configured for a specific factory by calling instance method 110 * {@link ProxyFactory#setUseCache(boolean)}. It is strongly recommended that new clients 111 * of class ProxyFactory enable caching. Failure to do so may lead to exhaustion of 112 * the heap memory area used to store classes. 113 * 114 * <p>Caching is automatically disabled for any given proxy factory if deprecated instance 115 * method {@link ProxyFactory#setHandler(MethodHandler)} is called. This method was 116 * used to specify a default handler which newly created proxy classes should install 117 * when they create their instances. It is only retained to provide backward compatibility 118 * with previous releases of javassist. Unfortunately,this legacy behaviour makes caching 119 * and reuse of proxy classes impossible. The current programming model expects javassist 120 * clients to set the handler of a proxy instance explicitly by calling method 121 * {@link ProxyObject#setHandler(MethodHandler)} as shown in the sample code above. New 122 * clients are strongly recommended to use this model rather than calling 123 * {@link ProxyFactory#setHandler(MethodHandler)}. 124 * 125 * <p>A proxy object generated by <code>ProxyFactory</code> is serializable 126 * if its super class or any of its interfaces implement <code>java.io.Serializable</code>. 127 * However, a serialized proxy object may not be compatible with future releases. 128 * The serialization support should be used for short-term storage or RMI. 129 * 130 * <p>For compatibility with older releases serialization of proxy objects is implemented by 131 * adding a writeReplace method to the proxy class. This allows a proxy to be serialized 132 * to a conventional {@link java.io.ObjectOutputStream} and deserialized from a corresponding 133 * {@link java.io.ObjectInputStream}. However this method suffers from several problems, the most 134 * notable one being that it fails to serialize state inherited from the proxy's superclass. 135 * <p> 136 * An alternative method of serializing proxy objects is available which fixes these problems. It 137 * requires inhibiting generation of the writeReplace method and instead using instances of 138 * {@link javassist.util.proxy.ProxyObjectOutputStream} and {@link javassist.util.proxy.ProxyObjectInputStream} 139 * (which are subclasses of {@link java.io.ObjectOutputStream} and {@link java.io.ObjectInputStream}) 140 * to serialize and deserialize, respectively, the proxy. These streams recognise javassist proxies and ensure 141 * that they are serialized and deserialized without the need for the proxy class to implement special methods 142 * such as writeReplace. Generation of the writeReplace method can be disabled globally by setting static field 143 * {@link ProxyFactory#useWriteReplace} to false. Alternatively, it may be 144 * configured per factory by calling instance method {@link ProxyFactory#setUseWriteReplace(boolean)}. 145 * 146 * @see MethodHandler 147 * @since 3.1 148 * @author Muga Nishizawa 149 * @author Shigeru Chiba 150 * @author Andrew Dinn 151 */ 152 public class ProxyFactory { 153 private Class superClass; 154 private Class[] interfaces; 155 private MethodFilter methodFilter; 156 private MethodHandler handler; // retained for legacy usage 157 private List signatureMethods; 158 private byte[] signature; 159 private String classname; 160 private String basename; 161 private String superName; 162 private Class thisClass; 163 /** 164 * per factory setting initialised from current setting for useCache but able to be reset before each create call 165 */ 166 private boolean factoryUseCache; 167 /** 168 * per factory setting initialised from current setting for useWriteReplace but able to be reset before each create call 169 */ 170 private boolean factoryWriteReplace; 171 172 173 /** 174 * If the value of this variable is not null, the class file of 175 * the generated proxy class is written under the directory specified 176 * by this variable. For example, if the value is 177 * <code>"."</code>, then the class file is written under the current 178 * directory. This method is for debugging. 179 * 180 * <p>The default value is null. 181 */ 182 public String writeDirectory; 183 184 private static final Class OBJECT_TYPE = Object.class; 185 186 private static final String HOLDER = "_methods_"; 187 private static final String HOLDER_TYPE = "[Ljava/lang/reflect/Method;"; 188 private static final String FILTER_SIGNATURE_FIELD = "_filter_signature"; 189 private static final String FILTER_SIGNATURE_TYPE = "[B"; 190 private static final String HANDLER = "handler"; 191 private static final String NULL_INTERCEPTOR_HOLDER = "javassist.util.proxy.RuntimeSupport"; 192 private static final String DEFAULT_INTERCEPTOR = "default_interceptor"; 193 private static final String HANDLER_TYPE 194 = 'L' + MethodHandler.class.getName().replace('.', '/') + ';'; 195 private static final String HANDLER_SETTER = "setHandler"; 196 private static final String HANDLER_SETTER_TYPE = "(" + HANDLER_TYPE + ")V"; 197 198 private static final String HANDLER_GETTER = "getHandler"; 199 private static final String HANDLER_GETTER_TYPE = "()" + HANDLER_TYPE; 200 201 private static final String SERIAL_VERSION_UID_FIELD = "serialVersionUID"; 202 private static final String SERIAL_VERSION_UID_TYPE = "J"; 203 private static final int SERIAL_VERSION_UID_VALUE = -1; 204 205 /** 206 * If true, a generated proxy class is cached and it will be reused 207 * when generating the proxy class with the same properties is requested. 208 * The default value is true. 209 * 210 * Note that this value merely specifies the initial setting employed by any newly created 211 * proxy factory. The factory setting may be overwritten by calling factory instance method 212 * {@link #setUseCache(boolean)} 213 * 214 * @since 3.4 215 */ 216 public static volatile boolean useCache = true; 217 218 /** 219 * If true, a generated proxy class will implement method writeReplace enabling 220 * serialization of its proxies to a conventional ObjectOutputStream. this (default) 221 * setting retains the old javassist behaviour which has the advantage that it 222 * retains compatibility with older releases and requires no extra work on the part 223 * of the client performing the serialization. However, it has the disadvantage that 224 * state inherited from the superclasses of the proxy is lost during serialization. 225 * if false then serialization/deserialization of the proxy instances will preserve 226 * all fields. However, serialization must be performed via a {@link ProxyObjectOutputStream} 227 * and deserialization must be via {@link ProxyObjectInputStream}. Any attempt to serialize 228 * proxies whose class was created with useWriteReplace set to false via a normal 229 * {@link java.io.ObjectOutputStream} will fail. 230 * 231 * Note that this value merely specifies the initial setting employed by any newly created 232 * proxy factory. The factory setting may be overwritten by calling factory instance method 233 * {@link #setUseWriteReplace(boolean)} 234 * 235 * @since 3.4 236 */ 237 public static volatile boolean useWriteReplace = true; 238 239 /* 240 * methods allowing individual factory settings for factoryUseCache and factoryWriteReplace to be reset 241 */ 242 243 /** 244 * test whether this factory uses the proxy cache 245 * @return true if this factory uses the proxy cache otherwise false 246 */ isUseCache()247 public boolean isUseCache() 248 { 249 return factoryUseCache; 250 } 251 252 /** 253 * configure whether this factory should use the proxy cache 254 * @param useCache true if this factory should use the proxy cache and false if it should not use the cache 255 * @throws RuntimeException if a default interceptor has been set for the factory 256 */ setUseCache(boolean useCache)257 public void setUseCache(boolean useCache) 258 { 259 // we cannot allow caching to be used if the factory is configured to install a default interceptor 260 // field into generated classes 261 if (handler != null && useCache) { 262 throw new RuntimeException("caching cannot be enabled if the factory default interceptor has been set"); 263 } 264 factoryUseCache = useCache; 265 } 266 267 /** 268 * test whether this factory installs a writeReplace method in created classes 269 * @return true if this factory installs a writeReplace method in created classes otherwise false 270 */ isUseWriteReplace()271 public boolean isUseWriteReplace() 272 { 273 return factoryWriteReplace; 274 } 275 276 /** 277 * configure whether this factory should add a writeReplace method to created classes 278 * @param useWriteReplace true if this factory should add a writeReplace method to created classes and false if it 279 * should not add a writeReplace method 280 */ setUseWriteReplace(boolean useWriteReplace)281 public void setUseWriteReplace(boolean useWriteReplace) 282 { 283 factoryWriteReplace = useWriteReplace; 284 } 285 286 private static WeakHashMap proxyCache = new WeakHashMap(); 287 288 /** 289 * determine if a class is a javassist proxy class 290 * @param cl 291 * @return true if the class is a javassist proxy class otherwise false 292 */ isProxyClass(Class cl)293 public static boolean isProxyClass(Class cl) 294 { 295 // all proxies implement ProxyObject. nothing else should. 296 return (ProxyObject.class.isAssignableFrom(cl)); 297 } 298 299 /** 300 * used to store details of a specific proxy class in the second tier of the proxy cache. this entry 301 * will be located in a hashmap keyed by the unique identifying name of the proxy class. the hashmap is 302 * located in a weak hashmap keyed by the classloader common to all proxy classes in the second tier map. 303 */ 304 static class ProxyDetails { 305 /** 306 * the unique signature of any method filter whose behaviour will be met by this class. each bit in 307 * the byte array is set if the filter redirects the corresponding super or interface method and clear 308 * if it does not redirect it. 309 */ 310 byte[] signature; 311 /** 312 * a hexadecimal string representation of the signature bit sequence. this string also forms part 313 * of the proxy class name. 314 */ 315 WeakReference proxyClass; 316 /** 317 * a flag which is true this class employs writeReplace to perform serialization of its instances 318 * and false if serialization must employ of a ProxyObjectOutputStream and ProxyObjectInputStream 319 */ 320 boolean isUseWriteReplace; 321 ProxyDetails(byte[] signature, Class proxyClass, boolean isUseWriteReplace)322 ProxyDetails(byte[] signature, Class proxyClass, boolean isUseWriteReplace) 323 { 324 this.signature = signature; 325 this.proxyClass = new WeakReference(proxyClass); 326 this.isUseWriteReplace = isUseWriteReplace; 327 } 328 } 329 330 /** 331 * Constructs a factory of proxy class. 332 */ ProxyFactory()333 public ProxyFactory() { 334 superClass = null; 335 interfaces = null; 336 methodFilter = null; 337 handler = null; 338 signature = null; 339 signatureMethods = null; 340 thisClass = null; 341 writeDirectory = null; 342 factoryUseCache = useCache; 343 factoryWriteReplace = useWriteReplace; 344 } 345 346 /** 347 * Sets the super class of a proxy class. 348 */ setSuperclass(Class clazz)349 public void setSuperclass(Class clazz) { 350 superClass = clazz; 351 // force recompute of signature 352 signature = null; 353 } 354 355 /** 356 * Obtains the super class set by <code>setSuperclass()</code>. 357 * 358 * @since 3.4 359 */ getSuperclass()360 public Class getSuperclass() { return superClass; } 361 362 /** 363 * Sets the interfaces of a proxy class. 364 */ setInterfaces(Class[] ifs)365 public void setInterfaces(Class[] ifs) { 366 interfaces = ifs; 367 // force recompute of signature 368 signature = null; 369 } 370 371 /** 372 * Obtains the interfaces set by <code>setInterfaces</code>. 373 * 374 * @since 3.4 375 */ getInterfaces()376 public Class[] getInterfaces() { return interfaces; } 377 378 /** 379 * Sets a filter that selects the methods that will be controlled by a handler. 380 */ setFilter(MethodFilter mf)381 public void setFilter(MethodFilter mf) { 382 methodFilter = mf; 383 // force recompute of signature 384 signature = null; 385 } 386 387 /** 388 * Generates a proxy class using the current filter. 389 */ createClass()390 public Class createClass() { 391 if (signature == null) { 392 computeSignature(methodFilter); 393 } 394 return createClass1(); 395 } 396 397 /** 398 * Generates a proxy class using the supplied filter. 399 */ createClass(MethodFilter filter)400 public Class createClass(MethodFilter filter) { 401 computeSignature(filter); 402 return createClass1(); 403 } 404 405 /** 406 * Generates a proxy class with a specific signature. 407 * access is package local so ProxyObjectInputStream can use this 408 * @param signature 409 * @return 410 */ createClass(byte[] signature)411 Class createClass(byte[] signature) 412 { 413 installSignature(signature); 414 return createClass1(); 415 } 416 createClass1()417 private Class createClass1() { 418 if (thisClass == null) { 419 ClassLoader cl = getClassLoader(); 420 synchronized (proxyCache) { 421 if (factoryUseCache) 422 createClass2(cl); 423 else 424 createClass3(cl); 425 } 426 } 427 428 // don't retain any unwanted references 429 Class result = thisClass; 430 thisClass = null; 431 432 return result; 433 } 434 435 private static char[] hexDigits = 436 { '0', '1', '2', '3', '4', '5', '6', '7', 437 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; 438 getKey(Class superClass, Class[] interfaces, byte[] signature, boolean useWriteReplace)439 public String getKey(Class superClass, Class[] interfaces, byte[] signature, boolean useWriteReplace) 440 { 441 StringBuffer sbuf = new StringBuffer(); 442 if (superClass != null){ 443 sbuf.append(superClass.getName()); 444 } 445 sbuf.append(":"); 446 for (int i = 0; i < interfaces.length; i++) { 447 sbuf.append(interfaces[i].getName()); 448 sbuf.append(":"); 449 } 450 for (int i = 0; i < signature.length; i++) { 451 byte b = signature[i]; 452 int lo = b & 0xf; 453 int hi = (b >> 4) & 0xf; 454 sbuf.append(hexDigits[lo]); 455 sbuf.append(hexDigits[hi]); 456 } 457 if (useWriteReplace) { 458 sbuf.append(":w"); 459 } 460 461 return sbuf.toString(); 462 } 463 createClass2(ClassLoader cl)464 private void createClass2(ClassLoader cl) { 465 String key = getKey(superClass, interfaces, signature, factoryWriteReplace); 466 /* 467 * Excessive concurrency causes a large memory footprint and slows the 468 * execution speed down (with JDK 1.5). Thus, we use a jumbo lock for 469 * reducing concrrency. 470 */ 471 // synchronized (proxyCache) { 472 HashMap cacheForTheLoader = (HashMap)proxyCache.get(cl); 473 ProxyDetails details; 474 if (cacheForTheLoader == null) { 475 cacheForTheLoader = new HashMap(); 476 proxyCache.put(cl, cacheForTheLoader); 477 } 478 details = (ProxyDetails)cacheForTheLoader.get(key); 479 if (details != null) { 480 WeakReference reference = details.proxyClass; 481 thisClass = (Class)reference.get(); 482 if (thisClass != null) { 483 return; 484 } 485 } 486 createClass3(cl); 487 details = new ProxyDetails(signature, thisClass, factoryWriteReplace); 488 cacheForTheLoader.put(key, details); 489 // } 490 } 491 createClass3(ClassLoader cl)492 private void createClass3(ClassLoader cl) { 493 // we need a new class so we need a new class name 494 allocateClassName(); 495 496 try { 497 ClassFile cf = make(); 498 if (writeDirectory != null) 499 FactoryHelper.writeFile(cf, writeDirectory); 500 501 thisClass = FactoryHelper.toClass(cf, cl, getDomain()); 502 setField(FILTER_SIGNATURE_FIELD, signature); 503 // legacy behaviour : we only set the default interceptor static field if we are not using the cache 504 if (!factoryUseCache) { 505 setField(DEFAULT_INTERCEPTOR, handler); 506 } 507 } 508 catch (CannotCompileException e) { 509 throw new RuntimeException(e.getMessage(), e); 510 } 511 512 } 513 setField(String fieldName, Object value)514 private void setField(String fieldName, Object value) { 515 if (thisClass != null && value != null) 516 try { 517 Field f = thisClass.getField(fieldName); 518 SecurityActions.setAccessible(f, true); 519 f.set(null, value); 520 SecurityActions.setAccessible(f, false); 521 } 522 catch (Exception e) { 523 throw new RuntimeException(e); 524 } 525 } 526 getFilterSignature(Class clazz)527 static byte[] getFilterSignature(Class clazz) { 528 return (byte[])getField(clazz, FILTER_SIGNATURE_FIELD); 529 } 530 getField(Class clazz, String fieldName)531 private static Object getField(Class clazz, String fieldName) { 532 try { 533 Field f = clazz.getField(fieldName); 534 f.setAccessible(true); 535 Object value = f.get(null); 536 f.setAccessible(false); 537 return value; 538 } 539 catch (Exception e) { 540 throw new RuntimeException(e); 541 } 542 } 543 544 /** 545 * A provider of class loaders. 546 * 547 * @see #classLoaderProvider 548 * @since 3.4 549 */ 550 public static interface ClassLoaderProvider { 551 /** 552 * Returns a class loader. 553 * 554 * @param pf a proxy factory that is going to obtain a class loader. 555 */ get(ProxyFactory pf)556 public ClassLoader get(ProxyFactory pf); 557 } 558 559 /** 560 * A provider used by <code>createClass()</code> for obtaining 561 * a class loader. 562 * <code>get()</code> on this <code>ClassLoaderProvider</code> object 563 * is called to obtain a class loader. 564 * 565 * <p>The value of this field can be updated for changing the default 566 * implementation. 567 * 568 * <p>Example: 569 * <ul><pre> 570 * ProxyFactory.classLoaderProvider = new ProxyFactory.ClassLoaderProvider() { 571 * public ClassLoader get(ProxyFactory pf) { 572 * return Thread.currentThread().getContextClassLoader(); 573 * } 574 * }; 575 * </pre></ul> 576 * 577 * @since 3.4 578 */ 579 public static ClassLoaderProvider classLoaderProvider 580 = new ClassLoaderProvider() { 581 public ClassLoader get(ProxyFactory pf) { 582 return pf.getClassLoader0(); 583 } 584 }; 585 getClassLoader()586 protected ClassLoader getClassLoader() { 587 return classLoaderProvider.get(this); 588 } 589 getClassLoader0()590 protected ClassLoader getClassLoader0() { 591 ClassLoader loader = null; 592 if (superClass != null && !superClass.getName().equals("java.lang.Object")) 593 loader = superClass.getClassLoader(); 594 else if (interfaces != null && interfaces.length > 0) 595 loader = interfaces[0].getClassLoader(); 596 597 if (loader == null) { 598 loader = getClass().getClassLoader(); 599 // In case javassist is in the endorsed dir 600 if (loader == null) { 601 loader = Thread.currentThread().getContextClassLoader(); 602 if (loader == null) 603 loader = ClassLoader.getSystemClassLoader(); 604 } 605 } 606 607 return loader; 608 } 609 getDomain()610 protected ProtectionDomain getDomain() { 611 Class clazz; 612 if (superClass != null && !superClass.getName().equals("java.lang.Object")) 613 clazz = superClass; 614 else if (interfaces != null && interfaces.length > 0) 615 clazz = interfaces[0]; 616 else 617 clazz = this.getClass(); 618 619 return clazz.getProtectionDomain(); 620 } 621 622 /** 623 * Creates a proxy class and returns an instance of that class. 624 * 625 * @param paramTypes parameter types for a constructor. 626 * @param args arguments passed to a constructor. 627 * @param mh the method handler for the proxy class. 628 * @since 3.4 629 */ create(Class[] paramTypes, Object[] args, MethodHandler mh)630 public Object create(Class[] paramTypes, Object[] args, MethodHandler mh) 631 throws NoSuchMethodException, IllegalArgumentException, 632 InstantiationException, IllegalAccessException, InvocationTargetException 633 { 634 Object obj = create(paramTypes, args); 635 ((ProxyObject)obj).setHandler(mh); 636 return obj; 637 } 638 639 /** 640 * Creates a proxy class and returns an instance of that class. 641 * 642 * @param paramTypes parameter types for a constructor. 643 * @param args arguments passed to a constructor. 644 */ create(Class[] paramTypes, Object[] args)645 public Object create(Class[] paramTypes, Object[] args) 646 throws NoSuchMethodException, IllegalArgumentException, 647 InstantiationException, IllegalAccessException, InvocationTargetException 648 { 649 Class c = createClass(); 650 Constructor cons = c.getConstructor(paramTypes); 651 return cons.newInstance(args); 652 } 653 654 /** 655 * Sets the default invocation handler. This invocation handler is shared 656 * among all the instances of a proxy class unless another is explicitly 657 * specified. 658 * @deprecated since 3.12 659 * use of this method is incompatible with proxy class caching. 660 * instead clients should call method {@link ProxyObject#setHandler(MethodHandler)} to set the handler 661 * for each newly created proxy instance. 662 * calling this method will automatically disable caching of classes created by the proxy factory. 663 */ setHandler(MethodHandler mi)664 public void setHandler(MethodHandler mi) { 665 // if we were using the cache and the handler is non-null then we must stop caching 666 if (factoryUseCache && mi != null) { 667 factoryUseCache = false; 668 // clear any currently held class so we don't try to reuse it or set its handler field 669 thisClass = null; 670 } 671 handler = mi; 672 // this retains the behaviour of the old code which resets any class we were holding on to 673 // this is probably not what is wanted 674 setField(DEFAULT_INTERCEPTOR, handler); 675 } 676 677 private static int counter = 0; 678 makeProxyName(String classname)679 private static synchronized String makeProxyName(String classname) { 680 return classname + "_$$_javassist_" + counter++; 681 } 682 make()683 private ClassFile make() throws CannotCompileException { 684 ClassFile cf = new ClassFile(false, classname, superName); 685 cf.setAccessFlags(AccessFlag.PUBLIC); 686 setInterfaces(cf, interfaces); 687 ConstPool pool = cf.getConstPool(); 688 689 // legacy: we only add the static field for the default interceptor if caching is disabled 690 if (!factoryUseCache) { 691 FieldInfo finfo = new FieldInfo(pool, DEFAULT_INTERCEPTOR, HANDLER_TYPE); 692 finfo.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC); 693 cf.addField(finfo); 694 } 695 696 // handler is per instance 697 FieldInfo finfo2 = new FieldInfo(pool, HANDLER, HANDLER_TYPE); 698 finfo2.setAccessFlags(AccessFlag.PRIVATE); 699 cf.addField(finfo2); 700 701 // filter signature is per class 702 FieldInfo finfo3 = new FieldInfo(pool, FILTER_SIGNATURE_FIELD, FILTER_SIGNATURE_TYPE); 703 finfo3.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC); 704 cf.addField(finfo3); 705 706 // the proxy class serial uid must always be a fixed value 707 FieldInfo finfo4 = new FieldInfo(pool, SERIAL_VERSION_UID_FIELD, SERIAL_VERSION_UID_TYPE); 708 finfo4.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC| AccessFlag.FINAL); 709 cf.addField(finfo4); 710 711 // HashMap allMethods = getMethods(superClass, interfaces); 712 // int size = allMethods.size(); 713 makeConstructors(classname, cf, pool, classname); 714 int s = overrideMethods(cf, pool, classname); 715 addMethodsHolder(cf, pool, classname, s); 716 addSetter(classname, cf, pool); 717 addGetter(classname, cf, pool); 718 719 if (factoryWriteReplace) { 720 try { 721 cf.addMethod(makeWriteReplace(pool)); 722 } 723 catch (DuplicateMemberException e) { 724 // writeReplace() is already declared in the super class/interfaces. 725 } 726 } 727 728 thisClass = null; 729 return cf; 730 } 731 checkClassAndSuperName()732 private void checkClassAndSuperName() 733 { 734 if (interfaces == null) 735 interfaces = new Class[0]; 736 737 if (superClass == null) { 738 superClass = OBJECT_TYPE; 739 superName = superClass.getName(); 740 basename = interfaces.length == 0 ? superName 741 : interfaces[0].getName(); 742 } else { 743 superName = superClass.getName(); 744 basename = superName; 745 } 746 747 if (Modifier.isFinal(superClass.getModifiers())) 748 throw new RuntimeException(superName + " is final"); 749 750 if (basename.startsWith("java.")) 751 basename = "org.javassist.tmp." + basename; 752 } 753 allocateClassName()754 private void allocateClassName() 755 { 756 classname = makeProxyName(basename); 757 } 758 759 private static Comparator sorter = new Comparator() { 760 761 public int compare(Object o1, Object o2) { 762 Map.Entry e1 = (Map.Entry)o1; 763 Map.Entry e2 = (Map.Entry)o2; 764 String key1 = (String)e1.getKey(); 765 String key2 = (String)e2.getKey(); 766 return key1.compareTo(key2); 767 } 768 }; 769 makeSortedMethodList()770 private void makeSortedMethodList() 771 { 772 checkClassAndSuperName(); 773 774 HashMap allMethods = getMethods(superClass, interfaces); 775 signatureMethods = new ArrayList(allMethods.entrySet()); 776 Collections.sort(signatureMethods, sorter); 777 } 778 computeSignature(MethodFilter filter)779 private void computeSignature(MethodFilter filter) // throws CannotCompileException 780 { 781 makeSortedMethodList(); 782 783 int l = signatureMethods.size(); 784 int maxBytes = ((l + 7) >> 3); 785 signature = new byte[maxBytes]; 786 for (int idx = 0; idx < l; idx++) 787 { 788 Map.Entry e = (Map.Entry)signatureMethods.get(idx); 789 Method m = (Method)e.getValue(); 790 int mod = m.getModifiers(); 791 if (!Modifier.isFinal(mod) && !Modifier.isStatic(mod) 792 && isVisible(mod, basename, m) && (filter == null || filter.isHandled(m))) { 793 setBit(signature, idx); 794 } 795 } 796 } 797 installSignature(byte[] signature)798 private void installSignature(byte[] signature) // throws CannotCompileException 799 { 800 makeSortedMethodList(); 801 802 int l = signatureMethods.size(); 803 int maxBytes = ((l + 7) >> 3); 804 if (signature.length != maxBytes) { 805 throw new RuntimeException("invalid filter signature length for deserialized proxy class"); 806 } 807 808 this.signature = signature; 809 } 810 testBit(byte[] signature, int idx)811 private boolean testBit(byte[] signature, int idx) 812 { 813 int byteIdx = idx >> 3; 814 if (byteIdx > signature.length) { 815 return false; 816 } else { 817 int bitIdx = idx & 0x7; 818 int mask = 0x1 << bitIdx; 819 int sigByte = signature[byteIdx]; 820 return ((sigByte & mask) != 0); 821 } 822 } 823 setBit(byte[] signature, int idx)824 private void setBit(byte[] signature, int idx) 825 { 826 int byteIdx = idx >> 3; 827 if (byteIdx < signature.length) { 828 int bitIdx = idx & 0x7; 829 int mask = 0x1 << bitIdx; 830 int sigByte = signature[byteIdx]; 831 signature[byteIdx] = (byte)(sigByte | mask); 832 } 833 } 834 setInterfaces(ClassFile cf, Class[] interfaces)835 private static void setInterfaces(ClassFile cf, Class[] interfaces) { 836 String setterIntf = ProxyObject.class.getName(); 837 String[] list; 838 if (interfaces == null || interfaces.length == 0) 839 list = new String[] { setterIntf }; 840 else { 841 list = new String[interfaces.length + 1]; 842 for (int i = 0; i < interfaces.length; i++) 843 list[i] = interfaces[i].getName(); 844 845 list[interfaces.length] = setterIntf; 846 } 847 848 cf.setInterfaces(list); 849 } 850 addMethodsHolder(ClassFile cf, ConstPool cp, String classname, int size)851 private static void addMethodsHolder(ClassFile cf, ConstPool cp, 852 String classname, int size) 853 throws CannotCompileException 854 { 855 FieldInfo finfo = new FieldInfo(cp, HOLDER, HOLDER_TYPE); 856 finfo.setAccessFlags(AccessFlag.PRIVATE | AccessFlag.STATIC); 857 cf.addField(finfo); 858 MethodInfo minfo = new MethodInfo(cp, "<clinit>", "()V"); 859 minfo.setAccessFlags(AccessFlag.STATIC); 860 Bytecode code = new Bytecode(cp, 0, 0); 861 code.addIconst(size * 2); 862 code.addAnewarray("java.lang.reflect.Method"); 863 code.addPutstatic(classname, HOLDER, HOLDER_TYPE); 864 // also need to set serial version uid 865 code.addLconst(-1L); 866 code.addPutstatic(classname, SERIAL_VERSION_UID_FIELD, SERIAL_VERSION_UID_TYPE); 867 code.addOpcode(Bytecode.RETURN); 868 minfo.setCodeAttribute(code.toCodeAttribute()); 869 cf.addMethod(minfo); 870 } 871 addSetter(String classname, ClassFile cf, ConstPool cp)872 private static void addSetter(String classname, ClassFile cf, ConstPool cp) 873 throws CannotCompileException 874 { 875 MethodInfo minfo = new MethodInfo(cp, HANDLER_SETTER, 876 HANDLER_SETTER_TYPE); 877 minfo.setAccessFlags(AccessFlag.PUBLIC); 878 Bytecode code = new Bytecode(cp, 2, 2); 879 code.addAload(0); 880 code.addAload(1); 881 code.addPutfield(classname, HANDLER, HANDLER_TYPE); 882 code.addOpcode(Bytecode.RETURN); 883 minfo.setCodeAttribute(code.toCodeAttribute()); 884 cf.addMethod(minfo); 885 } 886 addGetter(String classname, ClassFile cf, ConstPool cp)887 private static void addGetter(String classname, ClassFile cf, ConstPool cp) 888 throws CannotCompileException 889 { 890 MethodInfo minfo = new MethodInfo(cp, HANDLER_GETTER, 891 HANDLER_GETTER_TYPE); 892 minfo.setAccessFlags(AccessFlag.PUBLIC); 893 Bytecode code = new Bytecode(cp, 1, 1); 894 code.addAload(0); 895 code.addGetfield(classname, HANDLER, HANDLER_TYPE); 896 code.addOpcode(Bytecode.ARETURN); 897 minfo.setCodeAttribute(code.toCodeAttribute()); 898 cf.addMethod(minfo); 899 } 900 overrideMethods(ClassFile cf, ConstPool cp, String className)901 private int overrideMethods(ClassFile cf, ConstPool cp, String className) 902 throws CannotCompileException 903 { 904 String prefix = makeUniqueName("_d", signatureMethods); 905 Iterator it = signatureMethods.iterator(); 906 int index = 0; 907 while (it.hasNext()) { 908 Map.Entry e = (Map.Entry)it.next(); 909 String key = (String)e.getKey(); 910 Method meth = (Method)e.getValue(); 911 int mod = meth.getModifiers(); 912 if (testBit(signature, index)) { 913 override(className, meth, prefix, index, 914 keyToDesc(key), cf, cp); 915 } 916 index++; 917 } 918 919 return index; 920 } 921 override(String thisClassname, Method meth, String prefix, int index, String desc, ClassFile cf, ConstPool cp)922 private void override(String thisClassname, Method meth, String prefix, 923 int index, String desc, ClassFile cf, ConstPool cp) 924 throws CannotCompileException 925 { 926 Class declClass = meth.getDeclaringClass(); 927 String delegatorName = prefix + index + meth.getName(); 928 if (Modifier.isAbstract(meth.getModifiers())) 929 delegatorName = null; 930 else { 931 MethodInfo delegator 932 = makeDelegator(meth, desc, cp, declClass, delegatorName); 933 // delegator is not a bridge method. See Sec. 15.12.4.5 of JLS 3rd Ed. 934 delegator.setAccessFlags(delegator.getAccessFlags() & ~AccessFlag.BRIDGE); 935 cf.addMethod(delegator); 936 } 937 938 MethodInfo forwarder 939 = makeForwarder(thisClassname, meth, desc, cp, declClass, 940 delegatorName, index); 941 cf.addMethod(forwarder); 942 } 943 makeConstructors(String thisClassName, ClassFile cf, ConstPool cp, String classname)944 private void makeConstructors(String thisClassName, ClassFile cf, 945 ConstPool cp, String classname) throws CannotCompileException 946 { 947 Constructor[] cons = SecurityActions.getDeclaredConstructors(superClass); 948 // legacy: if we are not caching then we need to initialise the default handler 949 boolean doHandlerInit = !factoryUseCache; 950 for (int i = 0; i < cons.length; i++) { 951 Constructor c = cons[i]; 952 int mod = c.getModifiers(); 953 if (!Modifier.isFinal(mod) && !Modifier.isPrivate(mod) 954 && isVisible(mod, basename, c)) { 955 MethodInfo m = makeConstructor(thisClassName, c, cp, superClass, doHandlerInit); 956 cf.addMethod(m); 957 } 958 } 959 } 960 makeUniqueName(String name, List sortedMethods)961 private static String makeUniqueName(String name, List sortedMethods) { 962 if (makeUniqueName0(name, sortedMethods.iterator())) 963 return name; 964 965 for (int i = 100; i < 999; i++) { 966 String s = name + i; 967 if (makeUniqueName0(s, sortedMethods.iterator())) 968 return s; 969 } 970 971 throw new RuntimeException("cannot make a unique method name"); 972 } 973 makeUniqueName0(String name, Iterator it)974 private static boolean makeUniqueName0(String name, Iterator it) { 975 while (it.hasNext()) { 976 Map.Entry e = (Map.Entry)it.next(); 977 String key = (String)e.getKey(); 978 if (key.startsWith(name)) 979 return false; 980 } 981 982 return true; 983 } 984 985 /** 986 * Returns true if the method is visible from the package. 987 * 988 * @param mod the modifiers of the method. 989 */ isVisible(int mod, String from, Member meth)990 private static boolean isVisible(int mod, String from, Member meth) { 991 if ((mod & Modifier.PRIVATE) != 0) 992 return false; 993 else if ((mod & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0) 994 return true; 995 else { 996 String p = getPackageName(from); 997 String q = getPackageName(meth.getDeclaringClass().getName()); 998 if (p == null) 999 return q == null; 1000 else 1001 return p.equals(q); 1002 } 1003 } 1004 getPackageName(String name)1005 private static String getPackageName(String name) { 1006 int i = name.lastIndexOf('.'); 1007 if (i < 0) 1008 return null; 1009 else 1010 return name.substring(0, i); 1011 } 1012 getMethods(Class superClass, Class[] interfaceTypes)1013 private static HashMap getMethods(Class superClass, Class[] interfaceTypes) { 1014 HashMap hash = new HashMap(); 1015 for (int i = 0; i < interfaceTypes.length; i++) 1016 getMethods(hash, interfaceTypes[i]); 1017 1018 getMethods(hash, superClass); 1019 return hash; 1020 } 1021 getMethods(HashMap hash, Class clazz)1022 private static void getMethods(HashMap hash, Class clazz) { 1023 Class[] ifs = clazz.getInterfaces(); 1024 for (int i = 0; i < ifs.length; i++) 1025 getMethods(hash, ifs[i]); 1026 1027 Class parent = clazz.getSuperclass(); 1028 if (parent != null) 1029 getMethods(hash, parent); 1030 1031 Method[] methods = SecurityActions.getDeclaredMethods(clazz); 1032 for (int i = 0; i < methods.length; i++) 1033 if (!Modifier.isPrivate(methods[i].getModifiers())) { 1034 Method m = methods[i]; 1035 String key = m.getName() + ':' + RuntimeSupport.makeDescriptor(m); 1036 // JIRA JASSIST-85 1037 // put the method to the cache, retrieve previous definition (if any) 1038 Method oldMethod = (Method)hash.put(key, methods[i]); 1039 1040 // check if visibility has been reduced 1041 if (null != oldMethod && Modifier.isPublic(oldMethod.getModifiers()) 1042 && !Modifier.isPublic(methods[i].getModifiers()) ) { 1043 // we tried to overwrite a public definition with a non-public definition, 1044 // use the old definition instead. 1045 hash.put(key, oldMethod); 1046 } 1047 } 1048 } 1049 keyToDesc(String key)1050 private static String keyToDesc(String key) { 1051 return key.substring(key.indexOf(':') + 1); 1052 } 1053 makeConstructor(String thisClassName, Constructor cons, ConstPool cp, Class superClass, boolean doHandlerInit)1054 private static MethodInfo makeConstructor(String thisClassName, Constructor cons, 1055 ConstPool cp, Class superClass, boolean doHandlerInit) { 1056 String desc = RuntimeSupport.makeDescriptor(cons.getParameterTypes(), 1057 Void.TYPE); 1058 MethodInfo minfo = new MethodInfo(cp, "<init>", desc); 1059 minfo.setAccessFlags(Modifier.PUBLIC); // cons.getModifiers() & ~Modifier.NATIVE 1060 setThrows(minfo, cp, cons.getExceptionTypes()); 1061 Bytecode code = new Bytecode(cp, 0, 0); 1062 1063 // legacy: if we are not using caching then we initialise the instance's handler 1064 // from the class's static default interceptor and skip the next few instructions if 1065 // it is non-null 1066 if (doHandlerInit) { 1067 code.addAload(0); 1068 code.addGetstatic(thisClassName, DEFAULT_INTERCEPTOR, HANDLER_TYPE); 1069 code.addPutfield(thisClassName, HANDLER, HANDLER_TYPE); 1070 code.addGetstatic(thisClassName, DEFAULT_INTERCEPTOR, HANDLER_TYPE); 1071 code.addOpcode(Opcode.IFNONNULL); 1072 code.addIndex(10); 1073 } 1074 // if caching is enabled then we don't have a handler to initialise so this else branch will install 1075 // the handler located in the static field of class RuntimeSupport. 1076 code.addAload(0); 1077 code.addGetstatic(NULL_INTERCEPTOR_HOLDER, DEFAULT_INTERCEPTOR, HANDLER_TYPE); 1078 code.addPutfield(thisClassName, HANDLER, HANDLER_TYPE); 1079 int pc = code.currentPc(); 1080 1081 code.addAload(0); 1082 int s = addLoadParameters(code, cons.getParameterTypes(), 1); 1083 code.addInvokespecial(superClass.getName(), "<init>", desc); 1084 code.addOpcode(Opcode.RETURN); 1085 code.setMaxLocals(s + 1); 1086 CodeAttribute ca = code.toCodeAttribute(); 1087 minfo.setCodeAttribute(ca); 1088 1089 StackMapTable.Writer writer = new StackMapTable.Writer(32); 1090 writer.sameFrame(pc); 1091 ca.setAttribute(writer.toStackMapTable(cp)); 1092 return minfo; 1093 } 1094 makeDelegator(Method meth, String desc, ConstPool cp, Class declClass, String delegatorName)1095 private static MethodInfo makeDelegator(Method meth, String desc, 1096 ConstPool cp, Class declClass, String delegatorName) { 1097 MethodInfo delegator = new MethodInfo(cp, delegatorName, desc); 1098 delegator.setAccessFlags(Modifier.FINAL | Modifier.PUBLIC 1099 | (meth.getModifiers() & ~(Modifier.PRIVATE 1100 | Modifier.PROTECTED 1101 | Modifier.ABSTRACT 1102 | Modifier.NATIVE 1103 | Modifier.SYNCHRONIZED))); 1104 setThrows(delegator, cp, meth); 1105 Bytecode code = new Bytecode(cp, 0, 0); 1106 code.addAload(0); 1107 int s = addLoadParameters(code, meth.getParameterTypes(), 1); 1108 code.addInvokespecial(declClass.getName(), meth.getName(), desc); 1109 addReturn(code, meth.getReturnType()); 1110 code.setMaxLocals(++s); 1111 delegator.setCodeAttribute(code.toCodeAttribute()); 1112 return delegator; 1113 } 1114 1115 /** 1116 * @param delegatorName null if the original method is abstract. 1117 */ makeForwarder(String thisClassName, Method meth, String desc, ConstPool cp, Class declClass, String delegatorName, int index)1118 private static MethodInfo makeForwarder(String thisClassName, 1119 Method meth, String desc, ConstPool cp, 1120 Class declClass, String delegatorName, int index) { 1121 MethodInfo forwarder = new MethodInfo(cp, meth.getName(), desc); 1122 forwarder.setAccessFlags(Modifier.FINAL 1123 | (meth.getModifiers() & ~(Modifier.ABSTRACT 1124 | Modifier.NATIVE 1125 | Modifier.SYNCHRONIZED))); 1126 setThrows(forwarder, cp, meth); 1127 int args = Descriptor.paramSize(desc); 1128 Bytecode code = new Bytecode(cp, 0, args + 2); 1129 /* 1130 * if (methods[index * 2] == null) { 1131 * methods[index * 2] 1132 * = RuntimeSupport.findSuperMethod(this, <overridden name>, <desc>); 1133 * methods[index * 2 + 1] 1134 * = RuntimeSupport.findMethod(this, <delegator name>, <desc>); 1135 * or = null // the original method is abstract. 1136 * } 1137 * return ($r)handler.invoke(this, methods[index * 2], 1138 * methods[index * 2 + 1], $args); 1139 */ 1140 int origIndex = index * 2; 1141 int delIndex = index * 2 + 1; 1142 int arrayVar = args + 1; 1143 code.addGetstatic(thisClassName, HOLDER, HOLDER_TYPE); 1144 code.addAstore(arrayVar); 1145 1146 callFind2Methods(code, meth.getName(), delegatorName, origIndex, desc, arrayVar); 1147 1148 code.addAload(0); 1149 code.addGetfield(thisClassName, HANDLER, HANDLER_TYPE); 1150 code.addAload(0); 1151 1152 code.addAload(arrayVar); 1153 code.addIconst(origIndex); 1154 code.addOpcode(Opcode.AALOAD); 1155 1156 code.addAload(arrayVar); 1157 code.addIconst(delIndex); 1158 code.addOpcode(Opcode.AALOAD); 1159 1160 makeParameterList(code, meth.getParameterTypes()); 1161 code.addInvokeinterface(MethodHandler.class.getName(), "invoke", 1162 "(Ljava/lang/Object;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;", 1163 5); 1164 Class retType = meth.getReturnType(); 1165 addUnwrapper(code, retType); 1166 addReturn(code, retType); 1167 1168 CodeAttribute ca = code.toCodeAttribute(); 1169 forwarder.setCodeAttribute(ca); 1170 return forwarder; 1171 } 1172 setThrows(MethodInfo minfo, ConstPool cp, Method orig)1173 private static void setThrows(MethodInfo minfo, ConstPool cp, Method orig) { 1174 Class[] exceptions = orig.getExceptionTypes(); 1175 setThrows(minfo, cp, exceptions); 1176 } 1177 setThrows(MethodInfo minfo, ConstPool cp, Class[] exceptions)1178 private static void setThrows(MethodInfo minfo, ConstPool cp, 1179 Class[] exceptions) { 1180 if (exceptions.length == 0) 1181 return; 1182 1183 String[] list = new String[exceptions.length]; 1184 for (int i = 0; i < exceptions.length; i++) 1185 list[i] = exceptions[i].getName(); 1186 1187 ExceptionsAttribute ea = new ExceptionsAttribute(cp); 1188 ea.setExceptions(list); 1189 minfo.setExceptionsAttribute(ea); 1190 } 1191 addLoadParameters(Bytecode code, Class[] params, int offset)1192 private static int addLoadParameters(Bytecode code, Class[] params, 1193 int offset) { 1194 int stacksize = 0; 1195 int n = params.length; 1196 for (int i = 0; i < n; ++i) 1197 stacksize += addLoad(code, stacksize + offset, params[i]); 1198 1199 return stacksize; 1200 } 1201 addLoad(Bytecode code, int n, Class type)1202 private static int addLoad(Bytecode code, int n, Class type) { 1203 if (type.isPrimitive()) { 1204 if (type == Long.TYPE) { 1205 code.addLload(n); 1206 return 2; 1207 } 1208 else if (type == Float.TYPE) 1209 code.addFload(n); 1210 else if (type == Double.TYPE) { 1211 code.addDload(n); 1212 return 2; 1213 } 1214 else 1215 code.addIload(n); 1216 } 1217 else 1218 code.addAload(n); 1219 1220 return 1; 1221 } 1222 addReturn(Bytecode code, Class type)1223 private static int addReturn(Bytecode code, Class type) { 1224 if (type.isPrimitive()) { 1225 if (type == Long.TYPE) { 1226 code.addOpcode(Opcode.LRETURN); 1227 return 2; 1228 } 1229 else if (type == Float.TYPE) 1230 code.addOpcode(Opcode.FRETURN); 1231 else if (type == Double.TYPE) { 1232 code.addOpcode(Opcode.DRETURN); 1233 return 2; 1234 } 1235 else if (type == Void.TYPE) { 1236 code.addOpcode(Opcode.RETURN); 1237 return 0; 1238 } 1239 else 1240 code.addOpcode(Opcode.IRETURN); 1241 } 1242 else 1243 code.addOpcode(Opcode.ARETURN); 1244 1245 return 1; 1246 } 1247 makeParameterList(Bytecode code, Class[] params)1248 private static void makeParameterList(Bytecode code, Class[] params) { 1249 int regno = 1; 1250 int n = params.length; 1251 code.addIconst(n); 1252 code.addAnewarray("java/lang/Object"); 1253 for (int i = 0; i < n; i++) { 1254 code.addOpcode(Opcode.DUP); 1255 code.addIconst(i); 1256 Class type = params[i]; 1257 if (type.isPrimitive()) 1258 regno = makeWrapper(code, type, regno); 1259 else { 1260 code.addAload(regno); 1261 regno++; 1262 } 1263 1264 code.addOpcode(Opcode.AASTORE); 1265 } 1266 } 1267 makeWrapper(Bytecode code, Class type, int regno)1268 private static int makeWrapper(Bytecode code, Class type, int regno) { 1269 int index = FactoryHelper.typeIndex(type); 1270 String wrapper = FactoryHelper.wrapperTypes[index]; 1271 code.addNew(wrapper); 1272 code.addOpcode(Opcode.DUP); 1273 addLoad(code, regno, type); 1274 code.addInvokespecial(wrapper, "<init>", 1275 FactoryHelper.wrapperDesc[index]); 1276 return regno + FactoryHelper.dataSize[index]; 1277 } 1278 1279 /** 1280 * @param thisMethod might be null. 1281 */ callFind2Methods(Bytecode code, String superMethod, String thisMethod, int index, String desc, int arrayVar)1282 private static void callFind2Methods(Bytecode code, String superMethod, String thisMethod, 1283 int index, String desc, int arrayVar) { 1284 String findClass = RuntimeSupport.class.getName(); 1285 String findDesc 1286 = "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;[Ljava/lang/reflect/Method;)V"; 1287 1288 code.addAload(0); 1289 code.addLdc(superMethod); 1290 if (thisMethod == null) 1291 code.addOpcode(Opcode.ACONST_NULL); 1292 else 1293 code.addLdc(thisMethod); 1294 1295 code.addIconst(index); 1296 code.addLdc(desc); 1297 code.addAload(arrayVar); 1298 code.addInvokestatic(findClass, "find2Methods", findDesc); 1299 } 1300 addUnwrapper(Bytecode code, Class type)1301 private static void addUnwrapper(Bytecode code, Class type) { 1302 if (type.isPrimitive()) { 1303 if (type == Void.TYPE) 1304 code.addOpcode(Opcode.POP); 1305 else { 1306 int index = FactoryHelper.typeIndex(type); 1307 String wrapper = FactoryHelper.wrapperTypes[index]; 1308 code.addCheckcast(wrapper); 1309 code.addInvokevirtual(wrapper, 1310 FactoryHelper.unwarpMethods[index], 1311 FactoryHelper.unwrapDesc[index]); 1312 } 1313 } 1314 else 1315 code.addCheckcast(type.getName()); 1316 } 1317 makeWriteReplace(ConstPool cp)1318 private static MethodInfo makeWriteReplace(ConstPool cp) { 1319 MethodInfo minfo = new MethodInfo(cp, "writeReplace", "()Ljava/lang/Object;"); 1320 String[] list = new String[1]; 1321 list[0] = "java.io.ObjectStreamException"; 1322 ExceptionsAttribute ea = new ExceptionsAttribute(cp); 1323 ea.setExceptions(list); 1324 minfo.setExceptionsAttribute(ea); 1325 Bytecode code = new Bytecode(cp, 0, 1); 1326 code.addAload(0); 1327 code.addInvokestatic("javassist.util.proxy.RuntimeSupport", 1328 "makeSerializedProxy", 1329 "(Ljava/lang/Object;)Ljavassist/util/proxy/SerializedProxy;"); 1330 code.addOpcode(Opcode.ARETURN); 1331 minfo.setCodeAttribute(code.toCodeAttribute()); 1332 return minfo; 1333 } 1334 } 1335