OSDN Git Service

2004-12-10 Andrew Haley <aph@redhat.com>
[pf3gnuchains/gcc-fork.git] / libjava / java / lang / reflect / natMethod.cc
index d4cbb72..b4b3a7a 100644 (file)
@@ -1,6 +1,6 @@
 // natMethod.cc - Native code for Method class.
 
-/* Copyright (C) 1998, 1999, 2000, 2001 , 2002 Free Software Foundation
+/* Copyright (C) 1998, 1999, 2000, 2001 , 2002, 2003, 2004 Free Software Foundation
 
    This file is part of libgcj.
 
@@ -28,8 +28,12 @@ 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>
@@ -42,17 +46,6 @@ 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$
-
 struct cpair
 {
   jclass prim;
@@ -64,16 +57,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 }
 };
 
@@ -150,26 +143,54 @@ 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 (declaringClass, objClass))
+        throw new java::lang::IllegalArgumentException;
+    }
+
+  // Check accessibility, if required.
+  if (! (Modifier::isPublic (meth->accflags) || 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);
+      gnu::gcj::runtime::StackTrace *t 
+       = new gnu::gcj::runtime::StackTrace(4);
+      Class *caller = NULL;
+      try
+       {
+         for (int i = 1; !caller; i++)
+           {
+             caller = t->classAt (i);
+           }
+       }
+      catch (::java::lang::ArrayIndexOutOfBoundsException *e)
+       {
+       }
+
+      if (! _Jv_CheckAccess(caller, declaringClass, meth->accflags))
+       throw new IllegalAccessException;
     }
 
+  if (declaringClass->isInterface())
+    iface = declaringClass;
+  
   return _Jv_CallAnyMethodA (obj, return_type, meth, false,
-                            parameter_types, args);
+                            parameter_types, args, iface);
 }
 
 jint
@@ -206,13 +227,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
@@ -223,8 +243,8 @@ _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. */
   for (; ; ptr++)
@@ -261,7 +281,7 @@ _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;
@@ -295,9 +315,8 @@ _Jv_GetTypesFromSignature (jmethodID method,
          break;
        }
 
-      // FIXME: 2'nd argument should be "current loader"
       while (--num_arrays >= 0)
-       type = _Jv_GetArrayClass (type, 0);
+       type = _Jv_GetArrayClass (type, loader);
       // ARGPTR can be NULL if we are processing the return value of a
       // call from Constructor.
       if (argPtr)
@@ -314,15 +333,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);
@@ -331,7 +355,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;
@@ -349,60 +373,36 @@ _Jv_CallAnyMethodA (jobject obj,
 
   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 *) __builtin_alloca (size);
-  void **values = (void **) __builtin_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
@@ -411,18 +411,19 @@ _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;
@@ -432,17 +433,96 @@ _Jv_CallAnyMethodA (jobject obj,
     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->name));
+             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, &ffi_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
@@ -486,11 +566,8 @@ _Jv_CallAnyMethodA (jobject obj,
          break;
        }
     }
-
-  return ex;
 #else
-  throw new java::lang::UnsupportedOperationException;
-  return 0;
+  throw new java::lang::UnsupportedOperationException(JvNewStringLatin1("reflection not available in this build"));
 #endif // USE_LIBFFI
 }
 
@@ -502,10 +579,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.
@@ -569,16 +645,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))