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