OSDN Git Service

2007-04-16 Andrew Haley <aph@redhat.com>
[pf3gnuchains/gcc-fork.git] / libjava / java / lang / reflect / natMethod.cc
index 26c270e..4593da7 100644 (file)
@@ -1,6 +1,6 @@
 // natMethod.cc - Native code for Method class.
 
-/* Copyright (C) 1998, 1999, 2000, 2001  Free Software Foundation
+/* Copyright (C) 1998, 1999, 2000, 2001 , 2002, 2003, 2004, 2005, 2006 Free Software Foundation
 
    This file is part of libgcj.
 
@@ -10,13 +10,10 @@ details.  */
 
 #include <config.h>
 
-#if HAVE_ALLOCA_H
-#include <alloca.h>
-#endif
-
 #include <gcj/cni.h>
 #include <jvm.h>
 #include <jni.h>
+#include <java-stack.h>
 
 #include <java/lang/reflect/Method.h>
 #include <java/lang/reflect/Constructor.h>
@@ -32,11 +29,16 @@ details.  */
 #include <java/lang/Long.h>
 #include <java/lang/Float.h>
 #include <java/lang/Double.h>
+#include <java/lang/IllegalAccessException.h>
 #include <java/lang/IllegalArgumentException.h>
+#include <java/lang/IncompatibleClassChangeError.h>
 #include <java/lang/NullPointerException.h>
+#include <java/lang/ArrayIndexOutOfBoundsException.h>
+#include <java/lang/VirtualMachineError.h>
 #include <java/lang/Class.h>
 #include <gcj/method.h>
 #include <gnu/gcj/RawData.h>
+#include <java/lang/NoClassDefFoundError.h>
 
 #include <stdlib.h>
 
@@ -46,16 +48,10 @@ details.  */
 #include <java/lang/UnsupportedOperationException.h>
 #endif
 
-// FIXME: remove these.
-#define BooleanClass java::lang::Boolean::class$
-#define VoidClass java::lang::Void::class$
-#define ByteClass java::lang::Byte::class$
-#define ShortClass java::lang::Short::class$
-#define CharacterClass java::lang::Character::class$
-#define IntegerClass java::lang::Integer::class$
-#define LongClass java::lang::Long::class$
-#define FloatClass java::lang::Float::class$
-#define DoubleClass java::lang::Double::class$
+typedef JArray< ::java::lang::annotation::Annotation * > * anno_a_t;
+typedef JArray< JArray< ::java::lang::annotation::Annotation * > *> * anno_aa_t;
+
+\f
 
 struct cpair
 {
@@ -68,16 +64,16 @@ struct cpair
 static cpair primitives[] =
 {
 #define BOOLEAN 0
-  { JvPrimClass (boolean), &BooleanClass },
-  { JvPrimClass (byte), &ByteClass },
+  { JvPrimClass (boolean), &java::lang::Boolean::class$ },
+  { JvPrimClass (byte), &java::lang::Byte::class$ },
 #define SHORT 2
-  { JvPrimClass (short), &ShortClass },
+  { JvPrimClass (short), &java::lang::Short::class$ },
 #define CHAR 3
-  { JvPrimClass (char), &CharacterClass },
-  { JvPrimClass (int), &IntegerClass },
-  { JvPrimClass (long), &LongClass },
-  { JvPrimClass (float), &FloatClass },
-  { JvPrimClass (double), &DoubleClass },
+  { JvPrimClass (char), &java::lang::Character::class$ },
+  { JvPrimClass (int), &java::lang::Integer::class$ },
+  { JvPrimClass (long), &java::lang::Long::class$ },
+  { JvPrimClass (float), &java::lang::Float::class$ },
+  { JvPrimClass (double), &java::lang::Double::class$ },
   { NULL, NULL }
 };
 
@@ -154,33 +150,89 @@ get_ffi_type (jclass klass)
 jobject
 java::lang::reflect::Method::invoke (jobject obj, jobjectArray args)
 {
+  using namespace java::lang::reflect;
+  jclass iface = NULL;
+  
   if (parameter_types == NULL)
     getType ();
-
+    
   jmethodID meth = _Jv_FromReflectedMethod (this);
-  if (! java::lang::reflect::Modifier::isStatic(meth->accflags))
+
+  if (Modifier::isStatic(meth->accflags))
+    {
+      // We have to initialize a static class.  It is safe to do this
+      // here and not in _Jv_CallAnyMethodA because JNI initializes a
+      // class whenever a method lookup is done.
+      _Jv_InitClass (declaringClass);
+    }
+  else
+    {
+      jclass objClass = JV_CLASS (obj);
+      if (! _Jv_IsAssignableFrom (objClass, declaringClass))
+        throw new java::lang::IllegalArgumentException;
+    }
+
+  // Check accessibility, if required.
+  if (! this->isAccessible())
     {
-      jclass k = obj ? obj->getClass() : NULL;
-      if (! obj)
-       throw new java::lang::NullPointerException;
-      if (! declaringClass->isAssignableFrom(k))
-       throw new java::lang::IllegalArgumentException;
-      // FIXME: access checks.
-
-      // Find the possibly overloaded method based on the runtime type
-      // of the object.
-      meth = _Jv_LookupDeclaredMethod (k, meth->name, meth->signature);
+      if (! (Modifier::isPublic (meth->accflags)))
+       {
+         Class *caller = _Jv_StackTrace::GetCallingClass (&Method::class$);
+         if (! _Jv_CheckAccess(caller, declaringClass, meth->accflags))
+           throw new IllegalAccessException;
+       }
+      else
+       // Method is public, check to see if class is accessible.
+       {
+         jint flags = (declaringClass->accflags
+                       & (Modifier::PUBLIC
+                          | Modifier::PROTECTED
+                          | Modifier::PRIVATE));
+         if (flags == 0) // i.e. class is package private
+           {
+             Class *caller = _Jv_StackTrace::GetCallingClass (&Method::class$);
+             if (! _Jv_ClassNameSamePackage (caller->name,
+                                             declaringClass->name))
+               throw new IllegalAccessException;
+           }
+       }
     }
 
+  if (declaringClass->isInterface())
+    iface = declaringClass;
+
   return _Jv_CallAnyMethodA (obj, return_type, meth, false,
-                            parameter_types, args);
+                            parameter_types, args, iface);
 }
 
 jint
-java::lang::reflect::Method::getModifiers ()
+java::lang::reflect::Method::getModifiersInternal ()
 {
-  // Ignore all unknown flags.
-  return _Jv_FromReflectedMethod (this)->accflags & Modifier::ALL_FLAGS;
+  return _Jv_FromReflectedMethod (this)->accflags;
+}
+
+jstring
+java::lang::reflect::Method::getSignature()
+{
+  return declaringClass->getReflectionSignature (this);
+}
+
+jobject
+java::lang::reflect::Method::getDefaultValue()
+{
+  return declaringClass->getMethodDefaultValue(this);
+}
+
+anno_a_t
+java::lang::reflect::Method::getDeclaredAnnotationsInternal()
+{
+  return (anno_a_t) declaringClass->getDeclaredAnnotations(this, false);
+}
+
+anno_aa_t
+java::lang::reflect::Method::getParameterAnnotationsInternal()
+{
+  return (anno_aa_t) declaringClass->getDeclaredAnnotations(this, true);
 }
 
 jstring
@@ -210,13 +262,12 @@ java::lang::reflect::Method::getType ()
     }
 
   exception_types
-    = (JArray<jclass> *) JvNewObjectArray (count,
-                                          &java::lang::Class::class$,
+    = (JArray<jclass> *) JvNewObjectArray (count, &java::lang::Class::class$,
                                           NULL);
   jclass *elts = elements (exception_types);
   for (int i = 0; i < count; ++i)
-    elts[i] = _Jv_FindClassFromSignature (method->throws[i]->data,
-                                         declaringClass->getClassLoader ());
+    elts[i] = _Jv_FindClass (method->throws[i],
+                            declaringClass->getClassLoaderInternal ());
 }
 
 void
@@ -227,10 +278,12 @@ _Jv_GetTypesFromSignature (jmethodID method,
 {
 
   _Jv_Utf8Const* sig = method->signature;
-  java::lang::ClassLoader *loader = declaringClass->getClassLoader();
-  char *ptr = sig->data;
+  java::lang::ClassLoader *loader = declaringClass->getClassLoaderInternal();
+  char *ptr = sig->chars();
   int numArgs = 0;
   /* First just count the number of parameters. */
+  // FIXME: should do some validation here, e.g., that there is only
+  // one return type.
   for (; ; ptr++)
     {
       switch (*ptr)
@@ -265,47 +318,28 @@ _Jv_GetTypesFromSignature (jmethodID method,
   JArray<jclass> *args = (JArray<jclass> *)
     JvNewObjectArray (numArgs, &java::lang::Class::class$, NULL);
   jclass* argPtr = elements (args);
-  for (ptr = sig->data; *ptr != '\0'; ptr++)
+  for (ptr = sig->chars(); *ptr != '\0'; ptr++)
     {
-      int num_arrays = 0;
-      jclass type;
-      for (; *ptr == '[';  ptr++)
-       num_arrays++;
-      switch (*ptr)
+      if (*ptr == '(')
+       continue;
+      if (*ptr == ')')
        {
-       default:
-         return;
-       case ')':
          argPtr = return_type_out;
          continue;
-       case '(':
-         continue;
-       case 'V':
-       case 'B':
-       case 'C':
-       case 'D':
-       case 'F':
-       case 'S':
-       case 'I':
-       case 'J':
-       case 'Z':
-         type = _Jv_FindClassFromSignature(ptr, loader);
-         break;
-       case 'L':
-         type = _Jv_FindClassFromSignature(ptr, loader);
-         do 
-           ptr++;
-         while (*ptr != ';' && ptr[1] != '\0');
-         break;
        }
 
-      // FIXME: 2'nd argument should be "current loader"
-      while (--num_arrays >= 0)
-       type = _Jv_GetArrayClass (type, 0);
+      char *end_ptr;
+      jclass type = _Jv_FindClassFromSignature (ptr, loader, &end_ptr);
+      if (type == NULL)
+       // FIXME: This isn't ideal.
+       throw new java::lang::NoClassDefFoundError (sig->toString());
+
       // ARGPTR can be NULL if we are processing the return value of a
       // call from Constructor.
       if (argPtr)
        *argPtr++ = type;
+
+      ptr = end_ptr;
     }
   *arg_types_out = args;
 }
@@ -318,15 +352,20 @@ _Jv_GetTypesFromSignature (jmethodID method,
 // to a `jvalue' (see jni.h); for a void method this should be NULL.
 // This function returns an exception (if one was thrown), or NULL if
 // the call went ok.
-jthrowable
+void
 _Jv_CallAnyMethodA (jobject obj,
                    jclass return_type,
                    jmethodID meth,
                    jboolean is_constructor,
+                   jboolean is_virtual_call,
                    JArray<jclass> *parameter_types,
                    jvalue *args,
-                   jvalue *result)
+                   jvalue *result,
+                   jboolean is_jni_call,
+                   jclass iface)
 {
+  using namespace java::lang::reflect;
+  
 #ifdef USE_LIBFFI
   JvAssert (! is_constructor || ! obj);
   JvAssert (! is_constructor || return_type);
@@ -335,7 +374,7 @@ _Jv_CallAnyMethodA (jobject obj,
   // constructor does need a `this' argument, but it is one we create.
   jboolean needs_this = false;
   if (is_constructor
-      || ! java::lang::reflect::Modifier::isStatic(meth->accflags))
+      || ! Modifier::isStatic(meth->accflags))
     needs_this = true;
 
   int param_count = parameter_types->length;
@@ -348,65 +387,41 @@ _Jv_CallAnyMethodA (jobject obj,
     rtype = &ffi_type_void;
   else
     rtype = get_ffi_type (return_type);
-  ffi_type **argtypes = (ffi_type **) alloca (param_count
-                                             * sizeof (ffi_type *));
+  ffi_type **argtypes = (ffi_type **) __builtin_alloca (param_count
+                                                       * sizeof (ffi_type *));
 
   jclass *paramelts = elements (parameter_types);
 
-  // FIXME: at some point the compiler is going to add extra arguments
-  // to some functions.  In particular we are going to do this for
-  // handling access checks in reflection.  We must add these hidden
-  // arguments here.
-
   // Special case for the `this' argument of a constructor.  Note that
   // the JDK 1.2 docs specify that the new object must be allocated
   // before argument conversions are done.
   if (is_constructor)
-    {
-      // FIXME: must special-case String, arrays, maybe others here.
-      obj = JvAllocObject (return_type);
-    }
-
-  int i = 0;
-  int size = 0;
-  if (needs_this)
-    {
-      // The `NULL' type is `Object'.
-      argtypes[i++] = get_ffi_type (NULL);
-      size += sizeof (jobject);
-    }
-
-  for (int arg = 0; i < param_count; ++i, ++arg)
-    {
-      argtypes[i] = get_ffi_type (paramelts[arg]);
-      if (paramelts[arg]->isPrimitive())
-       size += paramelts[arg]->size();
-      else
-       size += sizeof (jobject);
-    }
+    obj = _Jv_AllocObject (return_type);
 
+  const int size_per_arg = sizeof(jvalue);
   ffi_cif cif;
-  if (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, param_count,
-                   rtype, argtypes) != FFI_OK)
-    {
-      // FIXME: throw some kind of VirtualMachineError here.
-    }
 
-  char *p = (char *) alloca (size);
-  void **values = (void **) alloca (param_count * sizeof (void *));
+  char *p = (char *) __builtin_alloca (param_count * size_per_arg);
+               // Overallocate to get correct alignment.
+  void **values = (void **)
+                       __builtin_alloca (param_count * sizeof (void *));
 
-  i = 0;
+  int i = 0;
   if (needs_this)
     {
+      // The `NULL' type is `Object'.
+      argtypes[i] = get_ffi_type (NULL);
       values[i] = p;
       memcpy (p, &obj, sizeof (jobject));
-      p += sizeof (jobject);
+      p += size_per_arg;
       ++i;
     }
 
   for (int arg = 0; i < param_count; ++i, ++arg)
     {
       int tsize;
+
+      argtypes[i] = get_ffi_type (paramelts[arg]);
       if (paramelts[arg]->isPrimitive())
        tsize = paramelts[arg]->size();
       else
@@ -415,38 +430,163 @@ _Jv_CallAnyMethodA (jobject obj,
       // Copy appropriate bits from the jvalue into the ffi array.
       // FIXME: we could do this copying all in one loop, above, by
       // over-allocating a bit.
+      // How do we do this without breaking big-endian platforms?
       values[i] = p;
       memcpy (p, &args[arg], tsize);
-      p += tsize;
+      p += size_per_arg;
     }
 
-  // FIXME: initialize class here.
+  if (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, param_count,
+                   rtype, argtypes) != FFI_OK)
+    throw new java::lang::VirtualMachineError(JvNewStringLatin1("internal error: ffi_prep_cif failed"));
 
   using namespace java::lang;
   using namespace java::lang::reflect;
 
-  Throwable *ex = NULL;
+  union
+  {
+    ffi_arg i;
+    jobject o;
+    jlong l;
+    jfloat f;
+    jdouble d;
+  } ffi_result;
+
+  switch (rtype->type)
+    {
+    case FFI_TYPE_VOID:
+      break;
+    case FFI_TYPE_SINT8:
+      result->b = 0;
+      break;
+    case FFI_TYPE_SINT16:
+      result->s = 0;
+      break;
+    case FFI_TYPE_UINT16:
+      result->c = 0;
+      break;
+    case FFI_TYPE_SINT32:
+      result->i = 0;
+      break;
+    case FFI_TYPE_SINT64:
+      result->j = 0;
+      break;
+    case FFI_TYPE_FLOAT:
+      result->f = 0;
+      break;
+    case FFI_TYPE_DOUBLE:
+      result->d = 0;
+      break;
+    case FFI_TYPE_POINTER:
+      result->l = 0;
+      break;
+    default:
+      JvFail ("Unknown ffi_call return type");
+      break;
+    }
+
+  void *ncode;
+
+  // FIXME: If a vtable index is -1 at this point it is invalid, so we
+  // have to use the ncode.  
+  //
+  // This can happen because methods in final classes don't have
+  // vtable entries, but _Jv_isVirtualMethod() doesn't know that.  We
+  // could solve this problem by allocating a vtable index for methods
+  // in final classes.
+  if (is_virtual_call 
+      && ! Modifier::isFinal (meth->accflags)
+      && (_Jv_ushort)-1 != meth->index)
+    {
+      _Jv_VTable *vtable = *(_Jv_VTable **) obj;
+      if (iface == NULL)
+       {
+         if (is_jni_call && Modifier::isAbstract (meth->accflags))
+           {
+             // With JNI we don't know if this is an interface call
+             // or a call to an abstract method.  Look up the method
+             // by name, the slow way.
+             _Jv_Method *concrete_meth
+               = _Jv_LookupDeclaredMethod (vtable->clas,
+                                           meth->name,
+                                           meth->signature,
+                                           NULL);
+             if (concrete_meth == NULL
+                 || concrete_meth->ncode == NULL
+                 || Modifier::isAbstract(concrete_meth->accflags))
+               throw new java::lang::IncompatibleClassChangeError
+                 (_Jv_GetMethodString (vtable->clas, meth));
+             ncode = concrete_meth->ncode;
+           }
+         else
+           ncode = vtable->get_method (meth->index);
+       }
+      else
+       ncode = _Jv_LookupInterfaceMethodIdx (vtable->clas, iface,
+                                             meth->index);
+    }
+  else
+    {
+      ncode = meth->ncode;
+    }
 
   try
     {
-      ffi_call (&cif, (void (*)()) meth->ncode, result, values);
+      ffi_call (&cif, (void (*)()) ncode, &ffi_result, values);
     }
-  catch (Throwable *ex2)
+  catch (Throwable *ex)
     {
-      // FIXME: this is wrong for JNI.  But if we just return the
-      // exception, then the non-JNI cases won't be able to
-      // distinguish it from exceptions we might generate ourselves.
-      // Sigh.
-      ex = new InvocationTargetException (ex2);
+      // For JNI we just throw the real error.  For reflection, we
+      // wrap the underlying method's exception in an
+      // InvocationTargetException.
+      if (! is_jni_call)
+       ex = new InvocationTargetException (ex);
+      throw ex;
     }
 
+  // Since ffi_call returns integer values promoted to a word, use
+  // a narrowing conversion for jbyte, jchar, etc. results.
+  // Note that boolean is handled either by the FFI_TYPE_SINT8 or
+  // FFI_TYPE_SINT32 case.
   if (is_constructor)
     result->l = obj;
-
-  return ex;
+  else
+    {
+      switch (rtype->type)
+       {
+       case FFI_TYPE_VOID:
+         break;
+       case FFI_TYPE_SINT8:
+         result->b = (jbyte)ffi_result.i;
+         break;
+       case FFI_TYPE_SINT16:
+         result->s = (jshort)ffi_result.i;
+         break;
+       case FFI_TYPE_UINT16:
+         result->c = (jchar)ffi_result.i;
+         break;
+       case FFI_TYPE_SINT32:
+         result->i = (jint)ffi_result.i;
+         break;
+       case FFI_TYPE_SINT64:
+         result->j = (jlong)ffi_result.l;
+         break;
+       case FFI_TYPE_FLOAT:
+         result->f = (jfloat)ffi_result.f;
+         break;
+       case FFI_TYPE_DOUBLE:
+         result->d = (jdouble)ffi_result.d;
+         break;
+       case FFI_TYPE_POINTER:
+         result->l = (jobject)ffi_result.o;
+         break;
+       default:
+         JvFail ("Unknown ffi_call return type");
+         break;
+       }
+    }
 #else
-  throw new java::lang::UnsupportedOperationException;
-  return 0;
+  throw new java::lang::UnsupportedOperationException(JvNewStringLatin1("reflection not available in this build"));
 #endif // USE_LIBFFI
 }
 
@@ -458,10 +598,9 @@ _Jv_CallAnyMethodA (jobject obj,
                    jmethodID meth,
                    jboolean is_constructor,
                    JArray<jclass> *parameter_types,
-                   jobjectArray args)
+                   jobjectArray args,
+                   jclass iface)
 {
-  // FIXME: access checks.
-
   if (parameter_types->length == 0 && args == NULL)
     {
       // The JDK accepts this, so we do too.
@@ -525,16 +664,10 @@ _Jv_CallAnyMethodA (jobject obj,
     }
 
   jvalue ret_value;
-  java::lang::Throwable *ex = _Jv_CallAnyMethodA (obj,
-                                                 return_type,
-                                                 meth,
-                                                 is_constructor,
-                                                 parameter_types,
-                                                 argvals,
-                                                 &ret_value);
-
-  if (ex)
-    throw ex;
+  _Jv_CallAnyMethodA (obj, return_type, meth, is_constructor,
+                     _Jv_isVirtualMethod (meth),
+                     parameter_types, argvals, &ret_value,
+                     false, iface);
 
   jobject r;
 #define VAL(Wrapper, Field)  (new Wrapper (ret_value.Field))