Christian Thalinger
Edwin Steiner
- $Id: jni.c 4567 2006-03-07 10:48:24Z twisti $
+ $Id: jni.c 4901 2006-05-11 12:18:55Z twisti $
*/
/* global reference table *****************************************************/
-static java_objectheader **global_ref_table;
+/* hashsize must be power of 2 */
-/* jmethodID and jclass caching variables for NewGlobalRef and DeleteGlobalRef*/
-static classinfo *ihmclass = NULL;
-static methodinfo *putmid = NULL;
-static methodinfo *getmid = NULL;
-static methodinfo *removemid = NULL;
+#define HASHTABLE_GLOBAL_REF_SIZE 64 /* initial size of globalref-hash */
+
+static hashtable *hashtable_global_ref; /* hashtable for globalrefs */
/* direct buffer stuff ********************************************************/
/* some forward declarations **************************************************/
jobject NewLocalRef(JNIEnv *env, jobject ref);
+jint EnsureLocalCapacity(JNIEnv* env, jint capacity);
/* jni_init ********************************************************************
bool jni_init(void)
{
- /* initalize global reference table */
-
- if (!(ihmclass =
- load_class_bootstrap(utf_new_char("java/util/IdentityHashMap"))))
- return false;
-
- global_ref_table = GCNEW(jobject);
-
- if (!(*global_ref_table = native_new_and_init(ihmclass)))
- return false;
-
- if (!(getmid = class_resolvemethod(ihmclass, utf_get,
- utf_java_lang_Object__java_lang_Object)))
- return false;
+ /* create global ref hashtable */
- if (!(putmid = class_resolvemethod(ihmclass, utf_put,
- utf_new_char("(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"))))
- return false;
+ hashtable_global_ref = NEW(hashtable);
- if (!(removemid =
- class_resolvemethod(ihmclass, utf_remove,
- utf_java_lang_Object__java_lang_Object)))
- return false;
+ hashtable_create(hashtable_global_ref, HASHTABLE_GLOBAL_REF_SIZE);
/* direct buffer stuff */
classinfo *c;
s4 i;
s4 j;
+ s8 value;
paramcount = descr->paramcount;
paramtypes = descr->paramtypes;
if (o != NULL) {
/* this pointer */
- vmargs[0].type = TYPE_ADR;
- vmargs[0].data = (u8) (ptrint) o;
+ vmargs[0].type = TYPE_ADR;
+ vmargs[0].data.l = (u8) (ptrint) o;
paramtypes++;
paramcount--;
switch (paramtypes->decltype) {
case PRIMITIVETYPE_BOOLEAN:
if (c == primitivetype_table[paramtypes->decltype].class_wrap)
- vmargs[i].data = (s8) ((java_lang_Boolean *) param)->value;
+ value = (s8) ((java_lang_Boolean *) param)->value;
else
goto illegal_arg;
+
+ vmargs[i].data.l = value;
break;
case PRIMITIVETYPE_BYTE:
if (c == primitivetype_table[paramtypes->decltype].class_wrap)
- vmargs[i].data = (s8) ((java_lang_Byte *) param)->value;
+ value = (s8) ((java_lang_Byte *) param)->value;
else
goto illegal_arg;
+
+ vmargs[i].data.l = value;
break;
case PRIMITIVETYPE_CHAR:
if (c == primitivetype_table[paramtypes->decltype].class_wrap)
- vmargs[i].data = (s8) ((java_lang_Character *) param)->value;
+ value = (s8) ((java_lang_Character *) param)->value;
else
goto illegal_arg;
+
+ vmargs[i].data.l = value;
break;
case PRIMITIVETYPE_SHORT:
if (c == primitivetype_table[paramtypes->decltype].class_wrap)
- vmargs[i].data = (s8) ((java_lang_Short *) param)->value;
+ value = (s8) ((java_lang_Short *) param)->value;
else if (c == primitivetype_table[PRIMITIVETYPE_BYTE].class_wrap)
- vmargs[i].data = (s8) ((java_lang_Byte *) param)->value;
+ value = (s8) ((java_lang_Byte *) param)->value;
else
goto illegal_arg;
+
+ vmargs[i].data.l = value;
break;
case PRIMITIVETYPE_INT:
if (c == primitivetype_table[paramtypes->decltype].class_wrap)
- vmargs[i].data = (s8) ((java_lang_Integer *) param)->value;
+ value = (s8) ((java_lang_Integer *) param)->value;
else if (c == primitivetype_table[PRIMITIVETYPE_SHORT].class_wrap)
- vmargs[i].data = (s8) ((java_lang_Short *) param)->value;
+ value = (s8) ((java_lang_Short *) param)->value;
else if (c == primitivetype_table[PRIMITIVETYPE_BYTE].class_wrap)
- vmargs[i].data = (s8) ((java_lang_Byte *) param)->value;
+ value = (s8) ((java_lang_Byte *) param)->value;
else
goto illegal_arg;
+
+ vmargs[i].data.l = value;
break;
case PRIMITIVETYPE_LONG:
if (c == primitivetype_table[paramtypes->decltype].class_wrap)
- vmargs[i].data = (s8) ((java_lang_Long *) param)->value;
+ value = (s8) ((java_lang_Long *) param)->value;
else if (c == primitivetype_table[PRIMITIVETYPE_INT].class_wrap)
- vmargs[i].data = (s8) ((java_lang_Integer *) param)->value;
+ value = (s8) ((java_lang_Integer *) param)->value;
else if (c == primitivetype_table[PRIMITIVETYPE_SHORT].class_wrap)
- vmargs[i].data = (s8) ((java_lang_Short *) param)->value;
+ value = (s8) ((java_lang_Short *) param)->value;
else if (c == primitivetype_table[PRIMITIVETYPE_BYTE].class_wrap)
- vmargs[i].data = (s8) ((java_lang_Byte *) param)->value;
+ value = (s8) ((java_lang_Byte *) param)->value;
else
goto illegal_arg;
+
+ vmargs[i].data.l = value;
break;
case PRIMITIVETYPE_FLOAT:
if (c == primitivetype_table[paramtypes->decltype].class_wrap)
- *((jfloat *) (&vmargs[i].data)) = (jfloat) ((java_lang_Float *) param)->value;
+ vmargs[i].data.f = (jfloat) ((java_lang_Float *) param)->value;
else
goto illegal_arg;
break;
case PRIMITIVETYPE_DOUBLE:
if (c == primitivetype_table[paramtypes->decltype].class_wrap)
- *((jdouble *) (&vmargs[i].data)) = (jdouble) ((java_lang_Double *) param)->value;
+ vmargs[i].data.d = (jdouble) ((java_lang_Double *) param)->value;
else if (c == primitivetype_table[PRIMITIVETYPE_FLOAT].class_wrap)
- *((jfloat *) (&vmargs[i].data)) = (jfloat) ((java_lang_Float *) param)->value;
+ vmargs[i].data.f = (jfloat) ((java_lang_Float *) param)->value;
else
goto illegal_arg;
break;
goto illegal_arg;
}
}
- vmargs[i].type = TYPE_ADR;
- vmargs[i].data = (u8) (ptrint) params->data[j];
+
+ vmargs[i].type = TYPE_ADR;
+ vmargs[i].data.l = (u8) (ptrint) params->data[j];
break;
default:
STATISTICS(jniinvokation());
cl = (java_lang_ClassLoader *) loader;
- s = javastring_new_char(name);
+ s = javastring_new_from_ascii(name);
ba = (java_bytearray *) buf;
c = (jclass) Java_java_lang_VMClassLoader_defineClass(env, NULL, cl, s, ba,
STATISTICS(jniinvokation());
- s = (java_lang_String *) javastring_new_char(msg);
+ s = (java_lang_String *) javastring_new_from_ascii(msg);
/* instantiate exception object */
jint PushLocalFrame(JNIEnv* env, jint capacity)
{
+ s4 additionalrefs;
+ localref_table *lrt;
+ localref_table *nlrt;
+
STATISTICS(jniinvokation());
- log_text("JNI-Call: PushLocalFrame: IMPLEMENT ME!");
+ if (capacity <= 0)
+ return -1;
- assert(0);
+ /* Allocate new local reference table on Java heap. Calculate the
+ additional memory we have to allocate. */
+
+ if (capacity > LOCALREFTABLE_CAPACITY)
+ additionalrefs = capacity - LOCALREFTABLE_CAPACITY;
+ else
+ additionalrefs = 0;
+
+ nlrt = GCMNEW(u1, sizeof(localref_table) + additionalrefs * SIZEOF_VOID_P);
+
+ if (nlrt == NULL)
+ return -1;
+
+ /* get current local reference table from thread */
+
+ lrt = LOCALREFTABLE;
+
+ /* Set up the new local reference table and add it to the local
+ frames chain. */
+
+ nlrt->capacity = capacity;
+ nlrt->used = 0;
+ nlrt->localframes = lrt->localframes + 1;
+ nlrt->prev = lrt;
+
+ /* store new local reference table in thread */
+
+ LOCALREFTABLE = nlrt;
return 0;
}
+
/* PopLocalFrame ***************************************************************
Pops off the current local reference frame, frees all the local
jobject PopLocalFrame(JNIEnv* env, jobject result)
{
+ localref_table *lrt;
+ localref_table *plrt;
+ s4 localframes;
+
STATISTICS(jniinvokation());
- log_text("JNI-Call: PopLocalFrame: IMPLEMENT ME!");
+ /* get current local reference table from thread */
- assert(0);
+ lrt = LOCALREFTABLE;
+
+ localframes = lrt->localframes;
+
+ /* Don't delete the top local frame, as this one is allocated in
+ the native stub on the stack and is freed automagically on
+ return. */
+
+ if (localframes == 1)
+ return NewLocalRef(env, result);
+
+ /* release all current local frames */
+
+ for (; localframes >= 1; localframes--) {
+ /* get previous frame */
+
+ plrt = lrt->prev;
+
+ /* clear all reference entries */
+
+ MSET(&lrt->refs[0], 0, java_objectheader*, lrt->capacity);
+
+ lrt->prev = NULL;
+
+ /* set new local references table */
+
+ lrt = plrt;
+ }
+
+ /* store new local reference table in thread */
+
+ LOCALREFTABLE = lrt;
/* add local reference and return the value */
- return NewLocalRef(env, NULL);
+ return NewLocalRef(env, result);
}
lrt = LOCALREFTABLE;
- /* remove the reference */
+ /* go through all local frames */
- for (i = 0; i < lrt->capacity; i++) {
- if (lrt->refs[i] == o) {
- lrt->refs[i] = NULL;
- lrt->used--;
+ for (; lrt != NULL; lrt = lrt->prev) {
- return;
+ /* and try to remove the reference */
+
+ for (i = 0; i < lrt->capacity; i++) {
+ if (lrt->refs[i] == o) {
+ lrt->refs[i] = NULL;
+ lrt->used--;
+
+ return;
+ }
}
}
/* if (opt_checkjni) */
/* FatalError(env, "Bad global or local ref passed to JNI"); */
- log_text("JNI-DeleteLocalRef: Bad global or local ref passed to JNI");
+ log_text("JNI-DeleteLocalRef: Local ref passed to JNI not found");
}
lrt = LOCALREFTABLE;
- /* check if we have space for the requested reference */
+ /* Check if we have space for the requested reference? No,
+ allocate a new frame. This is actually not what the spec says,
+ but for compatibility reasons... */
if (lrt->used == lrt->capacity) {
-/* throw_cacao_exception_exit(string_java_lang_InternalError, */
-/* "Too many local references"); */
- fprintf(stderr, "Too many local references");
- assert(0);
+ if (EnsureLocalCapacity(env, 16) != 0)
+ return NULL;
+
+ /* get the new local reference table */
+
+ lrt = LOCALREFTABLE;
}
/* insert the reference */
{
localref_table *lrt;
+ log_text("JNI-Call: EnsureLocalCapacity");
+
STATISTICS(jniinvokation());
/* get local reference table (thread specific) */
/* check if capacity elements are available in the local references table */
- if ((lrt->used + capacity) > lrt->capacity) {
- *exceptionptr = new_exception(string_java_lang_OutOfMemoryError);
- return -1;
- }
+ if ((lrt->used + capacity) > lrt->capacity)
+ return PushLocalFrame(env, capacity);
return 0;
}
*******************************************************************************/
-jobject NewGlobalRef(JNIEnv* env, jobject lobj)
+jobject NewGlobalRef(JNIEnv* env, jobject obj)
{
- java_objectheader *o;
- java_lang_Integer *refcount;
- java_objectheader *newval;
+ hashtable_global_ref_entry *gre;
+ u4 key; /* hashkey */
+ u4 slot; /* slot in hashtable */
STATISTICS(jniinvokation());
#if defined(USE_THREADS)
- builtin_monitorenter(*global_ref_table);
+ builtin_monitorenter(hashtable_global_ref->header);
#endif
+
+ /* normally addresses are aligned to 4, 8 or 16 bytes */
+
+ key = ((u4) (ptrint) obj) >> 4; /* align to 16-byte boundaries */
+ slot = key & (hashtable_global_ref->size - 1);
+ gre = hashtable_global_ref->ptr[slot];
- o = vm_call_method(getmid, *global_ref_table, lobj);
+ /* search external hash chain for the entry */
- refcount = (java_lang_Integer *) o;
+ while (gre) {
+ if (gre->o == obj) {
+ /* global object found, increment the reference */
- if (refcount == NULL) {
- newval = native_new_and_init_int(class_java_lang_Integer, 1);
+ gre->refs++;
- if (newval == NULL) {
#if defined(USE_THREADS)
- builtin_monitorexit(*global_ref_table);
+ builtin_monitorexit(hashtable_global_ref->header);
#endif
- return NULL;
+
+ return obj;
}
- (void) vm_call_method(putmid, *global_ref_table, lobj, newval);
+ gre = gre->hashlink; /* next element in external chain */
+ }
- } else {
- /* we can access the object itself, as we are in a
- synchronized section */
+ /* global ref not found, create a new one */
- refcount->value++;
- }
+ gre = NEW(hashtable_global_ref_entry);
+
+ gre->o = obj;
+ gre->refs = 1;
+
+ /* insert entry into hashtable */
+
+ gre->hashlink = hashtable_global_ref->ptr[slot];
+
+ hashtable_global_ref->ptr[slot] = gre;
+
+ /* update number of hashtable-entries */
+
+ hashtable_global_ref->entries++;
#if defined(USE_THREADS)
- builtin_monitorexit(*global_ref_table);
+ builtin_monitorexit(hashtable_global_ref->header);
#endif
- return lobj;
+ return obj;
}
void DeleteGlobalRef(JNIEnv* env, jobject globalRef)
{
- java_objectheader *o;
- java_lang_Integer *refcount;
- s4 val;
+ hashtable_global_ref_entry *gre;
+ hashtable_global_ref_entry *prevgre;
+ u4 key; /* hashkey */
+ u4 slot; /* slot in hashtable */
STATISTICS(jniinvokation());
#if defined(USE_THREADS)
- builtin_monitorenter(*global_ref_table);
+ builtin_monitorenter(hashtable_global_ref->header);
#endif
- o = vm_call_method(getmid, *global_ref_table, globalRef);
+ /* normally addresses are aligned to 4, 8 or 16 bytes */
- refcount = (java_lang_Integer *) o;
+ key = ((u4) (ptrint) globalRef) >> 4; /* align to 16-byte boundaries */
+ slot = key & (hashtable_global_ref->size - 1);
+ gre = hashtable_global_ref->ptr[slot];
- if (refcount == NULL) {
- log_text("JNI-DeleteGlobalRef: unable to find global reference");
- return;
- }
+ /* initialize prevgre */
- /* we can access the object itself, as we are in a synchronized
- section */
+ prevgre = NULL;
- val = refcount->value - 1;
+ /* search external hash chain for the entry */
- if (val == 0) {
- (void) vm_call_method(removemid, *global_ref_table, refcount);
+ while (gre) {
+ if (gre->o == globalRef) {
+ /* global object found, decrement the reference count */
- } else {
- /* we do not create a new object, but set the new value into
- the old one */
+ gre->refs--;
+
+ /* if reference count is 0, remove the entry */
+
+ if (gre->refs == 0) {
+ /* special handling if it's the first in the chain */
+
+ if (prevgre == NULL)
+ hashtable_global_ref->ptr[slot] = gre->hashlink;
+ else
+ prevgre->hashlink = gre->hashlink;
- refcount->value = val;
+ FREE(gre, hashtable_global_ref_entry);
+ }
+
+#if defined(USE_THREADS)
+ builtin_monitorexit(hashtable_global_ref->header);
+#endif
+
+ return;
+ }
+
+ prevgre = gre; /* save current pointer for removal */
+ gre = gre->hashlink; /* next element in external chain */
}
+ log_println("JNI-DeleteGlobalRef: global reference not found");
+
#if defined(USE_THREADS)
- builtin_monitorexit(*global_ref_table);
+ builtin_monitorexit(hashtable_global_ref->header);
#endif
}
_Jv_env = env;
- /* actually create the JVM */
-
- if (!vm_create(_vm_args))
- return -1;
/* create and fill a JavaVM structure */
jvm->functions = &_Jv_JNIInvokeInterface;
/* XXX Set the global variable. Maybe we should do that differently. */
-
+ /* XXX JVMTI Agents needs a JavaVM */
_Jv_jvm = jvm;
+
+ /* actually create the JVM */
+
+ if (!vm_create(_vm_args))
+ return -1;
+
/* setup the local ref table (must be created after vm_create) */
lrt = GCNEW(localref_table);