Example #1
0
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;
}
Example #2
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;
}