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 "&lt;modifiers&gt; &lt;returntype&gt;
278    * &lt;methodname&gt;(&lt;paramtypes&gt;) throws &lt;exceptions&gt;", 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