Пример #1
0
// get attribute 'name' for object.
// uses obj->attr list of tuples for storage.
// returns new reference.
PyObject* pyjobject_getattr(PyJObject *obj,
                            char *name)
{
    PyObject *ret, *pyname, *methods, *members;
    ret = pyname = methods = members = NULL;

    if (!name) {
        Py_RETURN_NONE;
    }
    pyname  = PyString_FromString(name);
    methods = PyString_FromString("__methods__");
    members = PyString_FromString("__members__");

    if (PyObject_RichCompareBool(pyname, methods, Py_EQ)) {
        Py_DECREF(pyname);
        Py_DECREF(methods);
        Py_DECREF(members);

        Py_INCREF(obj->methods);
        return obj->methods;
    }
    Py_DECREF(methods);

    if (PyObject_RichCompareBool(pyname, members, Py_EQ)) {
        Py_DECREF(pyname);
        Py_DECREF(members);

        Py_INCREF(obj->fields);
        return obj->fields;
    }
    Py_DECREF(members);

    if (!PyList_Check(obj->attr)) {
        Py_DECREF(pyname);
        PyErr_Format(PyExc_RuntimeError, "Invalid attr list.");
        return NULL;
    }

    // util function fetches from attr list for us.
    ret = tuplelist_getitem(obj->attr, pyname);      /* new reference */
    Py_DECREF(pyname);

    // method optimizations
    if (pyjmethod_check(ret) || PyJmultiMethod_Check(ret)) {
        /*
         * TODO Should not bind non-static methods to pyjclass objects, but not
         * sure yet how to handle multimethods and static methods.
         */
#if PY_MAJOR_VERSION >= 3
        PyObject* wrapper = PyMethod_New(ret, (PyObject*) obj);
#else
        PyObject* wrapper = PyMethod_New(ret, (PyObject*) obj,
                                         (PyObject*) Py_TYPE(obj));
#endif
        Py_DECREF(ret);
        ret = wrapper;
    }

    if (PyErr_Occurred() || ret == Py_None) {
        if (ret == Py_None) {
            Py_DECREF(Py_None);
        }
        PyErr_Format(PyExc_AttributeError, "attr not found: %s", name);
        return NULL;
    }

    if (pyjfield_check(ret)) {
        PyObject *t = pyjfield_get((PyJFieldObject *) ret);
        Py_DECREF(ret);
        return t;
    }

    return ret;
}
Пример #2
0
// get attribute 'name' for object.
// uses obj->attr list of tuples for storage.
// returns new reference.
static PyObject* pyjobject_getattr(PyJobject_Object *obj,
                                   char *name) {
    PyObject *ret, *pyname, *methods, *members;
    int       listSize, i, found;
    
    ret = pyname = methods = members = NULL;
    listSize = i = found = 0;
    
    if(!name) {
        Py_INCREF(Py_None);
        return Py_None;
    }
    pyname  = PyString_FromString(name);
    methods = PyString_FromString("__methods__");
    members = PyString_FromString("__members__");
    
    if(PyObject_Compare(pyname, methods) == 0) {
        Py_DECREF(pyname);
        Py_DECREF(methods);
        Py_DECREF(members);

        Py_INCREF(obj->methods);
        return obj->methods;
    }
    Py_DECREF(methods);
    
    if(PyObject_Compare(pyname, members) == 0) {
        Py_DECREF(pyname);
        Py_DECREF(members);
        
        Py_INCREF(obj->fields);
        return obj->fields;
    }
    Py_DECREF(members);
    
    if(!PyList_Check(obj->attr)) {
        Py_DECREF(pyname);
        PyErr_Format(PyExc_RuntimeError, "Invalid attr list.");
        return NULL;
    }
    
    // util function fetches from attr list for us.
    ret = tuplelist_getitem(obj->attr, pyname);      /* new reference */
    
    Py_DECREF(pyname);
    
    if(PyErr_Occurred() || ret == Py_None) {
        if(ret == Py_None)
            Py_DECREF(Py_None);
        PyErr_Format(PyExc_AttributeError, "attr not found: %s", name);
        return NULL;
    }
    
    if(pyjfield_check(ret)) {
        PyObject *t = pyjfield_get((PyJfield_Object *) ret);
        Py_DECREF(ret);
        return t;
    }
    if(pyjmethod_check(ret))
        Py_INCREF(obj);
    
    return ret;
}
Пример #3
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;
}
Пример #4
0
// find and call a method on this object that matches the python args.
// typically called by way of pyjmethod when python invokes __call__.
//
// steals reference to self, methodname and args.
PyObject* find_method(JNIEnv *env,
                      PyObject *methodName,
                      Py_ssize_t methodCount,
                      PyObject *attr,
                      PyObject *args) {
    // all possible method candidates
    PyJmethod_Object **cand = NULL;
    Py_ssize_t         pos, i, listSize, argsSize;
    
    pos = i = listSize = argsSize = 0;

    // not really likely if we were called from pyjmethod, but hey...
    if(methodCount < 1) {
        PyErr_Format(PyExc_RuntimeError, "I have no methods.");
        return NULL;
    }

    if(!attr || !PyList_CheckExact(attr)) {
        PyErr_Format(PyExc_RuntimeError, "Invalid attr list.");
        return NULL;
    }
    
    cand = (PyJmethod_Object **)
        PyMem_Malloc(sizeof(PyJmethod_Object*) * methodCount);
    
    // just for safety
    for(i = 0; i < methodCount; i++)
        cand[i] = NULL;
    
    listSize = PyList_GET_SIZE(attr);
    for(i = 0; i < listSize; i++) {
        PyObject *tuple = PyList_GetItem(attr, i);               /* borrowed */

        if(PyErr_Occurred())
            break;
        
        if(!tuple || tuple == Py_None || !PyTuple_CheckExact(tuple))
            continue;

        if(PyTuple_Size(tuple) == 2) {
            PyObject *key = PyTuple_GetItem(tuple, 0);           /* borrowed */
            
            if(PyErr_Occurred())
                break;
            
            if(!key || !PyString_Check(key))
                continue;
            
            if(PyObject_Compare(key, methodName) == 0) {
                PyObject *method = PyTuple_GetItem(tuple, 1);    /* borrowed */
                if(pyjmethod_check(method))
                    cand[pos++] = (PyJmethod_Object *) method;
            }
        }
    }
    
    if(PyErr_Occurred())
        goto EXIT_ERROR;
    
    // makes more sense to work with...
    pos--;
    
    if(pos < 0) {
        // didn't find a method by that name....
        // that shouldn't happen unless the search above is broken.
        PyErr_Format(PyExc_NameError, "No such method.");
        goto EXIT_ERROR;
    }
    if(pos == 0) {
        // we're done, call that one
        PyObject *ret = pyjmethod_call_internal(cand[0], args);
        PyMem_Free(cand);
        return ret;
    }

    // first, find out if there's only one method that
    // has the correct number of args
    argsSize = PyTuple_Size(args);
    {
        PyJmethod_Object *matching = NULL;
        int               count    = 0;
        
        for(i = 0; i <= pos && cand[i]; i++) {
            // make sure method is fully initialized
            if(!cand[i]->parameters) {
                if(!pyjmethod_init(env, cand[i])) {
                    // init failed, that's not good.
                    cand[i] = NULL;
                    PyErr_Warn(PyExc_Warning, "pyjmethod init failed.");
                    continue;
                }
            }

            if(cand[i]->lenParameters == argsSize) {
                matching = cand[i];
                count++;
            }
            else
                cand[i] = NULL; // eliminate non-matching
        }
        
        if(matching && count == 1) {
            PyMem_Free(cand);
            return pyjmethod_call_internal(matching, args);
        }
    } // local scope
    
    for(i = 0; i <= pos; i++) {
        int parmpos = 0;
        
        // already eliminated?
        if(!cand[i])
            continue;
        
        // check if argument types match
        (*env)->PushLocalFrame(env, 20);
        for(parmpos = 0; parmpos < cand[i]->lenParameters; parmpos++) {
            PyObject *param       = PyTuple_GetItem(args, parmpos);
            int       paramTypeId = -1;
            jclass    pclazz;
            jclass    paramType =
                (jclass) (*env)->GetObjectArrayElement(env,
                                                       cand[i]->parameters,
                                                       parmpos);

            if(process_java_exception(env) || !paramType)
                break;
            
            pclazz = (*env)->GetObjectClass(env, paramType);
            if(process_java_exception(env) || !pclazz)
                break;
            
            paramTypeId = get_jtype(env, paramType, pclazz);
            
            if(pyarg_matches_jtype(env, param, paramType, paramTypeId)) {
                if(PyErr_Occurred())
                    break;
                continue;
            }
            
            // args don't match
            break;
        }
        (*env)->PopLocalFrame(env, NULL);
        
        // this method matches?
        if(parmpos == cand[i]->lenParameters) {
            PyObject *ret = pyjmethod_call_internal(cand[i], args);
            PyMem_Free(cand);
            return ret;
        }
    }


EXIT_ERROR:
    PyMem_Free(cand);
    if(!PyErr_Occurred())
        PyErr_Format(PyExc_NameError,
                     "Matching overloaded method not found.");
    return NULL;
}