static int pyjobject_init(JNIEnv *env, PyJObject *pyjob) { jobjectArray methodArray = NULL; jobjectArray fieldArray = NULL; int i, len = 0; jobject langClass = NULL; jstring className = NULL; const char *cClassName = NULL; PyObject *pyClassName = NULL; PyObject *pyAttrName = NULL; JepThread *jepThread; PyObject *cachedMethodList = NULL; (*env)->PushLocalFrame(env, 20); // ------------------------------ call Class.getMethods() // well, first call getClass() if (objectGetClass == 0) { objectGetClass = (*env)->GetMethodID(env, pyjob->clazz, "getClass", "()Ljava/lang/Class;"); if (process_java_exception(env) || !objectGetClass) { goto EXIT_ERROR; } } langClass = (*env)->CallObjectMethod(env, pyjob->clazz, objectGetClass); if (process_java_exception(env) || !langClass) { goto EXIT_ERROR; } /* * attach attribute java_name to the pyjobject instance to assist with * understanding the type at runtime */ if (classGetName == 0) { classGetName = (*env)->GetMethodID(env, langClass, "getName", "()Ljava/lang/String;"); } className = (*env)->CallObjectMethod(env, pyjob->clazz, classGetName); cClassName = jstring2char(env, className); pyClassName = PyString_FromString(cClassName); release_utf_char(env, className, cClassName); pyAttrName = PyString_FromString("java_name"); if (PyObject_SetAttr((PyObject *) pyjob, pyAttrName, pyClassName) == -1) { PyErr_Format(PyExc_RuntimeError, "Couldn't add java_name as attribute."); } else { pyjobject_addfield(pyjob, pyAttrName); } pyjob->javaClassName = pyClassName; Py_DECREF(pyAttrName); (*env)->DeleteLocalRef(env, className); // then, get methodid for getMethods() if (classGetMethods == 0) { classGetMethods = (*env)->GetMethodID(env, langClass, "getMethods", "()[Ljava/lang/reflect/Method;"); if (process_java_exception(env) || !classGetMethods) { goto EXIT_ERROR; } } /* * Performance improvement. The code below is very similar to previous * versions except methods are now cached in memory. * * Previously every time you instantiate a pyjobject, JEP would get the * complete list of methods, turn them into pyjmethods, and add them as * attributes to the pyjobject. * * Now JEP retains a python dictionary in memory with a key of the fully * qualified Java classname to a list of pyjmethods. Since the * Java methods will never change at runtime for a particular Class, this * is safe and drastically speeds up pyjobject instantiation by reducing * reflection calls. We continue to set and reuse the pyjmethods as * attributes on the pyjobject instance, but if pyjobject_getattr sees a * pyjmethod, it will put it inside a pymethod and return that, enabling * the reuse of the pyjmethod for this particular object instance. * * We have the GIL at this point, so we can safely assume we're * synchronized and multiple threads will not alter the dictionary at the * same time. */ jepThread = pyembed_get_jepthread(); if (jepThread == NULL) { goto EXIT_ERROR; } if (jepThread->fqnToPyJmethods == NULL) { PyObject *methodCache = PyDict_New(); jepThread->fqnToPyJmethods = methodCache; } cachedMethodList = PyDict_GetItem(jepThread->fqnToPyJmethods, pyClassName); if (cachedMethodList == NULL) { PyObject *pyjMethodList = NULL; pyjMethodList = PyList_New(0); // - GetMethodID fails when you pass the clazz object, it expects // a java.lang.Class jobject. // - if you CallObjectMethod with the langClass jclass object, // it'll return an array of methods, but they're methods of the // java.lang.reflect.Method class -- not ->object. // // so what i did here was find the methodid using langClass, // but then i call the method using clazz. methodIds for java // classes are shared.... methodArray = (jobjectArray) (*env)->CallObjectMethod(env, pyjob->clazz, classGetMethods); if (process_java_exception(env) || !methodArray) { goto EXIT_ERROR; } // for each method, create a new pyjmethod object // and add to the internal methods list. len = (*env)->GetArrayLength(env, methodArray); for (i = 0; i < len; i++) { PyJMethodObject *pymethod = NULL; jobject rmethod = NULL; rmethod = (*env)->GetObjectArrayElement(env, methodArray, i); // make new PyJMethodObject, linked to pyjob if (pyjob->object) { pymethod = pyjmethod_new(env, rmethod, pyjob); } else { pymethod = pyjmethod_new_static(env, rmethod, pyjob); } if (!pymethod) { continue; } if (pymethod->pyMethodName && PyString_Check(pymethod->pyMethodName)) { int multi = 0; Py_ssize_t cacheLen = PyList_Size(pyjMethodList); int cacheIndex = 0; for (cacheIndex = 0; cacheIndex < cacheLen; cacheIndex += 1) { PyObject* cached = PyList_GetItem(pyjMethodList, cacheIndex); if (pyjmethod_check(cached)) { PyJMethodObject* cachedMethod = (PyJMethodObject*) cached; if (PyObject_RichCompareBool(pymethod->pyMethodName, cachedMethod->pyMethodName, Py_EQ)) { PyObject* multimethod = PyJmultiMethod_New((PyObject*) pymethod, cached); PyList_SetItem(pyjMethodList, cacheIndex, multimethod); multi = 1; break; } } else if (PyJmultiMethod_Check(cached)) { PyObject* methodName = PyJmultiMethod_GetName(cached); if (PyObject_RichCompareBool(pymethod->pyMethodName, methodName, Py_EQ)) { Py_DECREF(methodName); PyJmultiMethod_Append(cached, (PyObject*) pymethod); multi = 1; break; } else { Py_DECREF(methodName); } } } if (!multi) { if (PyList_Append(pyjMethodList, (PyObject*) pymethod) != 0) { printf("WARNING: couldn't add method"); } } } Py_DECREF(pymethod); (*env)->DeleteLocalRef(env, rmethod); } // end of looping over available methods PyDict_SetItem(jepThread->fqnToPyJmethods, pyClassName, pyjMethodList); cachedMethodList = pyjMethodList; Py_DECREF(pyjMethodList); // fqnToPyJmethods will hold the reference (*env)->DeleteLocalRef(env, methodArray); } // end of setting up cache for this Java Class len = (int) PyList_Size(cachedMethodList); for (i = 0; i < len; i++) { PyObject* name = NULL; PyObject* cached = PyList_GetItem(cachedMethodList, i); if (pyjmethod_check(cached)) { PyJMethodObject* cachedMethod = (PyJMethodObject*) cached; name = cachedMethod->pyMethodName; Py_INCREF(name); } else if (PyJmultiMethod_Check(cached)) { name = PyJmultiMethod_GetName(cached); } if (name) { if (PyObject_SetAttr((PyObject *) pyjob, name, cached) != 0) { PyErr_SetString(PyExc_RuntimeError, "Couldn't add method as attribute."); } else { pyjobject_addmethod(pyjob, name); } Py_DECREF(name); } } // end of cached method optimizations // ------------------------------ process fields if (classGetFields == 0) { classGetFields = (*env)->GetMethodID(env, langClass, "getFields", "()[Ljava/lang/reflect/Field;"); if (process_java_exception(env) || !classGetFields) { goto EXIT_ERROR; } } fieldArray = (jobjectArray) (*env)->CallObjectMethod(env, pyjob->clazz, classGetFields); if (process_java_exception(env) || !fieldArray) { goto EXIT_ERROR; } // for each field, create a pyjfield object and // add to the internal members list. len = (*env)->GetArrayLength(env, fieldArray); for (i = 0; i < len; i++) { jobject rfield = NULL; PyJFieldObject *pyjfield = NULL; rfield = (*env)->GetObjectArrayElement(env, fieldArray, i); pyjfield = pyjfield_new(env, rfield, pyjob); if (!pyjfield) { continue; } if (pyjfield->pyFieldName && PyString_Check(pyjfield->pyFieldName)) { if (PyObject_SetAttr((PyObject *) pyjob, pyjfield->pyFieldName, (PyObject *) pyjfield) != 0) { printf("WARNING: couldn't add field.\n"); } else { pyjobject_addfield(pyjob, pyjfield->pyFieldName); } } Py_DECREF(pyjfield); (*env)->DeleteLocalRef(env, rfield); } (*env)->DeleteLocalRef(env, fieldArray); // we've finished the object. pyjob->finishAttr = 1; (*env)->PopLocalFrame(env, NULL); return 1; EXIT_ERROR: (*env)->PopLocalFrame(env, NULL); if (PyErr_Occurred()) { // java exceptions translated by this time if (pyjob) { pyjobject_dealloc(pyjob); } } return 0; }
static int pyjobject_init(JNIEnv *env, PyJobject_Object *pyjob) { jobjectArray methodArray = NULL; jobjectArray fieldArray = NULL; int i, len = 0; jobject langClass = NULL; (*env)->PushLocalFrame(env, 20); // ------------------------------ call Class.getMethods() // well, first call getClass() if(objectGetClass == 0) { objectGetClass = (*env)->GetMethodID(env, pyjob->clazz, "getClass", "()Ljava/lang/Class;"); if(process_java_exception(env) || !objectGetClass) goto EXIT_ERROR; } langClass = (*env)->CallObjectMethod(env, pyjob->clazz, objectGetClass); if(process_java_exception(env) || !langClass) goto EXIT_ERROR; // then, get methodid for getMethods() if(classGetMethods == 0) { classGetMethods = (*env)->GetMethodID(env, langClass, "getMethods", "()[Ljava/lang/reflect/Method;"); if(process_java_exception(env) || !classGetMethods) goto EXIT_ERROR; } // - GetMethodID fails when you pass the clazz object, it expects // a java.lang.Class jobject. // - if you CallObjectMethod with the langClass jclass object, // it'll return an array of methods, but they're methods of the // java.lang.reflect.Method class -- not ->object. // // so what i did here was find the methodid using langClass, // but then i call the method using clazz. methodIds for java // classes are shared.... methodArray = (jobjectArray) (*env)->CallObjectMethod(env, pyjob->clazz, classGetMethods); if(process_java_exception(env) || !methodArray) goto EXIT_ERROR; // for each method, create a new pyjmethod object // and add to the internal methods list. len = (*env)->GetArrayLength(env, methodArray); for(i = 0; i < len; i++) { PyJmethod_Object *pymethod = NULL; jobject rmethod = NULL; rmethod = (*env)->GetObjectArrayElement(env, methodArray, i); // make new PyJmethod_Object, linked to pyjob if(pyjob->object) pymethod = pyjmethod_new(env, rmethod, pyjob); else pymethod = pyjmethod_new_static(env, rmethod, pyjob); if(!pymethod) continue; if(pymethod->pyMethodName && PyString_Check(pymethod->pyMethodName)) { if(PyObject_SetAttr((PyObject *) pyjob, pymethod->pyMethodName, (PyObject *) pymethod) != 0) { printf("WARNING: couldn't add method.\n"); } else pyjobject_addmethod(pyjob, pymethod->pyMethodName); } Py_DECREF(pymethod); (*env)->DeleteLocalRef(env, rmethod); } (*env)->DeleteLocalRef(env, methodArray); // ------------------------------ process fields if(classGetFields == 0) { classGetFields = (*env)->GetMethodID(env, langClass, "getFields", "()[Ljava/lang/reflect/Field;"); if(process_java_exception(env) || !classGetFields) goto EXIT_ERROR; } fieldArray = (jobjectArray) (*env)->CallObjectMethod(env, pyjob->clazz, classGetFields); if(process_java_exception(env) || !fieldArray) goto EXIT_ERROR; // for each field, create a pyjfield object and // add to the internal members list. len = (*env)->GetArrayLength(env, fieldArray); for(i = 0; i < len; i++) { jobject rfield = NULL; PyJfield_Object *pyjfield = NULL; rfield = (*env)->GetObjectArrayElement(env, fieldArray, i); pyjfield = pyjfield_new(env, rfield, pyjob); if(!pyjfield) continue; if(pyjfield->pyFieldName && PyString_Check(pyjfield->pyFieldName)) { if(PyObject_SetAttr((PyObject *) pyjob, pyjfield->pyFieldName, (PyObject *) pyjfield) != 0) { printf("WARNING: couldn't add field.\n"); } else pyjobject_addfield(pyjob, pyjfield->pyFieldName); } Py_DECREF(pyjfield); (*env)->DeleteLocalRef(env, rfield); } (*env)->DeleteLocalRef(env, fieldArray); // we've finished the object. pyjob->finishAttr = 1; (*env)->PopLocalFrame(env, NULL); return 1; EXIT_ERROR: (*env)->PopLocalFrame(env, NULL); if(PyErr_Occurred()) { // java exceptions translated by this time if(pyjob) pyjobject_dealloc(pyjob); } return 0; }