1 /* java.lang.reflect.Method - reflection of Java methods 2 Copyright (C) 1998, 2001, 2002 Free Software Foundation, Inc. 3 4 Modifications Copyright (C) 2004 by Etienne Gagnon. 5 Modifications Copyright (C) 2004, 2005 by David Belanger. 6 Modifications Copyright (C) 2004, 2005 by Grzegorz Prokopski. 7 8 This file is part of GNU Classpath. 9 10 GNU Classpath is free software; you can redistribute it and/or modify 11 it under the terms of the GNU General Public License as published by 12 the Free Software Foundation; either version 2, or (at your option) 13 any later version. 14 15 GNU Classpath is distributed in the hope that it will be useful, but 16 WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 General Public License for more details. 19 20 You should have received a copy of the GNU General Public License 21 along with GNU Classpath; see the file COPYING. If not, write to the 22 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 23 02110-1301 USA. 24 25 Linking this library statically or dynamically with other modules is 26 making a combined work based on this library. Thus, the terms and 27 conditions of the GNU General Public License cover the whole 28 combination. 29 30 As a special exception, the copyright holders of this library give you 31 permission to link this library with independent modules to produce an 32 executable, regardless of the license terms of these independent 33 modules, and to copy and distribute the resulting executable under 34 terms of your choice, provided that you also meet, for each linked 35 independent module, the terms and conditions of the license of that 36 module. An independent module is a module which is not derived from 37 or based on this library. If you modify this library, you may extend 38 this exception to your version of the library, but you are not 39 obligated to do so. If you do not wish to do so, delete this 40 exception statement from your version. */ 41 42 43 package java.lang.reflect; 44 45 import java.util.Arrays; 46 47 /** 48 * The Method class represents a member method of a class. It also allows 49 * dynamic invocation, via reflection. This works for both static and 50 * instance methods. Invocation on Method objects knows how to do 51 * widening conversions, but throws {@link IllegalArgumentException} if 52 * a narrowing conversion would be necessary. You can query for information 53 * on this Method regardless of location, but invocation access may be limited 54 * by Java language access controls. If you can't do it in the compiler, you 55 * can't normally do it here either.<p> 56 * 57 * <B>Note:</B> This class returns and accepts types as Classes, even 58 * primitive types; there are Class types defined that represent each 59 * different primitive type. They are <code>java.lang.Boolean.TYPE, 60 * java.lang.Byte.TYPE,</code>, also available as <code>boolean.class, 61 * byte.class</code>, etc. These are not to be confused with the 62 * classes <code>java.lang.Boolean, java.lang.Byte</code>, etc., which are 63 * real classes.<p> 64 * 65 * Also note that this is not a serializable class. It is entirely feasible 66 * to make it serializable using the Externalizable interface, but this is 67 * on Sun, not me. 68 * 69 * @author John Keiser 70 * @author Eric Blake <ebb9@email.byu.edu> 71 * @see Member 72 * @see Class 73 * @see java.lang.Class#getMethod(String,Object[]) 74 * @see java.lang.Class#getDeclaredMethod(String,Object[]) 75 * @see java.lang.Class#getMethods() 76 * @see java.lang.Class#getDeclaredMethods() 77 * @since 1.1 78 * @status updated to 1.4 79 */ 80 public final class Method 81 extends AccessibleObject implements Member 82 { 83 //Class declaringClass; 84 //String name; 85 int slot; 86 87 byte[] vmData; Method(byte[] vmData)88 private Method(byte[] vmData) 89 { 90 this.vmData = vmData; 91 } 92 93 private String name; 94 private Class declaringClass; 95 private Class returnType; 96 private Class[] parameterTypes; 97 private Class[] exceptionTypes; 98 99 100 101 /** 102 * This class is uninstantiable. 103 */ Method(Class declaringClass, String name, int slot)104 private Method(Class declaringClass, String name, int slot) 105 { 106 this.declaringClass = declaringClass; 107 this.name = name; 108 this.slot = slot; 109 } 110 111 /** 112 * Gets the class that declared this method, or the class where this method 113 * is a non-inherited member. 114 * @return the class that declared this member 115 */ getDeclaringClass()116 public Class getDeclaringClass() 117 { 118 if (declaringClass == null) 119 { 120 declaringClass = nativeGetDeclaringClass(vmData); 121 } 122 return declaringClass; 123 } nativeGetDeclaringClass(byte[] vmData)124 private static native Class nativeGetDeclaringClass(byte[] vmData); 125 126 127 /** 128 * Gets the name of this method. 129 * @return the name of this method 130 */ 131 /* 132 public String getName() 133 { 134 return name; 135 } 136 */ getName()137 public String getName() 138 { 139 if (name == null) 140 { 141 name = nativeGetName(vmData); 142 } 143 return name; 144 } nativeGetName(byte[] vmData)145 public static native String nativeGetName(byte[] vmData); 146 147 148 /** 149 * Gets the modifiers this method uses. Use the <code>Modifier</code> 150 * class to interpret the values. A method can only have a subset of the 151 * following modifiers: public, private, protected, abstract, static, 152 * final, synchronized, native, and strictfp. 153 * 154 * @return an integer representing the modifiers to this Member 155 * @see Modifier 156 */ 157 //public native int getModifiers(); getModifiers()158 public int getModifiers() 159 { 160 /* DB: Modified prototype of native method. */ 161 return nativeGetModifiers(vmData); 162 } nativeGetModifiers(byte[] vmData)163 private static native int nativeGetModifiers(byte[] vmData); 164 165 /** 166 * Gets the return type of this method. 167 * @return the type of this method 168 */ 169 //public native Class getReturnType(); getReturnType()170 public Class getReturnType() 171 { 172 173 if (returnType == null) 174 { 175 returnType = 176 ReflectUtil.getReturnType(nativeGetDescriptor(vmData), 177 getDeclaringClass().getClassLoader()); 178 } 179 return returnType; 180 } 181 182 /** 183 * Get the parameter list for this method, in declaration order. If the 184 * method takes no parameters, returns a 0-length array (not null). 185 * 186 * @return a list of the types of the method's parameters 187 */ 188 //public native Class[] getParameterTypes(); getParameterTypes()189 public Class[] getParameterTypes() 190 { 191 if (parameterTypes == null) 192 { 193 parameterTypes = 194 ReflectUtil.getParameterTypes(nativeGetDescriptor(vmData), 195 getDeclaringClass().getClassLoader()); 196 } 197 return parameterTypes; 198 } 199 200 201 /** 202 * Get the exception types this method says it throws, in no particular 203 * order. If the method has no throws clause, returns a 0-length array 204 * (not null). 205 * 206 * @return a list of the types in the method's throws clause 207 */ 208 ///public native Class[] getExceptionTypes(); getExceptionTypes()209 public Class[] getExceptionTypes() 210 { 211 if (exceptionTypes == null) 212 { 213 exceptionTypes = nativeGetExceptionTypes(vmData); 214 } 215 return exceptionTypes; 216 } nativeGetExceptionTypes(byte[] vmData)217 public static native Class[] nativeGetExceptionTypes(byte[] vmData); 218 219 220 /** 221 * Compare two objects to see if they are semantically equivalent. 222 * Two Methods are semantically equivalent if they have the same declaring 223 * class, name, parameter list, and return type. 224 * 225 * @param o the object to compare to 226 * @return <code>true</code> if they are equal; <code>false</code> if not 227 */ equals(Object o)228 public boolean equals(Object o) 229 { 230 // Implementation note: 231 // The following is a correct but possibly slow implementation. 232 // 233 // This class has a private field 'slot' that could be used by 234 // the VM implementation to "link" a particular method to a Class. 235 // In that case equals could be simply implemented as: 236 // 237 // if (o instanceof Method) 238 // { 239 // Method m = (Method)o; 240 // return m.declaringClass == this.declaringClass 241 // && m.slot == this.slot; 242 // } 243 // return false; 244 // 245 // If a VM uses the Method class as their native/internal representation 246 // then just using the following would be optimal: 247 // 248 // return this == o; 249 // 250 if (!(o instanceof Method)) 251 return false; 252 Method that = (Method)o; 253 if (this.getDeclaringClass() != that.getDeclaringClass()) 254 return false; 255 if (!this.getName().equals(that.getName())) 256 return false; 257 if (this.getReturnType() != that.getReturnType()) 258 return false; 259 if (!Arrays.equals(this.getParameterTypes(), that.getParameterTypes())) 260 return false; 261 return true; 262 } 263 264 /** 265 * Get the hash code for the Method. The Method hash code is the hash code 266 * of its name XOR'd with the hash code of its class name. 267 * 268 * @return the hash code for the object 269 */ hashCode()270 public int hashCode() 271 { 272 return getDeclaringClass().getName().hashCode() ^ getName().hashCode(); 273 } 274 275 /** 276 * Get a String representation of the Method. A Method's String 277 * representation is "<modifiers> <returntype> 278 * <methodname>(<paramtypes>) throws <exceptions>", where 279 * everything after ')' is omitted if there are no exceptions.<br> Example: 280 * <code>public static int run(java.lang.Runnable,int)</code> 281 * 282 * @return the String representation of the Method 283 */ toString()284 public String toString() 285 { 286 // 128 is a reasonable buffer initial size for constructor 287 StringBuffer sb = new StringBuffer(128); 288 Modifier.toString(getModifiers(), sb).append(' '); 289 sb.append(getUserTypeName(getReturnType().getName())).append(' '); 290 sb.append(getDeclaringClass().getName()).append('.'); 291 sb.append(getName()).append('('); 292 Class[] c = getParameterTypes(); 293 if (c.length > 0) 294 { 295 sb.append(getUserTypeName(c[0].getName())); 296 for (int i = 1; i < c.length; i++) 297 sb.append(',').append(getUserTypeName(c[i].getName())); 298 } 299 sb.append(')'); 300 c = getExceptionTypes(); 301 if (c.length > 0) 302 { 303 sb.append(" throws ").append(c[0].getName()); 304 for (int i = 1; i < c.length; i++) 305 sb.append(',').append(c[i].getName()); 306 } 307 return sb.toString(); 308 } 309 getUserTypeName(String typeSpec)310 private static String getUserTypeName(String typeSpec) 311 { 312 int pos = 0; 313 String typeName = ""; 314 String arrayPart = ""; 315 316 while (typeSpec.charAt(pos) == '[') 317 { 318 arrayPart += "[]"; 319 ++pos; 320 } 321 322 switch (typeSpec.charAt(pos)) 323 { 324 case 'Z': 325 typeName = "boolean"; 326 break; 327 case 'B': 328 typeName = "byte"; 329 break; 330 case 'C': 331 typeName = "char"; 332 break; 333 case 'D': 334 typeName = "double"; 335 break; 336 case 'F': 337 typeName = "float"; 338 break; 339 case 'I': 340 typeName = "int"; 341 break; 342 case 'J': 343 typeName = "long"; 344 break; 345 case 'S': 346 typeName = "short"; 347 break; 348 case 'L': 349 typeName = typeSpec.substring(pos + 1, typeSpec.length() - 1); 350 break; 351 default: 352 typeName = typeSpec; 353 break; 354 } 355 356 return typeName + arrayPart; 357 } 358 359 /** 360 * Invoke the method. Arguments are automatically unwrapped and widened, 361 * and the result is automatically wrapped, if needed.<p> 362 * 363 * If the method is static, <code>o</code> will be ignored. Otherwise, 364 * the method uses dynamic lookup as described in JLS 15.12.4.4. You cannot 365 * mimic the behavior of nonvirtual lookup (as in super.foo()). This means 366 * you will get a <code>NullPointerException</code> if <code>o</code> is 367 * null, and an <code>IllegalArgumentException</code> if it is incompatible 368 * with the declaring class of the method. If the method takes 0 arguments, 369 * you may use null or a 0-length array for <code>args</code>.<p> 370 * 371 * Next, if this Method enforces access control, your runtime context is 372 * evaluated, and you may have an <code>IllegalAccessException</code> if 373 * you could not acces this method in similar compiled code. If the method 374 * is static, and its class is uninitialized, you trigger class 375 * initialization, which may end in a 376 * <code>ExceptionInInitializerError</code>.<p> 377 * 378 * Finally, the method is invoked. If it completes normally, the return value 379 * will be null for a void method, a wrapped object for a primitive return 380 * method, or the actual return of an Object method. If it completes 381 * abruptly, the exception is wrapped in an 382 * <code>InvocationTargetException</code>. 383 * 384 * @param o the object to invoke the method on 385 * @param args the arguments to the method 386 * @return the return value of the method, wrapped in the appropriate 387 * wrapper if it is primitive 388 * @throws IllegalAccessException if the method could not normally be called 389 * by the Java code (i.e. it is not public) 390 * @throws IllegalArgumentException if the number of arguments is incorrect; 391 * if the arguments types are wrong even with a widening conversion; 392 * or if <code>o</code> is not an instance of the class or interface 393 * declaring this method 394 * @throws InvocationTargetException if the method throws an exception 395 * @throws NullPointerException if <code>o</code> is null and this field 396 * requires an instance 397 * @throws ExceptionInInitializerError if accessing a static method triggered 398 * class initialization, which then failed 399 */ 400 /* 401 public Object invoke(Object o, Object[] args) 402 throws IllegalAccessException, InvocationTargetException 403 { 404 return invokeNative(o, args, declaringClass, slot); 405 } 406 */ invoke(Object o, Object[] args)407 public Object invoke(Object o, Object[] args) 408 throws IllegalAccessException, InvocationTargetException 409 { 410 if (args == null) { 411 args = new Object[0]; 412 } else { 413 /* 414 * If args actual type is not Object[], allocate an Object[] 415 * and copy the args to that array. 416 * 417 * If this is not done, an ArrayStoreException may result 418 * in the following code. 419 * 420 * ex: Boolean[] passed. 421 * 422 * boolean[] wrapper = new boolean[1]; 423 * wrapper[0] = ((Boolean) args[i]).booleanValue(); 424 * --> args[i] = wrapper; 425 * 426 * ArrayStoreException since boolean[] is not assignable to 427 * type Boolean. 428 * 429 */ 430 if (args.getClass() != Object[].class) { 431 Object[] newArgs; 432 newArgs = new Object[args.length]; 433 System.arraycopy(args, 0, newArgs, 0, args.length); 434 // ugly, changing param value 435 args = newArgs; 436 } 437 438 } 439 440 char[] paramTypes = getParamTypes(); 441 Class[] parameterTypes = getParameterTypes(); 442 int count = paramTypes.length; 443 444 if (count != args.length) 445 { 446 throw new IllegalArgumentException("incorrect number of arguments"); 447 } 448 449 try { 450 for (int i = 0; i < count; i++) 451 { 452 switch (paramTypes[i]) 453 { 454 case 'Z': 455 { 456 /* using a wrapper array is simpler for the VM. */ 457 boolean[] wrapper = new boolean[1]; 458 wrapper[0] = ((Boolean) args[i]).booleanValue(); 459 args[i] = wrapper; 460 } 461 break; 462 463 case 'B': 464 { 465 byte[] wrapper = new byte[1]; 466 wrapper[0] = ((Byte) args[i]).byteValue(); 467 args[i] = wrapper; 468 } 469 break; 470 471 case 'S': 472 { 473 short[] wrapper = new short[1]; 474 wrapper[0] = ((Short) args[i]).shortValue(); 475 args[i] = wrapper; 476 } 477 break; 478 479 case 'C': 480 { 481 char[] wrapper = new char[1]; 482 wrapper[0] = ((Character) args[i]).charValue(); 483 args[i] = wrapper; 484 } 485 break; 486 487 case 'I': 488 { 489 int[] wrapper = new int[1]; 490 wrapper[0] = ((Integer) args[i]).intValue(); 491 args[i] = wrapper; 492 } 493 break; 494 495 case 'J': 496 { 497 long[] wrapper = new long[1]; 498 wrapper[0] = ((Long) args[i]).longValue(); 499 args[i] = wrapper; 500 } 501 break; 502 503 case 'F': 504 { 505 float[] wrapper = new float[1]; 506 wrapper[0] = ((Float) args[i]).floatValue(); 507 args[i] = wrapper; 508 } 509 break; 510 511 case 'D': 512 { 513 double[] wrapper = new double[1]; 514 wrapper[0] = ((Double) args[i]).doubleValue(); 515 args[i] = wrapper; 516 } 517 break; 518 519 case 'L': 520 { 521 // check type 522 if (args[i] != null) { 523 if (!(parameterTypes[i].isAssignableFrom(args[i].getClass()))) { 524 throw new IllegalArgumentException("argument of incorrect type"); 525 } 526 } 527 } 528 break; 529 530 default: 531 throw new InternalError(); 532 } 533 } 534 } catch (ClassCastException e) { 535 throw new IllegalArgumentException("argument of incorrect type"); 536 } 537 538 Object result; 539 540 switch (resultType) 541 { 542 case 'Z': 543 { 544 boolean[] wrapper = new boolean[1]; 545 result = wrapper; 546 } 547 break; 548 549 case 'B': 550 { 551 byte[] wrapper = new byte[1]; 552 result = wrapper; 553 } 554 break; 555 556 case 'S': 557 { 558 short[] wrapper = new short[1]; 559 result = wrapper; 560 } 561 break; 562 563 case 'C': 564 { 565 char[] wrapper = new char[1]; 566 result = wrapper; 567 } 568 break; 569 570 case 'I': 571 { 572 int[] wrapper = new int[1]; 573 result = wrapper; 574 } 575 break; 576 577 case 'J': 578 { 579 long[] wrapper = new long[1]; 580 result = wrapper; 581 } 582 break; 583 584 case 'F': 585 { 586 float[] wrapper = new float[1]; 587 result = wrapper; 588 } 589 break; 590 591 case 'D': 592 { 593 double[] wrapper = new double[1]; 594 result = wrapper; 595 } 596 break; 597 598 case 'L': 599 { 600 Object[] wrapper = new Object[1]; 601 result = wrapper; 602 } 603 break; 604 605 case 'V': 606 { 607 result = null; 608 } 609 break; 610 611 default: 612 throw new InternalError(); 613 } 614 615 // invoke the thing. 616 invokeNative(vmData, paramTypes, resultType, o, args, result); 617 618 // unwrap and rewrap the result appropriately 619 switch (resultType) 620 { 621 case 'Z': 622 { 623 boolean[] wrapper = (boolean[]) result; 624 return new Boolean(wrapper[0]); 625 } 626 627 case 'B': 628 { 629 byte[] wrapper = (byte[]) result; 630 return new Byte(wrapper[0]); 631 } 632 633 case 'S': 634 { 635 short[] wrapper = (short[]) result; 636 return new Short(wrapper[0]); 637 } 638 639 case 'C': 640 { 641 char[] wrapper = (char[]) result; 642 return new Character(wrapper[0]); 643 } 644 645 case 'I': 646 { 647 int[] wrapper = (int[]) result; 648 return new Integer(wrapper[0]); 649 } 650 651 case 'J': 652 { 653 long[] wrapper = (long[]) result; 654 return new Long(wrapper[0]); 655 } 656 657 case 'F': 658 { 659 float[] wrapper = (float[]) result; 660 return new Float(wrapper[0]); 661 } 662 663 case 'D': 664 { 665 double[] wrapper = (double[]) result; 666 return new Double(wrapper[0]); 667 } 668 669 case 'L': 670 { 671 Object[] wrapper = (Object[]) result; 672 return wrapper[0]; 673 } 674 675 case 'V': 676 { 677 return null; 678 } 679 680 default: 681 throw new InternalError(); 682 } 683 } 684 invokeNative(byte[] vmData, char[] paramTypes, char resultType, Object o, Object[] args, Object result)685 private static native void invokeNative(byte[] vmData, char[] paramTypes, char resultType, Object o, Object[] args, Object result) 686 throws IllegalAccessException, InvocationTargetException; 687 nativeGetDescriptor(byte[] vmData)688 private static native String nativeGetDescriptor(byte[] vmData); 689 690 private char[] paramTypes; 691 private char resultType; 692 getParamTypes()693 private char[] getParamTypes() 694 { 695 if(paramTypes == null) 696 { 697 char[] array = nativeGetDescriptor(vmData).toCharArray(); 698 int count = 0; 699 int i = 0; 700 char c; 701 702 while ((c = array[++i]) != ')') 703 { 704 switch (c) 705 { 706 case 'Z': 707 case 'B': 708 case 'S': 709 case 'C': 710 case 'I': 711 case 'J': 712 case 'F': 713 case 'D': 714 { 715 array[count++] = c; 716 } 717 break; 718 719 case 'L': 720 { 721 array[count++] = 'L'; 722 723 /* skip to next ';' */ 724 while (array[++i] != ';') 725 ; 726 } 727 break; 728 729 case '[': 730 { 731 array[count++] = 'L'; 732 733 /* skip all '[' */ 734 while (array[++i] == '[') 735 ; 736 737 if (array[i] == 'L') 738 { 739 /* skip to next ';' */ 740 while (array[++i] != ';') 741 ; 742 } 743 } 744 break; 745 746 default: 747 throw new InternalError(); 748 } 749 } 750 751 switch (c = array[++i]) 752 { 753 case 'Z': 754 case 'B': 755 case 'S': 756 case 'C': 757 case 'I': 758 case 'J': 759 case 'F': 760 case 'D': 761 case 'L': 762 case 'V': 763 { 764 resultType = c; 765 } 766 break; 767 768 case '[': 769 { 770 resultType = 'L'; 771 } 772 break; 773 774 default: 775 throw new InternalError(); 776 } 777 778 char[] types = new char[count]; 779 System.arraycopy(array, 0, types, 0, count); 780 paramTypes = types; 781 } 782 783 return paramTypes; 784 } 785 786 787 } 788 789