// jni.cc - JNI implementation, including the jump table. /* Copyright (C) 1998, 1999, 2000 Red Hat, Inc. This file is part of libgcj. This software is copyrighted work licensed under the terms of the Libgcj License. Please consult the file "LIBGCJ_LICENSE" for details. */ // Note: currently we take the approach of not checking most // arguments. Instead we could do more checking conditionally (e.g., // if DEBUG is defined). That might be beneficial in some cases, // though to me it seems that one could just as easily use the // debugger. #include #include #include // Define this before including jni.h. #define __GCJ_JNI_IMPL__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ClassClass _CL_Q34java4lang5Class extern java::lang::Class ClassClass; #define ObjectClass _CL_Q34java4lang6Object extern java::lang::Class ObjectClass; #define MethodClass _CL_Q44java4lang7reflect6Method extern java::lang::Class MethodClass; // This enum is used to select different template instantiations in // the invocation code. enum invocation_type { normal, nonvirtual, static_type, constructor }; // Forward declaration. extern struct JNINativeInterface _Jv_JNIFunctions; // Number of slots in the default frame. The VM must allow at least // 16. #define FRAME_SIZE 32 // This structure is used to keep track of local references. struct _Jv_JNI_LocalFrame { // This is true if this frame object represents a pushed frame (eg // from PushLocalFrame). int marker : 1; // Number of elements in frame. int size : 31; // Next frame in chain. _Jv_JNI_LocalFrame *next; // The elements. These are allocated using the C "struct hack". jobject vec[0]; }; // This holds a reference count for all local and global references. static java::util::Hashtable *ref_table; void _Jv_JNI_Init (void) { ref_table = new java::util::Hashtable; } // Tell the GC that a certain pointer is live. static void mark_for_gc (jobject obj) { JvSynchronize sync (ref_table); using namespace java::lang; Integer *refcount = (Integer *) ref_table->get (obj); jint val = (refcount == NULL) ? 0 : refcount->intValue (); ref_table->put (obj, new Integer (val + 1)); } // Unmark a pointer. static void unmark_for_gc (jobject obj) { JvSynchronize sync (ref_table); using namespace java::lang; Integer *refcount = (Integer *) ref_table->get (obj); JvAssert (refcount); jint val = refcount->intValue () - 1; if (val == 0) ref_table->remove (obj); else ref_table->put (obj, new Integer (val)); } static jobject _Jv_JNI_NewGlobalRef (JNIEnv *, jobject obj) { mark_for_gc (obj); return obj; } static void _Jv_JNI_DeleteGlobalRef (JNIEnv *, jobject obj) { unmark_for_gc (obj); } static void _Jv_JNI_DeleteLocalRef (JNIEnv *env, jobject obj) { _Jv_JNI_LocalFrame *frame; for (frame = env->locals; frame != NULL; frame = frame->next) { for (int i = 0; i < FRAME_SIZE; ++i) { if (frame->vec[i] == obj) { frame->vec[i] = NULL; unmark_for_gc (obj); return; } } // Don't go past a marked frame. JvAssert (! frame->marker); } JvAssert (0); } static jint _Jv_JNI_EnsureLocalCapacity (JNIEnv *env, jint size) { // It is easier to just always allocate a new frame of the requested // size. This isn't the most efficient thing, but for now we don't // care. Note that _Jv_JNI_PushLocalFrame relies on this right now. _Jv_JNI_LocalFrame *frame = (_Jv_JNI_LocalFrame *) _Jv_MallocUnchecked (sizeof (_Jv_JNI_LocalFrame) + size * sizeof (jobject)); if (frame == NULL) { // FIXME: exception processing. env->ex = new java::lang::OutOfMemoryError; return -1; } frame->marker = true; frame->size = size; memset (&frame->vec[0], 0, size * sizeof (jobject)); frame->next = env->locals; env->locals = frame; return 0; } static jint _Jv_JNI_PushLocalFrame (JNIEnv *env, jint size) { jint r = _Jv_JNI_EnsureLocalCapacity (env, size); if (r < 0) return r; // The new frame is on top. env->locals->marker = true; return 0; } static jobject _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 (int i = 0; i < frame->size; ++i) if (frame->vec[i] == NULL) { set = true; done = true; frame->vec[i] = obj; break; } } if (! set) { // No slots, so we allocate a new frame. According to the spec // we could just die here. FIXME: return value. _Jv_JNI_EnsureLocalCapacity (env, 16); // We know the first element of the new frame will be ok. env->locals->vec[0] = obj; } mark_for_gc (obj); return obj; } static jobject _Jv_JNI_PopLocalFrame (JNIEnv *env, jobject result) { _Jv_JNI_LocalFrame *rf = env->locals; bool done = false; while (rf != NULL && ! done) { for (int i = 0; i < rf->size; ++i) if (rf->vec[i] != NULL) unmark_for_gc (rf->vec[i]); // If the frame we just freed is the marker frame, we are done. done = rf->marker; _Jv_JNI_LocalFrame *n = rf->next; // When N==NULL, we've reached the stack-allocated frame, and we // must not free it. However, we must be sure to clear all its // elements, since we might conceivably reuse it. if (n == NULL) { memset (&rf->vec[0], 0, rf->size * sizeof (jobject)); break; } _Jv_Free (rf); rf = n; } return result == NULL ? NULL : _Jv_JNI_NewLocalRef (env, result); } // This function is used from other template functions. It wraps the // return value appropriately; we specialize it so that object returns // are turned into local references. template static T wrap_value (JNIEnv *, T value) { return value; } template<> static jobject wrap_value (JNIEnv *env, jobject value) { return _Jv_JNI_NewLocalRef (env, value); } static jint _Jv_JNI_GetVersion (JNIEnv *) { return JNI_VERSION_1_2; } static jclass _Jv_JNI_DefineClass (JNIEnv *env, jobject loader, const jbyte *buf, jsize bufLen) { jbyteArray bytes = JvNewByteArray (bufLen); jbyte *elts = elements (bytes); memcpy (elts, buf, bufLen * sizeof (jbyte)); java::lang::ClassLoader *l = reinterpret_cast (loader); // FIXME: exception processing. jclass result = l->defineClass (bytes, 0, bufLen); return (jclass) _Jv_JNI_NewLocalRef (env, result); } static jclass _Jv_JNI_FindClass (JNIEnv *env, const char *name) { // FIXME: assume that NAME isn't too long. int len = strlen (name); char s[len + 1]; for (int i = 0; i <= len; ++i) s[i] = (name[i] == '/') ? '.' : name[i]; jstring n = JvNewStringUTF (s); java::lang::ClassLoader *loader; if (env->klass == NULL) { // FIXME: should use getBaseClassLoader, but we don't have that // yet. loader = java::lang::ClassLoader::getSystemClassLoader (); } else loader = env->klass->getClassLoader (); // FIXME: exception processing. jclass r = loader->findClass (n); return (jclass) _Jv_JNI_NewLocalRef (env, r); } static jclass _Jv_JNI_GetSuperclass (JNIEnv *env, jclass clazz) { return (jclass) _Jv_JNI_NewLocalRef (env, clazz->getSuperclass ()); } static jboolean _Jv_JNI_IsAssignableFrom(JNIEnv *, jclass clazz1, jclass clazz2) { return clazz1->isAssignableFrom (clazz2); } static jint _Jv_JNI_Throw (JNIEnv *env, jthrowable obj) { env->ex = obj; return 0; } static jint _Jv_JNI_ThrowNew (JNIEnv *env, jclass clazz, const char *message) { using namespace java::lang::reflect; JArray *argtypes = (JArray *) JvNewObjectArray (1, &ClassClass, NULL); jclass *elts = elements (argtypes); elts[0] = &StringClass; // FIXME: exception processing. Constructor *cons = clazz->getConstructor (argtypes); jobjectArray values = JvNewObjectArray (1, &StringClass, NULL); jobject *velts = elements (values); velts[0] = JvNewStringUTF (message); // FIXME: exception processing. jobject obj = cons->newInstance (values); env->ex = reinterpret_cast (obj); return 0; } static jthrowable _Jv_JNI_ExceptionOccurred (JNIEnv *env) { return (jthrowable) _Jv_JNI_NewLocalRef (env, env->ex); } static void _Jv_JNI_ExceptionDescribe (JNIEnv *env) { if (env->ex != NULL) env->ex->printStackTrace(); } static void _Jv_JNI_ExceptionClear (JNIEnv *env) { env->ex = NULL; } static jboolean _Jv_JNI_ExceptionCheck (JNIEnv *env) { return env->ex != NULL; } static void _Jv_JNI_FatalError (JNIEnv *, const char *message) { JvFail (message); } static jboolean _Jv_JNI_IsSameObject (JNIEnv *, jobject obj1, jobject obj2) { return obj1 == obj2; } static jobject _Jv_JNI_AllocObject (JNIEnv *env, jclass clazz) { jobject obj = NULL; using namespace java::lang::reflect; if (clazz->isInterface() || Modifier::isAbstract(clazz->getModifiers())) env->ex = new java::lang::InstantiationException (); else { // FIXME: exception processing. // FIXME: will this work for String? obj = JvAllocObject (clazz); } return _Jv_JNI_NewLocalRef (env, obj); } static jclass _Jv_JNI_GetObjectClass (JNIEnv *env, jobject obj) { return (jclass) _Jv_JNI_NewLocalRef (env, obj->getClass()); } static jboolean _Jv_JNI_IsInstanceOf (JNIEnv *, jobject obj, jclass clazz) { return clazz->isInstance(obj); } // // This section concerns method invocation. // template static jmethodID _Jv_JNI_GetAnyMethodID (JNIEnv *env, jclass clazz, const char *name, const char *sig) { // FIXME: exception processing. _Jv_InitClass (clazz); _Jv_Utf8Const *name_u = _Jv_makeUtf8Const ((char *) name, -1); _Jv_Utf8Const *sig_u = _Jv_makeUtf8Const ((char *) sig, -1); JvAssert (! clazz->isPrimitive()); using namespace java::lang::reflect; while (clazz != NULL) { jint count = JvNumMethods (clazz); jmethodID meth = JvGetFirstMethod (clazz); for (jint i = 0; i < count; ++i) { if (((is_static && Modifier::isStatic (meth->accflags)) || (! is_static && ! Modifier::isStatic (meth->accflags))) && _Jv_equalUtf8Consts (meth->name, name_u) && _Jv_equalUtf8Consts (meth->signature, sig_u)) return meth; meth = meth->getNextMethod(); } clazz = clazz->getSuperclass (); } env->ex = new java::lang::NoSuchMethodError (); return NULL; } // This is a helper function which turns a va_list into an array of // `jvalue's. It needs signature information in order to do its work. // The array of values must already be allocated. static void array_from_valist (jvalue *values, JArray *arg_types, va_list vargs) { jclass *arg_elts = elements (arg_types); for (int i = 0; i < arg_types->length; ++i) { if (arg_elts[i] == JvPrimClass (byte)) values[i].b = va_arg (vargs, jbyte); else if (arg_elts[i] == JvPrimClass (short)) values[i].s = va_arg (vargs, jshort); else if (arg_elts[i] == JvPrimClass (int)) values[i].i = va_arg (vargs, jint); else if (arg_elts[i] == JvPrimClass (long)) values[i].j = va_arg (vargs, jlong); else if (arg_elts[i] == JvPrimClass (float)) values[i].f = va_arg (vargs, jfloat); else if (arg_elts[i] == JvPrimClass (double)) values[i].d = va_arg (vargs, jdouble); else if (arg_elts[i] == JvPrimClass (boolean)) values[i].z = va_arg (vargs, jboolean); else if (arg_elts[i] == JvPrimClass (char)) values[i].c = va_arg (vargs, jchar); else { // An object. values[i].l = va_arg (vargs, jobject); } } } // This can call any sort of method: virtual, "nonvirtual", static, or // constructor. template static T _Jv_JNI_CallAnyMethodV (JNIEnv *env, jobject obj, jclass klass, jmethodID id, va_list vargs) { if (style == normal) id = _Jv_LookupDeclaredMethod (obj->getClass (), id->name, id->signature); jclass decl_class = klass ? klass : obj->getClass (); JvAssert (decl_class != NULL); jclass return_type; JArray *arg_types; // FIXME: exception processing. _Jv_GetTypesFromSignature (id, decl_class, &arg_types, &return_type); jvalue args[arg_types->length]; array_from_valist (args, arg_types, vargs); jvalue result; jthrowable ex = _Jv_CallAnyMethodA (obj, return_type, id, style == constructor, arg_types, args, &result); if (ex != NULL) env->ex = ex; // We cheat a little here. FIXME. return wrap_value (env, * (T *) &result); } template static T _Jv_JNI_CallAnyMethod (JNIEnv *env, jobject obj, jclass klass, jmethodID method, ...) { va_list args; T result; va_start (args, method); result = _Jv_JNI_CallAnyMethodV (env, obj, klass, method, args); va_end (args); return result; } template static T _Jv_JNI_CallAnyMethodA (JNIEnv *env, jobject obj, jclass klass, jmethodID id, jvalue *args) { if (style == normal) id = _Jv_LookupDeclaredMethod (obj->getClass (), id->name, id->signature); jclass decl_class = klass ? klass : obj->getClass (); JvAssert (decl_class != NULL); jclass return_type; JArray *arg_types; // FIXME: exception processing. _Jv_GetTypesFromSignature (id, decl_class, &arg_types, &return_type); jvalue result; jthrowable ex = _Jv_CallAnyMethodA (obj, return_type, id, style == constructor, arg_types, args, &result); if (ex != NULL) env->ex = ex; // We cheat a little here. FIXME. return wrap_value (env, * (T *) &result); } template static void _Jv_JNI_CallAnyVoidMethodV (JNIEnv *env, jobject obj, jclass klass, jmethodID id, va_list vargs) { if (style == normal) id = _Jv_LookupDeclaredMethod (obj->getClass (), id->name, id->signature); jclass decl_class = klass ? klass : obj->getClass (); JvAssert (decl_class != NULL); jclass return_type; JArray *arg_types; // FIXME: exception processing. _Jv_GetTypesFromSignature (id, decl_class, &arg_types, &return_type); jvalue args[arg_types->length]; array_from_valist (args, arg_types, vargs); jthrowable ex = _Jv_CallAnyMethodA (obj, return_type, id, style == constructor, arg_types, args, NULL); if (ex != NULL) env->ex = ex; } template static void _Jv_JNI_CallAnyVoidMethod (JNIEnv *env, jobject obj, jclass klass, jmethodID method, ...) { va_list args; va_start (args, method); _Jv_JNI_CallAnyVoidMethodV