OSDN Git Service

* jni.cc (nathash, nathash_count, nathash_size): New globals.
[pf3gnuchains/gcc-fork.git] / libjava / jni.cc
index 34f2995..be45cb2 100644 (file)
@@ -37,23 +37,16 @@ details.  */
 #include <java/lang/reflect/Method.h>
 #include <java/lang/reflect/Modifier.h>
 #include <java/lang/OutOfMemoryError.h>
-#include <java/util/Hashtable.h>
+#include <java/util/IdentityHashMap.h>
 #include <java/lang/Integer.h>
 #include <java/lang/ThreadGroup.h>
-#include <gnu/gcj/jni/NativeThread.h>
+#include <java/lang/Thread.h>
 
 #include <gcj/method.h>
 #include <gcj/field.h>
 
 #include <java-interp.h>
-
-// FIXME: remove these defines.
-#define ClassClass java::lang::Class::class$
-#define ObjectClass java::lang::Object::class$
-#define ThrowableClass java::lang::Throwable::class$
-#define MethodClass java::lang::reflect::Method::class$
-#define ThreadGroupClass java::lang::ThreadGroup::class$
-#define NativeThreadClass gnu::gcj::jni::NativeThread::class$
+#include <java-threads.h>
 
 // This enum is used to select different template instantiations in
 // the invocation code.
@@ -97,8 +90,10 @@ struct _Jv_JNI_LocalFrame
   jobject vec[0];
 };
 
-// This holds a reference count for all local and global references.
-static java::util::Hashtable *ref_table;
+// This holds a reference count for all local references.
+static java::util::IdentityHashMap *local_ref_table;
+// This holds a reference count for all global references.
+static java::util::IdentityHashMap *global_ref_table;
 
 // The only VM.
 static JavaVM *the_vm;
@@ -153,8 +148,9 @@ jvmpiDisableEvent (jint event_type, void *)
 void
 _Jv_JNI_Init (void)
 {
-  ref_table = new java::util::Hashtable;
-  
+  local_ref_table = new java::util::IdentityHashMap;
+  global_ref_table = new java::util::IdentityHashMap;
+
 #ifdef ENABLE_JVMPI
   _Jv_JVMPI_Interface.version = 1;
   _Jv_JVMPI_Interface.EnableEvent = &jvmpiEnableEvent;
@@ -167,7 +163,7 @@ _Jv_JNI_Init (void)
 
 // Tell the GC that a certain pointer is live.
 static void
-mark_for_gc (jobject obj)
+mark_for_gc (jobject obj, java::util::IdentityHashMap *ref_table)
 {
   JvSynchronize sync (ref_table);
 
@@ -180,7 +176,7 @@ mark_for_gc (jobject obj)
 
 // Unmark a pointer.
 static void
-unmark_for_gc (jobject obj)
+unmark_for_gc (jobject obj, java::util::IdentityHashMap *ref_table)
 {
   JvSynchronize sync (ref_table);
 
@@ -188,6 +184,7 @@ unmark_for_gc (jobject obj)
   Integer *refcount = (Integer *) ref_table->get (obj);
   JvAssert (refcount);
   jint val = refcount->intValue () - 1;
+  JvAssert (val >= 0);
   if (val == 0)
     ref_table->remove (obj);
   else
@@ -200,14 +197,14 @@ unmark_for_gc (jobject obj)
 static jobject
 _Jv_JNI_NewGlobalRef (JNIEnv *, jobject obj)
 {
-  mark_for_gc (obj);
+  mark_for_gc (obj, global_ref_table);
   return obj;
 }
 
 static void
 _Jv_JNI_DeleteGlobalRef (JNIEnv *, jobject obj)
 {
-  unmark_for_gc (obj);
+  unmark_for_gc (obj, global_ref_table);
 }
 
 static void
@@ -222,7 +219,7 @@ _Jv_JNI_DeleteLocalRef (JNIEnv *env, jobject obj)
          if (frame->vec[i] == obj)
            {
              frame->vec[i] = NULL;
-             unmark_for_gc (obj);
+             unmark_for_gc (obj, local_ref_table);
              return;
            }
        }
@@ -281,16 +278,23 @@ _Jv_JNI_NewLocalRef (JNIEnv *env, jobject obj)
   // Try to find an open slot somewhere in the topmost frame.
   _Jv_JNI_LocalFrame *frame = env->locals;
   bool done = false, set = false;
-  while (frame != NULL && ! done)
+  for (; frame != NULL && ! done; frame = frame->next)
     {
       for (int i = 0; i < frame->size; ++i)
-       if (frame->vec[i] == NULL)
-         {
-           set = true;
-           done = true;
-           frame->vec[i] = obj;
-           break;
-         }
+       {
+         if (frame->vec[i] == NULL)
+           {
+             set = true;
+             done = true;
+             frame->vec[i] = obj;
+             break;
+           }
+       }
+
+      // If we found a slot, or if the frame we just searched is the
+      // mark frame, then we are done.
+      if (done || frame->marker != MARK_NONE)
+       break;
     }
 
   if (! set)
@@ -302,7 +306,7 @@ _Jv_JNI_NewLocalRef (JNIEnv *env, jobject obj)
       env->locals->vec[0] = obj;
     }
 
-  mark_for_gc (obj);
+  mark_for_gc (obj, local_ref_table);
   return obj;
 }
 
@@ -316,7 +320,7 @@ _Jv_JNI_PopLocalFrame (JNIEnv *env, jobject result, int stop)
     {  
       for (int i = 0; i < rf->size; ++i)
        if (rf->vec[i] != NULL)
-         unmark_for_gc (rf->vec[i]);
+         unmark_for_gc (rf->vec[i], local_ref_table);
 
       // If the frame we just freed is the marker frame, we are done.
       done = (rf->marker == stop);
@@ -467,7 +471,7 @@ static jint
 _Jv_JNI_Throw (JNIEnv *env, jthrowable obj)
 {
   // We check in case the user did some funky cast.
-  JvAssert (obj != NULL && (&ThrowableClass)->isInstance (obj));
+  JvAssert (obj != NULL && java::lang::Throwable::class$.isInstance (obj));
   env->ex = obj;
   return 0;
 }
@@ -477,13 +481,14 @@ _Jv_JNI_ThrowNew (JNIEnv *env, jclass clazz, const char *message)
 {
   using namespace java::lang::reflect;
 
-  JvAssert ((&ThrowableClass)->isAssignableFrom (clazz));
+  JvAssert (java::lang::Throwable::class$.isAssignableFrom (clazz));
 
   int r = JNI_OK;
   try
     {
       JArray<jclass> *argtypes
-       = (JArray<jclass> *) JvNewObjectArray (1, &ClassClass, NULL);
+       = (JArray<jclass> *) JvNewObjectArray (1, &java::lang::Class::class$,
+                                              NULL);
 
       jclass *elts = elements (argtypes);
       elts[0] = &StringClass;
@@ -922,7 +927,7 @@ _Jv_JNI_CallStaticMethodV (JNIEnv *env, jclass klass,
                           jmethodID id, va_list args)
 {
   JvAssert (((id->accflags) & java::lang::reflect::Modifier::STATIC));
-  JvAssert ((&ClassClass)->isInstance (klass));
+  JvAssert (java::lang::Class::class$.isInstance (klass));
 
   return _Jv_JNI_CallAnyMethodV<T, static_type> (env, NULL, klass, id, args);
 }
@@ -937,7 +942,7 @@ _Jv_JNI_CallStaticMethod (JNIEnv *env, jclass klass, jmethodID id, ...)
   T result;
 
   JvAssert (((id->accflags) & java::lang::reflect::Modifier::STATIC));
-  JvAssert ((&ClassClass)->isInstance (klass));
+  JvAssert (java::lang::Class::class$.isInstance (klass));
 
   va_start (args, id);
   result = _Jv_JNI_CallAnyMethodV<T, static_type> (env, NULL, klass,
@@ -955,7 +960,7 @@ _Jv_JNI_CallStaticMethodA (JNIEnv *env, jclass klass, jmethodID id,
                           jvalue *args)
 {
   JvAssert (((id->accflags) & java::lang::reflect::Modifier::STATIC));
-  JvAssert ((&ClassClass)->isInstance (klass));
+  JvAssert (java::lang::Class::class$.isInstance (klass));
 
   return _Jv_JNI_CallAnyMethodA<T, static_type> (env, NULL, klass, id, args);
 }
@@ -1155,7 +1160,7 @@ static const jchar *
 _Jv_JNI_GetStringChars (JNIEnv *, jstring string, jboolean *isCopy)
 {
   jchar *result = _Jv_GetStringChars (string);
-  mark_for_gc (string);
+  mark_for_gc (string, global_ref_table);
   if (isCopy)
     *isCopy = false;
   return (const jchar *) result;
@@ -1164,7 +1169,7 @@ _Jv_JNI_GetStringChars (JNIEnv *, jstring string, jboolean *isCopy)
 static void
 _Jv_JNI_ReleaseStringChars (JNIEnv *, jstring string, const jchar *)
 {
-  unmark_for_gc (string);
+  unmark_for_gc (string, global_ref_table);
 }
 
 static jstring
@@ -1343,7 +1348,7 @@ _Jv_JNI_GetPrimitiveArrayElements (JNIEnv *, JArray<T> *array,
       // We elect never to copy.
       *isCopy = false;
     }
-  mark_for_gc (array);
+  mark_for_gc (array, global_ref_table);
   return elts;
 }
 
@@ -1355,7 +1360,7 @@ _Jv_JNI_ReleasePrimitiveArrayElements (JNIEnv *, JArray<T> *array,
   // Note that we ignore MODE.  We can do this because we never copy
   // the array elements.  My reading of the JNI documentation is that
   // this is an option for the implementor.
-  unmark_for_gc (array);
+  unmark_for_gc (array, global_ref_table);
 }
 
 template<typename T>
@@ -1366,7 +1371,7 @@ _Jv_JNI_GetPrimitiveArrayRegion (JNIEnv *env, JArray<T> *array,
 {
   // The cast to unsigned lets us save a comparison.
   if (start < 0 || len < 0
-      || (unsigned long) (start + len) >= (unsigned long) array->length)
+      || (unsigned long) (start + len) > (unsigned long) array->length)
     {
       try
        {
@@ -1393,7 +1398,7 @@ _Jv_JNI_SetPrimitiveArrayRegion (JNIEnv *env, JArray<T> *array,
 {
   // The cast to unsigned lets us save a comparison.
   if (start < 0 || len < 0
-      || (unsigned long) (start + len) >= (unsigned long) array->length)
+      || (unsigned long) (start + len) > (unsigned long) array->length)
     {
       try
        {
@@ -1532,25 +1537,137 @@ static jmethodID
 _Jv_JNI_FromReflectedMethod (JNIEnv *, jobject method)
 {
   using namespace java::lang::reflect;
-  if ((&MethodClass)->isInstance (method))
+  if (Method::class$.isInstance (method))
     return _Jv_FromReflectedMethod (reinterpret_cast<Method *> (method));
   return
     _Jv_FromReflectedConstructor (reinterpret_cast<Constructor *> (method));
 }
 
+\f
+
+// Hash table of native methods.
+static JNINativeMethod *nathash;
+// Number of slots used.
+static int nathash_count = 0;
+// Number of slots available.  Must be power of 2.
+static int nathash_size = 0;
+
+#define DELETED_ENTRY ((char *) (~0))
+
+// Compute a hash value for a native method descriptor.
+static int
+hash (const JNINativeMethod *method)
+{
+  char *ptr;
+  int hash = 0;
+
+  ptr = method->name;
+  while (*ptr)
+    hash = (31 * hash) + *ptr++;
+
+  ptr = method->signature;
+  while (*ptr)
+    hash = (31 * hash) + *ptr++;
+
+  return hash;
+}
+
+// Find the slot where a native method goes.
+static JNINativeMethod *
+nathash_find_slot (const JNINativeMethod *method)
+{
+  jint h = hash (method);
+  int step = (h ^ (h >> 16)) | 1;
+  int w = h & (nathash_size - 1);
+  int del = -1;
+
+  for (;;)
+    {
+      JNINativeMethod *slotp = &nathash[w];
+      if (slotp->name == NULL)
+       {
+         if (del >= 0)
+           return &nathash[del];
+         else
+           return slotp;
+       }
+      else if (slotp->name == DELETED_ENTRY)
+       del = w;
+      else if (! strcmp (slotp->name, method->name)
+              && ! strcmp (slotp->signature, method->signature))
+       return slotp;
+      w = (w + step) & (nathash_size - 1);
+    }
+}
+
+// Find a method.  Return NULL if it isn't in the hash table.
+static void *
+nathash_find (JNINativeMethod *method)
+{
+  if (nathash == NULL)
+    return NULL;
+  JNINativeMethod *slot = nathash_find_slot (method);
+  if (slot->name == NULL || slot->name == DELETED_ENTRY)
+    return NULL;
+  return slot->fnPtr;
+}
+
+static void
+natrehash ()
+{
+  if (nathash == NULL)
+    {
+      nathash_size = 1024;
+      nathash =
+       (JNINativeMethod *) _Jv_AllocBytes (nathash_size
+                                           * sizeof (JNINativeMethod));
+      memset (nathash, 0, nathash_size * sizeof (JNINativeMethod));
+    }
+  else
+    {
+      int savesize = nathash_size;
+      JNINativeMethod *savehash = nathash;
+      nathash_size *= 2;
+      nathash =
+       (JNINativeMethod *) _Jv_AllocBytes (nathash_size
+                                           * sizeof (JNINativeMethod));
+      memset (nathash, 0, nathash_size * sizeof (JNINativeMethod));
+
+      for (int i = 0; i < savesize; ++i)
+       {
+         if (savehash[i].name != NULL && savehash[i].name != DELETED_ENTRY)
+           {
+             JNINativeMethod *slot = nathash_find_slot (&savehash[i]);
+             *slot = savehash[i];
+           }
+       }
+    }
+}
+
+static void
+nathash_add (const JNINativeMethod *method)
+{
+  if (3 * nathash_count >= 2 * nathash_size)
+    natrehash ();
+  JNINativeMethod *slot = nathash_find_slot (method);
+  // If the slot has a real entry in it, then there is no work to do.
+  if (slot->name != NULL && slot->name != DELETED_ENTRY)
+    return;
+  // FIXME
+  slot->name = strdup (method->name);
+  slot->signature = strdup (method->signature);
+  slot->fnPtr = method->fnPtr;
+}
+
 static jint
-_Jv_JNI_RegisterNatives (JNIEnv *env, jclass k,
+_Jv_JNI_RegisterNatives (JNIEnv *env, jclass klass,
                         const JNINativeMethod *methods,
                         jint nMethods)
 {
-#ifdef INTERPRETER
-  // For now, this only matters for interpreted methods.  FIXME.
-  if (! _Jv_IsInterpretedClass (k))
-    {
-      // FIXME: throw exception.
-      return JNI_ERR;
-    }
-  _Jv_InterpClass *klass = reinterpret_cast<_Jv_InterpClass *> (k);
+  // Synchronize while we do the work.  This must match
+  // synchronization in some other functions that manipulate or use
+  // the nathash table.
+  JvSynchronize sync (global_ref_table);
 
   // Look at each descriptor given us, and find the corresponding
   // method in the class.
@@ -1558,11 +1675,10 @@ _Jv_JNI_RegisterNatives (JNIEnv *env, jclass k,
     {
       bool found = false;
 
-      _Jv_MethodBase **imeths = _Jv_GetFirstMethod (klass);
+      _Jv_Method *imeths = JvGetFirstMethod (klass);
       for (int i = 0; i < JvNumMethods (klass); ++i)
        {
-         _Jv_MethodBase *meth = imeths[i];
-         _Jv_Method *self = meth->get_method ();
+         _Jv_Method *self = &imeths[i];
 
          if (! strcmp (self->name->data, methods[j].name)
              && ! strcmp (self->signature->data, methods[j].signature))
@@ -1572,9 +1688,9 @@ _Jv_JNI_RegisterNatives (JNIEnv *env, jclass k,
                break;
 
              // Found a match that is native.
-             _Jv_JNIMethod *jmeth = reinterpret_cast<_Jv_JNIMethod *> (meth);
-             jmeth->set_function (methods[i].fnPtr);
              found = true;
+             nathash_add (&methods[j]);
+
              break;
            }
        }
@@ -1595,14 +1711,12 @@ _Jv_JNI_RegisterNatives (JNIEnv *env, jclass k,
     }
 
   return JNI_OK;
-#else /* INTERPRETER */
-  return JNI_ERR;
-#endif /* INTERPRETER */
 }
 
 static jint
 _Jv_JNI_UnregisterNatives (JNIEnv *, jclass)
 {
+  // FIXME -- we could implement this.
   return JNI_ERR;
 }
 
@@ -1746,6 +1860,22 @@ _Jv_LookupJNIMethod (jclass klass, _Jv_Utf8Const *name,
   int long_start;
   void *function;
 
+  // Synchronize on something convenient.  Right now we use the hash.
+  JvSynchronize sync (global_ref_table);
+
+  // First see if we have an override in the hash table.
+  strncpy (buf, name->data, name->length);
+  buf[name->length] = '\0';
+  strncpy (buf + name->length + 1, signature->data, signature->length);
+  buf[name->length + signature->length + 1] = '\0';
+  JNINativeMethod meth;
+  meth.name = buf;
+  meth.signature = buf + name->length + 1;
+  function = nathash_find (&meth);
+  if (function != NULL)
+    return function;
+
+  // If there was no override, then look in the symbol table.
   mangled_name (klass, name, signature, buf, &long_start);
   char c = buf[long_start];
   buf[long_start] = '\0';
@@ -1782,10 +1912,16 @@ _Jv_JNIMethod::call (ffi_cif *, void *ret, ffi_raw *args, void *__this)
   // We cache the value that we find, of course, but if we don't find
   // a value we don't cache that fact -- we might subsequently load a
   // library which finds the function in question.
-  if (_this->function == NULL)
-    _this->function = _Jv_LookupJNIMethod (_this->defining_class,
-                                          _this->self->name,
-                                          _this->self->signature);
+  {
+    // Synchronize on a convenient object to ensure sanity in case two
+    // threads reach this point for the same function at the same
+    // time.
+    JvSynchronize sync (global_ref_table);
+    if (_this->function == NULL)
+      _this->function = _Jv_LookupJNIMethod (_this->defining_class,
+                                            _this->self->name,
+                                            _this->self->signature);
+  }
 
   JvAssert (_this->args_raw_size % sizeof (ffi_raw) == 0);
   ffi_raw real_args[2 + _this->args_raw_size / sizeof (ffi_raw)];
@@ -1831,7 +1967,7 @@ _Jv_JNI_AttachCurrentThread (JavaVM *, jstring name, void **penv, void *args)
          && attach->version != JNI_VERSION_1_1)
        return JNI_EVERSION;
 
-      JvAssert ((&ThreadGroupClass)->isInstance (attach->group));
+      JvAssert (java::lang::ThreadGroup::class$.isInstance (attach->group));
       group = reinterpret_cast<java::lang::ThreadGroup *> (attach->group);
     }
 
@@ -1862,7 +1998,7 @@ _Jv_JNI_AttachCurrentThread (JavaVM *, jstring name, void **penv, void *args)
     {
       try
        {
-         (void) new gnu::gcj::jni::NativeThread (group, name);
+         _Jv_AttachCurrentThread (name, group);
        }
       catch (jthrowable t)
        {
@@ -1916,28 +2052,11 @@ _Jv_JNI_DestroyJavaVM (JavaVM *vm)
   return JNI_ERR;
 }
 
-static jint
+jint
 _Jv_JNI_DetachCurrentThread (JavaVM *)
 {
-  java::lang::Thread *t = _Jv_ThreadCurrent ();
-  if (t == NULL)
-    return JNI_EDETACHED;
-
-  // FIXME: we only allow threads attached via AttachCurrentThread to
-  // be detached.  I have no idea how we could implement detaching
-  // other threads, given the requirement that we must release all the
-  // monitors.  That just seems evil.
-  JvAssert ((&NativeThreadClass)->isInstance (t));
-
-  // FIXME: release the monitors.  We'll take this to mean all
-  // monitors acquired via the JNI interface.  This means we have to
-  // keep track of them.
-
-  gnu::gcj::jni::NativeThread *nt
-    = reinterpret_cast<gnu::gcj::jni::NativeThread *> (t);
-  nt->finish ();
-
-  return 0;
+  jint code = _Jv_DetachCurrentThread ();
+  return code  ? JNI_EDETACHED : 0;
 }
 
 static jint
@@ -2044,6 +2163,9 @@ JNI_CreateJavaVM (JavaVM **vm, void **penv, void *args)
 
   the_vm = nvm;
   *vm = the_vm;
+
+  _Jv_JNI_Init();
+
   return 0;
 }