// pyjmethod_call_internal. where the magic happens. // // okay, some of the magic -- we already the methodId, so we don't have // to reflect. we just have to parse the arguments from python, // check them against the java args, and call the java function. // // easy. :-) PyObject* pyjmethod_call_internal(PyJmethod_Object *self, PyJobject_Object *instance, PyObject *args) { PyObject *result = NULL; const char *str = NULL; JNIEnv *env = NULL; int pos = 0; jvalue *jargs = NULL; int foundArray = 0; /* if params includes pyjarray instance */ PyThreadState *_save; env = pyembed_get_env(); if(!self->parameters) { if(!pyjmethod_init(env, self) || PyErr_Occurred()) return NULL; return pyjmethod_call_internal(self, instance, args); } // validate we can call this method if(!instance->object && self->isStatic != JNI_TRUE) { PyErr_Format(PyExc_RuntimeError, "Instantiate this class before " "calling an object method."); return NULL; } // shouldn't happen if(self->lenParameters != PyTuple_GET_SIZE(args)) { PyErr_Format(PyExc_RuntimeError, "Invalid number of arguments: %i, expected %i.", (int) PyTuple_GET_SIZE(args), self->lenParameters); return NULL; } jargs = (jvalue *) PyMem_Malloc(sizeof(jvalue) * self->lenParameters); // ------------------------------ build jargs off python values // hopefully 40 local references are enough per method call (*env)->PushLocalFrame(env, 40); for(pos = 0; pos < self->lenParameters; pos++) { PyObject *param = NULL; int paramTypeId = -1; jclass paramType = (jclass) (*env)->GetObjectArrayElement(env, self->parameters, pos); param = PyTuple_GetItem(args, pos); /* borrowed */ if(PyErr_Occurred()) { /* borrowed */ goto EXIT_ERROR; } paramTypeId = get_jtype(env, paramType); if(paramTypeId == JARRAY_ID) foundArray = 1; jargs[pos] = convert_pyarg_jvalue(env, param, paramType, paramTypeId, pos); if(PyErr_Occurred()) { /* borrowed */ goto EXIT_ERROR; } (*env)->DeleteLocalRef(env, paramType); } // for parameters // ------------------------------ call based off return type switch(self->returnTypeId) { case JSTRING_ID: { jstring jstr; Py_UNBLOCK_THREADS; if(self->isStatic) jstr = (jstring) (*env)->CallStaticObjectMethodA( env, instance->clazz, self->methodId, jargs); else { // not static, a method on class then. if(!instance->object) jstr = (jstring) (*env)->CallObjectMethodA( env, instance->clazz, self->methodId, jargs); else jstr = (jstring) (*env)->CallObjectMethodA( env, instance->object, self->methodId, jargs); } Py_BLOCK_THREADS; if(!process_java_exception(env) && jstr != NULL) { str = (*env)->GetStringUTFChars(env, jstr, 0); result = PyString_FromString(str); (*env)->ReleaseStringUTFChars(env, jstr, str); (*env)->DeleteLocalRef(env, jstr); } break; } case JARRAY_ID: { jobjectArray obj; Py_UNBLOCK_THREADS; if(self->isStatic) obj = (jobjectArray) (*env)->CallStaticObjectMethodA( env, instance->clazz, self->methodId, jargs); else { if(!instance->object) obj = (jobjectArray) (*env)->CallObjectMethodA( env, instance->clazz, self->methodId, jargs); else obj = (jobjectArray) (*env)->CallObjectMethodA( env, instance->object, self->methodId, jargs); } Py_BLOCK_THREADS; if(!process_java_exception(env) && obj != NULL) result = pyjarray_new(env, obj); break; } case JCLASS_ID: { jobject obj; Py_UNBLOCK_THREADS; if(self->isStatic) obj = (*env)->CallStaticObjectMethodA( env, instance->clazz, self->methodId, jargs); else { if(!instance->object) obj = (*env)->CallObjectMethodA(env, instance->clazz, self->methodId, jargs); else obj = (*env)->CallObjectMethodA(env, instance->object, self->methodId, jargs); } Py_BLOCK_THREADS; if(!process_java_exception(env) && obj != NULL) result = pyjobject_new_class(env, obj); break; } case JOBJECT_ID: { jobject obj; Py_UNBLOCK_THREADS; if(self->isStatic) obj = (*env)->CallStaticObjectMethodA( env, instance->clazz, self->methodId, jargs); else { if(!instance->object) obj = (*env)->CallObjectMethodA(env, instance->clazz, self->methodId, jargs); else obj = (*env)->CallObjectMethodA(env, instance->object, self->methodId, jargs); } Py_BLOCK_THREADS; if(!process_java_exception(env) && obj != NULL) { result = pyjobject_new(env, obj); } break; } case JINT_ID: { jint ret; Py_UNBLOCK_THREADS; if(self->isStatic) ret = (*env)->CallStaticIntMethodA( env, instance->clazz, self->methodId, jargs); else { if(!instance->object) ret = (*env)->CallIntMethodA(env, instance->clazz, self->methodId, jargs); else ret = (*env)->CallIntMethodA(env, instance->object, self->methodId, jargs); } Py_BLOCK_THREADS; if(!process_java_exception(env)) result = Py_BuildValue("i", ret); break; } case JBYTE_ID: { jbyte ret; Py_UNBLOCK_THREADS; if(self->isStatic) ret = (*env)->CallStaticByteMethodA( env, instance->clazz, self->methodId, jargs); else { if(!instance->object) ret = (*env)->CallByteMethodA(env, instance->clazz, self->methodId, jargs); else ret = (*env)->CallByteMethodA(env, instance->object, self->methodId, jargs); } Py_BLOCK_THREADS; if(!process_java_exception(env)) result = Py_BuildValue("i", ret); break; } case JCHAR_ID: { jchar ret; char val[2]; Py_UNBLOCK_THREADS; if(self->isStatic) ret = (*env)->CallStaticCharMethodA( env, instance->clazz, self->methodId, jargs); else { if(!instance->object) ret = (*env)->CallCharMethodA(env, instance->clazz, self->methodId, jargs); else ret = (*env)->CallCharMethodA(env, instance->object, self->methodId, jargs); } Py_BLOCK_THREADS; if(!process_java_exception(env)) { val[0] = (char) ret; val[1] = '\0'; result = PyString_FromString(val); } break; } case JSHORT_ID: { jshort ret; Py_UNBLOCK_THREADS; if(self->isStatic) ret = (*env)->CallStaticShortMethodA( env, instance->clazz, self->methodId, jargs); else { if(!instance->object) ret = (*env)->CallShortMethodA(env, instance->clazz, self->methodId, jargs); else ret = (*env)->CallShortMethodA(env, instance->object, self->methodId, jargs); } Py_BLOCK_THREADS; if(!process_java_exception(env)) result = Py_BuildValue("i", (int) ret); break; } case JDOUBLE_ID: { jdouble ret; Py_UNBLOCK_THREADS; if(self->isStatic) ret = (*env)->CallStaticDoubleMethodA( env, instance->clazz, self->methodId, jargs); else { if(!instance->object) ret = (*env)->CallDoubleMethodA(env, instance->clazz, self->methodId, jargs); else ret = (*env)->CallDoubleMethodA(env, instance->object, self->methodId, jargs); } Py_BLOCK_THREADS; if(!process_java_exception(env)) result = PyFloat_FromDouble(ret); break; } case JFLOAT_ID: { jfloat ret; Py_UNBLOCK_THREADS; if(self->isStatic) ret = (*env)->CallStaticFloatMethodA( env, instance->clazz, self->methodId, jargs); else { if(!instance->object) ret = (*env)->CallFloatMethodA(env, instance->clazz, self->methodId, jargs); else ret = (*env)->CallFloatMethodA(env, instance->object, self->methodId, jargs); } Py_BLOCK_THREADS; if(!process_java_exception(env)) result = PyFloat_FromDouble((double) ret); break; } case JLONG_ID: { jlong ret; Py_UNBLOCK_THREADS; if(self->isStatic) ret = (*env)->CallStaticLongMethodA( env, instance->clazz, self->methodId, jargs); else { if(!instance->object) ret = (*env)->CallLongMethodA(env, instance->clazz, self->methodId, jargs); else ret = (*env)->CallLongMethodA(env, instance->object, self->methodId, jargs); } Py_BLOCK_THREADS; if(!process_java_exception(env)) result = PyLong_FromLongLong(ret); break; } case JBOOLEAN_ID: { jboolean ret; Py_UNBLOCK_THREADS; if(self->isStatic) ret = (*env)->CallStaticBooleanMethodA( env, instance->clazz, self->methodId, jargs); else { if(!instance->object) ret = (*env)->CallBooleanMethodA(env, instance->clazz, self->methodId, jargs); else ret = (*env)->CallBooleanMethodA(env, instance->object, self->methodId, jargs); } Py_BLOCK_THREADS; if(!process_java_exception(env)) result = Py_BuildValue("i", ret); break; } default: Py_UNBLOCK_THREADS; // i hereby anoint thee a void method if(self->isStatic) (*env)->CallStaticVoidMethodA(env, instance->clazz, self->methodId, jargs); else (*env)->CallVoidMethodA(env, instance->object, self->methodId, jargs); Py_BLOCK_THREADS; process_java_exception(env); break; } PyMem_Free(jargs); (*env)->PopLocalFrame(env, NULL); if(PyErr_Occurred()) return NULL; // re pin array objects if needed if(foundArray) { for(pos = 0; pos < self->lenParameters; pos++) { PyObject *param = PyTuple_GetItem(args, pos); /* borrowed */ if(param && pyjarray_check(param)) pyjarray_pin((PyJarray_Object *) param); } } if(result == NULL) { Py_RETURN_NONE; } return result; EXIT_ERROR: PyMem_Free(jargs); (*env)->PopLocalFrame(env, NULL); return NULL; }
// 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; }