001/* 002 * Copyright 2003,2004 The Apache Software Foundation 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.cglib.core; 018 019import java.beans.BeanInfo; 020import java.beans.IntrospectionException; 021import java.beans.Introspector; 022import java.beans.PropertyDescriptor; 023import java.lang.invoke.MethodHandles; 024import java.lang.reflect.Constructor; 025import java.lang.reflect.InvocationTargetException; 026import java.lang.reflect.Member; 027import java.lang.reflect.Method; 028import java.lang.reflect.Modifier; 029import java.security.AccessController; 030import java.security.PrivilegedAction; 031import java.security.PrivilegedExceptionAction; 032import java.security.ProtectionDomain; 033import java.util.ArrayList; 034import java.util.Arrays; 035import java.util.HashMap; 036import java.util.HashSet; 037import java.util.List; 038import java.util.Map; 039import java.util.Set; 040 041import org.springframework.asm.Attribute; 042import org.springframework.asm.Type; 043 044/** 045 * @version $Id: ReflectUtils.java,v 1.30 2009/01/11 19:47:49 herbyderby Exp $ 046 */ 047@SuppressWarnings({"rawtypes", "unchecked"}) 048public class ReflectUtils { 049 050 private ReflectUtils() { 051 } 052 053 private static final Map primitives = new HashMap(8); 054 055 private static final Map transforms = new HashMap(8); 056 057 private static final ClassLoader defaultLoader = ReflectUtils.class.getClassLoader(); 058 059 // SPRING PATCH BEGIN 060 private static final Method privateLookupInMethod; 061 062 private static final Method lookupDefineClassMethod; 063 064 private static final Method classLoaderDefineClassMethod; 065 066 private static final ProtectionDomain PROTECTION_DOMAIN; 067 068 private static final Throwable THROWABLE; 069 070 private static final List<Method> OBJECT_METHODS = new ArrayList<Method>(); 071 072 static { 073 Method privateLookupIn; 074 Method lookupDefineClass; 075 Method classLoaderDefineClass; 076 ProtectionDomain protectionDomain; 077 Throwable throwable = null; 078 try { 079 privateLookupIn = (Method) AccessController.doPrivileged(new PrivilegedExceptionAction() { 080 public Object run() throws Exception { 081 try { 082 return MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class); 083 } 084 catch (NoSuchMethodException ex) { 085 return null; 086 } 087 } 088 }); 089 lookupDefineClass = (Method) AccessController.doPrivileged(new PrivilegedExceptionAction() { 090 public Object run() throws Exception { 091 try { 092 return MethodHandles.Lookup.class.getMethod("defineClass", byte[].class); 093 } 094 catch (NoSuchMethodException ex) { 095 return null; 096 } 097 } 098 }); 099 classLoaderDefineClass = (Method) AccessController.doPrivileged(new PrivilegedExceptionAction() { 100 public Object run() throws Exception { 101 return ClassLoader.class.getDeclaredMethod("defineClass", 102 String.class, byte[].class, Integer.TYPE, Integer.TYPE, ProtectionDomain.class); 103 } 104 }); 105 protectionDomain = getProtectionDomain(ReflectUtils.class); 106 AccessController.doPrivileged(new PrivilegedExceptionAction() { 107 public Object run() throws Exception { 108 Method[] methods = Object.class.getDeclaredMethods(); 109 for (Method method : methods) { 110 if ("finalize".equals(method.getName()) 111 || (method.getModifiers() & (Modifier.FINAL | Modifier.STATIC)) > 0) { 112 continue; 113 } 114 OBJECT_METHODS.add(method); 115 } 116 return null; 117 } 118 }); 119 } 120 catch (Throwable t) { 121 privateLookupIn = null; 122 lookupDefineClass = null; 123 classLoaderDefineClass = null; 124 protectionDomain = null; 125 throwable = t; 126 } 127 privateLookupInMethod = privateLookupIn; 128 lookupDefineClassMethod = lookupDefineClass; 129 classLoaderDefineClassMethod = classLoaderDefineClass; 130 PROTECTION_DOMAIN = protectionDomain; 131 THROWABLE = throwable; 132 } 133 // SPRING PATCH END 134 135 private static final String[] CGLIB_PACKAGES = { 136 "java.lang", 137 }; 138 139 static { 140 primitives.put("byte", Byte.TYPE); 141 primitives.put("char", Character.TYPE); 142 primitives.put("double", Double.TYPE); 143 primitives.put("float", Float.TYPE); 144 primitives.put("int", Integer.TYPE); 145 primitives.put("long", Long.TYPE); 146 primitives.put("short", Short.TYPE); 147 primitives.put("boolean", Boolean.TYPE); 148 149 transforms.put("byte", "B"); 150 transforms.put("char", "C"); 151 transforms.put("double", "D"); 152 transforms.put("float", "F"); 153 transforms.put("int", "I"); 154 transforms.put("long", "J"); 155 transforms.put("short", "S"); 156 transforms.put("boolean", "Z"); 157 } 158 159 public static ProtectionDomain getProtectionDomain(final Class source) { 160 if (source == null) { 161 return null; 162 } 163 return (ProtectionDomain) AccessController.doPrivileged(new PrivilegedAction() { 164 public Object run() { 165 return source.getProtectionDomain(); 166 } 167 }); 168 } 169 170 public static Type[] getExceptionTypes(Member member) { 171 if (member instanceof Method) { 172 return TypeUtils.getTypes(((Method) member).getExceptionTypes()); 173 } 174 else if (member instanceof Constructor) { 175 return TypeUtils.getTypes(((Constructor) member).getExceptionTypes()); 176 } 177 else { 178 throw new IllegalArgumentException("Cannot get exception types of a field"); 179 } 180 } 181 182 public static Signature getSignature(Member member) { 183 if (member instanceof Method) { 184 return new Signature(member.getName(), Type.getMethodDescriptor((Method) member)); 185 } 186 else if (member instanceof Constructor) { 187 Type[] types = TypeUtils.getTypes(((Constructor) member).getParameterTypes()); 188 return new Signature(Constants.CONSTRUCTOR_NAME, 189 Type.getMethodDescriptor(Type.VOID_TYPE, types)); 190 191 } 192 else { 193 throw new IllegalArgumentException("Cannot get signature of a field"); 194 } 195 } 196 197 public static Constructor findConstructor(String desc) { 198 return findConstructor(desc, defaultLoader); 199 } 200 201 public static Constructor findConstructor(String desc, ClassLoader loader) { 202 try { 203 int lparen = desc.indexOf('('); 204 String className = desc.substring(0, lparen).trim(); 205 return getClass(className, loader).getConstructor(parseTypes(desc, loader)); 206 } 207 catch (ClassNotFoundException | NoSuchMethodException ex) { 208 throw new CodeGenerationException(ex); 209 } 210 } 211 212 public static Method findMethod(String desc) { 213 return findMethod(desc, defaultLoader); 214 } 215 216 public static Method findMethod(String desc, ClassLoader loader) { 217 try { 218 int lparen = desc.indexOf('('); 219 int dot = desc.lastIndexOf('.', lparen); 220 String className = desc.substring(0, dot).trim(); 221 String methodName = desc.substring(dot + 1, lparen).trim(); 222 return getClass(className, loader).getDeclaredMethod(methodName, parseTypes(desc, loader)); 223 } 224 catch (ClassNotFoundException | NoSuchMethodException ex) { 225 throw new CodeGenerationException(ex); 226 } 227 } 228 229 private static Class[] parseTypes(String desc, ClassLoader loader) throws ClassNotFoundException { 230 int lparen = desc.indexOf('('); 231 int rparen = desc.indexOf(')', lparen); 232 List params = new ArrayList(); 233 int start = lparen + 1; 234 for (; ; ) { 235 int comma = desc.indexOf(',', start); 236 if (comma < 0) { 237 break; 238 } 239 params.add(desc.substring(start, comma).trim()); 240 start = comma + 1; 241 } 242 if (start < rparen) { 243 params.add(desc.substring(start, rparen).trim()); 244 } 245 Class[] types = new Class[params.size()]; 246 for (int i = 0; i < types.length; i++) { 247 types[i] = getClass((String) params.get(i), loader); 248 } 249 return types; 250 } 251 252 private static Class getClass(String className, ClassLoader loader) throws ClassNotFoundException { 253 return getClass(className, loader, CGLIB_PACKAGES); 254 } 255 256 private static Class getClass(String className, ClassLoader loader, String[] packages) throws ClassNotFoundException { 257 String save = className; 258 int dimensions = 0; 259 int index = 0; 260 while ((index = className.indexOf("[]", index) + 1) > 0) { 261 dimensions++; 262 } 263 StringBuffer brackets = new StringBuffer(className.length() - dimensions); 264 for (int i = 0; i < dimensions; i++) { 265 brackets.append('['); 266 } 267 className = className.substring(0, className.length() - 2 * dimensions); 268 269 String prefix = (dimensions > 0) ? brackets + "L" : ""; 270 String suffix = (dimensions > 0) ? ";" : ""; 271 try { 272 return Class.forName(prefix + className + suffix, false, loader); 273 } 274 catch (ClassNotFoundException ignore) { 275 } 276 for (int i = 0; i < packages.length; i++) { 277 try { 278 return Class.forName(prefix + packages[i] + '.' + className + suffix, false, loader); 279 } 280 catch (ClassNotFoundException ignore) { 281 } 282 } 283 if (dimensions == 0) { 284 Class c = (Class) primitives.get(className); 285 if (c != null) { 286 return c; 287 } 288 } 289 else { 290 String transform = (String) transforms.get(className); 291 if (transform != null) { 292 try { 293 return Class.forName(brackets + transform, false, loader); 294 } 295 catch (ClassNotFoundException ignore) { 296 } 297 } 298 } 299 throw new ClassNotFoundException(save); 300 } 301 302 public static Object newInstance(Class type) { 303 return newInstance(type, Constants.EMPTY_CLASS_ARRAY, null); 304 } 305 306 public static Object newInstance(Class type, Class[] parameterTypes, Object[] args) { 307 return newInstance(getConstructor(type, parameterTypes), args); 308 } 309 310 @SuppressWarnings("deprecation") // on JDK 9 311 public static Object newInstance(final Constructor cstruct, final Object[] args) { 312 boolean flag = cstruct.isAccessible(); 313 try { 314 if (!flag) { 315 cstruct.setAccessible(true); 316 } 317 Object result = cstruct.newInstance(args); 318 return result; 319 } 320 catch (InstantiationException e) { 321 throw new CodeGenerationException(e); 322 } 323 catch (IllegalAccessException e) { 324 throw new CodeGenerationException(e); 325 } 326 catch (InvocationTargetException e) { 327 throw new CodeGenerationException(e.getTargetException()); 328 } 329 finally { 330 if (!flag) { 331 cstruct.setAccessible(flag); 332 } 333 } 334 } 335 336 public static Constructor getConstructor(Class type, Class[] parameterTypes) { 337 try { 338 Constructor constructor = type.getDeclaredConstructor(parameterTypes); 339 if (System.getSecurityManager() != null) { 340 AccessController.doPrivileged((PrivilegedAction<Object>) () -> { 341 constructor.setAccessible(true); 342 return null; 343 }); 344 } 345 else { 346 constructor.setAccessible(true); 347 } 348 return constructor; 349 } 350 catch (NoSuchMethodException e) { 351 throw new CodeGenerationException(e); 352 } 353 } 354 355 public static String[] getNames(Class[] classes) { 356 if (classes == null) 357 return null; 358 String[] names = new String[classes.length]; 359 for (int i = 0; i < names.length; i++) { 360 names[i] = classes[i].getName(); 361 } 362 return names; 363 } 364 365 public static Class[] getClasses(Object[] objects) { 366 Class[] classes = new Class[objects.length]; 367 for (int i = 0; i < objects.length; i++) { 368 classes[i] = objects[i].getClass(); 369 } 370 return classes; 371 } 372 373 public static Method findNewInstance(Class iface) { 374 Method m = findInterfaceMethod(iface); 375 if (!m.getName().equals("newInstance")) { 376 throw new IllegalArgumentException(iface + " missing newInstance method"); 377 } 378 return m; 379 } 380 381 public static Method[] getPropertyMethods(PropertyDescriptor[] properties, boolean read, boolean write) { 382 Set methods = new HashSet(); 383 for (int i = 0; i < properties.length; i++) { 384 PropertyDescriptor pd = properties[i]; 385 if (read) { 386 methods.add(pd.getReadMethod()); 387 } 388 if (write) { 389 methods.add(pd.getWriteMethod()); 390 } 391 } 392 methods.remove(null); 393 return (Method[]) methods.toArray(new Method[methods.size()]); 394 } 395 396 public static PropertyDescriptor[] getBeanProperties(Class type) { 397 return getPropertiesHelper(type, true, true); 398 } 399 400 public static PropertyDescriptor[] getBeanGetters(Class type) { 401 return getPropertiesHelper(type, true, false); 402 } 403 404 public static PropertyDescriptor[] getBeanSetters(Class type) { 405 return getPropertiesHelper(type, false, true); 406 } 407 408 private static PropertyDescriptor[] getPropertiesHelper(Class type, boolean read, boolean write) { 409 try { 410 BeanInfo info = Introspector.getBeanInfo(type, Object.class); 411 PropertyDescriptor[] all = info.getPropertyDescriptors(); 412 if (read && write) { 413 return all; 414 } 415 List properties = new ArrayList(all.length); 416 for (int i = 0; i < all.length; i++) { 417 PropertyDescriptor pd = all[i]; 418 if ((read && pd.getReadMethod() != null) || 419 (write && pd.getWriteMethod() != null)) { 420 properties.add(pd); 421 } 422 } 423 return (PropertyDescriptor[]) properties.toArray(new PropertyDescriptor[properties.size()]); 424 } 425 catch (IntrospectionException e) { 426 throw new CodeGenerationException(e); 427 } 428 } 429 430 public static Method findDeclaredMethod(final Class type, 431 final String methodName, final Class[] parameterTypes) 432 throws NoSuchMethodException { 433 434 Class cl = type; 435 while (cl != null) { 436 try { 437 return cl.getDeclaredMethod(methodName, parameterTypes); 438 } 439 catch (NoSuchMethodException e) { 440 cl = cl.getSuperclass(); 441 } 442 } 443 throw new NoSuchMethodException(methodName); 444 } 445 446 public static List addAllMethods(final Class type, final List list) { 447 if (type == Object.class) { 448 list.addAll(OBJECT_METHODS); 449 } 450 else 451 list.addAll(java.util.Arrays.asList(type.getDeclaredMethods())); 452 453 Class superclass = type.getSuperclass(); 454 if (superclass != null) { 455 addAllMethods(superclass, list); 456 } 457 Class[] interfaces = type.getInterfaces(); 458 for (int i = 0; i < interfaces.length; i++) { 459 addAllMethods(interfaces[i], list); 460 } 461 462 return list; 463 } 464 465 public static List addAllInterfaces(Class type, List list) { 466 Class superclass = type.getSuperclass(); 467 if (superclass != null) { 468 list.addAll(Arrays.asList(type.getInterfaces())); 469 addAllInterfaces(superclass, list); 470 } 471 return list; 472 } 473 474 475 public static Method findInterfaceMethod(Class iface) { 476 if (!iface.isInterface()) { 477 throw new IllegalArgumentException(iface + " is not an interface"); 478 } 479 Method[] methods = iface.getDeclaredMethods(); 480 if (methods.length != 1) { 481 throw new IllegalArgumentException("expecting exactly 1 method in " + iface); 482 } 483 return methods[0]; 484 } 485 486 // SPRING PATCH BEGIN 487 public static Class defineClass(String className, byte[] b, ClassLoader loader) throws Exception { 488 return defineClass(className, b, loader, null, null); 489 } 490 491 public static Class defineClass(String className, byte[] b, ClassLoader loader, 492 ProtectionDomain protectionDomain) throws Exception { 493 494 return defineClass(className, b, loader, protectionDomain, null); 495 } 496 497 @SuppressWarnings("deprecation") // on JDK 9 498 public static Class defineClass(String className, byte[] b, ClassLoader loader, 499 ProtectionDomain protectionDomain, Class<?> contextClass) throws Exception { 500 501 Class c = null; 502 503 // Preferred option: JDK 9+ Lookup.defineClass API if ClassLoader matches 504 if (contextClass != null && contextClass.getClassLoader() == loader && 505 privateLookupInMethod != null && lookupDefineClassMethod != null) { 506 try { 507 MethodHandles.Lookup lookup = (MethodHandles.Lookup) 508 privateLookupInMethod.invoke(null, contextClass, MethodHandles.lookup()); 509 c = (Class) lookupDefineClassMethod.invoke(lookup, b); 510 } 511 catch (InvocationTargetException ex) { 512 Throwable target = ex.getTargetException(); 513 if (target.getClass() != LinkageError.class && target.getClass() != IllegalArgumentException.class) { 514 throw new CodeGenerationException(target); 515 } 516 // in case of plain LinkageError (class already defined) 517 // or IllegalArgumentException (class in different package): 518 // fall through to traditional ClassLoader.defineClass below 519 } 520 catch (Throwable ex) { 521 throw new CodeGenerationException(ex); 522 } 523 } 524 525 // Classic option: protected ClassLoader.defineClass method 526 if (c == null && classLoaderDefineClassMethod != null) { 527 if (protectionDomain == null) { 528 protectionDomain = PROTECTION_DOMAIN; 529 } 530 Object[] args = new Object[]{className, b, 0, b.length, protectionDomain}; 531 try { 532 if (!classLoaderDefineClassMethod.isAccessible()) { 533 classLoaderDefineClassMethod.setAccessible(true); 534 } 535 c = (Class) classLoaderDefineClassMethod.invoke(loader, args); 536 } 537 catch (InvocationTargetException ex) { 538 throw new CodeGenerationException(ex.getTargetException()); 539 } 540 catch (Throwable ex) { 541 // Fall through if setAccessible fails with InaccessibleObjectException on JDK 9+ 542 // (on the module path and/or with a JVM bootstrapped with --illegal-access=deny) 543 if (!ex.getClass().getName().endsWith("InaccessibleObjectException")) { 544 throw new CodeGenerationException(ex); 545 } 546 } 547 } 548 549 // Fallback option: JDK 9+ Lookup.defineClass API even if ClassLoader does not match 550 if (c == null && contextClass != null && contextClass.getClassLoader() != loader && 551 privateLookupInMethod != null && lookupDefineClassMethod != null) { 552 try { 553 MethodHandles.Lookup lookup = (MethodHandles.Lookup) 554 privateLookupInMethod.invoke(null, contextClass, MethodHandles.lookup()); 555 c = (Class) lookupDefineClassMethod.invoke(lookup, b); 556 } 557 catch (InvocationTargetException ex) { 558 throw new CodeGenerationException(ex.getTargetException()); 559 } 560 catch (Throwable ex) { 561 throw new CodeGenerationException(ex); 562 } 563 } 564 565 // No defineClass variant available at all? 566 if (c == null) { 567 throw new CodeGenerationException(THROWABLE); 568 } 569 570 // Force static initializers to run. 571 Class.forName(className, true, loader); 572 return c; 573 } 574 // SPRING PATCH END 575 576 public static int findPackageProtected(Class[] classes) { 577 for (int i = 0; i < classes.length; i++) { 578 if (!Modifier.isPublic(classes[i].getModifiers())) { 579 return i; 580 } 581 } 582 return 0; 583 } 584 585 public static MethodInfo getMethodInfo(final Member member, final int modifiers) { 586 final Signature sig = getSignature(member); 587 return new MethodInfo() { 588 private ClassInfo ci; 589 590 public ClassInfo getClassInfo() { 591 if (ci == null) 592 ci = ReflectUtils.getClassInfo(member.getDeclaringClass()); 593 return ci; 594 } 595 596 public int getModifiers() { 597 return modifiers; 598 } 599 600 public Signature getSignature() { 601 return sig; 602 } 603 604 public Type[] getExceptionTypes() { 605 return ReflectUtils.getExceptionTypes(member); 606 } 607 608 public Attribute getAttribute() { 609 return null; 610 } 611 }; 612 } 613 614 public static MethodInfo getMethodInfo(Member member) { 615 return getMethodInfo(member, member.getModifiers()); 616 } 617 618 public static ClassInfo getClassInfo(final Class clazz) { 619 final Type type = Type.getType(clazz); 620 final Type sc = (clazz.getSuperclass() == null) ? null : Type.getType(clazz.getSuperclass()); 621 return new ClassInfo() { 622 public Type getType() { 623 return type; 624 } 625 public Type getSuperType() { 626 return sc; 627 } 628 public Type[] getInterfaces() { 629 return TypeUtils.getTypes(clazz.getInterfaces()); 630 } 631 public int getModifiers() { 632 return clazz.getModifiers(); 633 } 634 }; 635 } 636 637 // used by MethodInterceptorGenerated generated code 638 public static Method[] findMethods(String[] namesAndDescriptors, Method[] methods) { 639 Map map = new HashMap(); 640 for (int i = 0; i < methods.length; i++) { 641 Method method = methods[i]; 642 map.put(method.getName() + Type.getMethodDescriptor(method), method); 643 } 644 Method[] result = new Method[namesAndDescriptors.length / 2]; 645 for (int i = 0; i < result.length; i++) { 646 result[i] = (Method) map.get(namesAndDescriptors[i * 2] + namesAndDescriptors[i * 2 + 1]); 647 if (result[i] == null) { 648 // TODO: error? 649 } 650 } 651 return result; 652 } 653 654}