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 16package javassist.util.proxy; 17 18import java.lang.reflect.Field; 19import java.lang.reflect.InvocationTargetException; 20import java.lang.reflect.Method; 21import java.lang.reflect.Constructor; 22import java.lang.reflect.Member; 23import java.lang.reflect.Modifier; 24import java.security.ProtectionDomain; 25import java.util.*; 26import java.lang.ref.WeakReference; 27 28import javassist.CannotCompileException; 29import 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 */ 152public 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 */ 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 */ 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 */ 360 public Class getSuperclass() { return superClass; } 361 362 /** 363 * Sets the interfaces of a proxy class. 364 */ 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 */ 376 public Class[] getInterfaces() { return interfaces; } 377 378 /** 379 * Sets a filter that selects the methods that will be controlled by a handler. 380 */ 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 */ 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 */ 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 */ 411 Class createClass(byte[] signature) 412 { 413 installSignature(signature); 414 return createClass1(); 415 } 416 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 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 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 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 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 527 static byte[] getFilterSignature(Class clazz) { 528 return (byte[])getField(clazz, FILTER_SIGNATURE_FIELD); 529 } 530 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 */ 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 586 protected ClassLoader getClassLoader() { 587 return classLoaderProvider.get(this); 588 } 589 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 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 */ 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 */ 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 */ 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 679 private static synchronized String makeProxyName(String classname) { 680 return classname + "_$$_javassist_" + counter++; 681 } 682 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 */ 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 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 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 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 1050 private static String keyToDesc(String key) { 1051 return key.substring(key.indexOf(':') + 1); 1052 } 1053 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 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 */ 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 1173 private static void setThrows(MethodInfo minfo, ConstPool cp, Method orig) { 1174 Class[] exceptions = orig.getExceptionTypes(); 1175 setThrows(minfo, cp, exceptions); 1176 } 1177 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 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 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 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 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 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 */ 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 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 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