static PyObject* pyjconstructor_call(PyJMethodObject *self, PyObject *args, PyObject *keywords) { PyObject *firstArg = NULL; PyJObject *clazz = NULL; JNIEnv *env = NULL; int pos = 0; jvalue *jargs = NULL; int foundArray = 0; /* if params includes pyjarray instance */ PyThreadState *_save = NULL; jobject obj = NULL; PyObject *pobj = NULL; if (keywords != NULL) { PyErr_Format(PyExc_TypeError, "Keywords are not supported."); return NULL; } if (self->lenParameters != PyTuple_GET_SIZE(args) - 1) { PyErr_Format(PyExc_RuntimeError, "Invalid number of arguments: %i, expected %i.", (int) PyTuple_GET_SIZE(args), self->lenParameters + 1); return NULL; } firstArg = PyTuple_GetItem(args, 0); if (!pyjclass_check(firstArg)) { PyErr_SetString(PyExc_RuntimeError, "First argument to a java constructor must be a java class."); return NULL; } clazz = (PyJObject*) firstArg; // ------------------------------ build jargs off python values env = pyembed_get_env(); if ((*env)->PushLocalFrame(env, JLOCAL_REFS + self->lenParameters) != 0) { process_java_exception(env); return NULL; } jargs = (jvalue *) PyMem_Malloc(sizeof(jvalue) * self->lenParameters); 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 + 1); /* borrowed */ if (PyErr_Occurred()) { 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()) { goto EXIT_ERROR; } (*env)->DeleteLocalRef(env, paramType); } Py_UNBLOCK_THREADS; obj = (*env)->NewObjectA(env, clazz->clazz, self->methodId, jargs); Py_BLOCK_THREADS; if (process_java_exception(env) || !obj) { goto EXIT_ERROR; } // finally, make pyjobject and return pobj = pyjobject_new(env, obj); // we already closed the local frame, so make // sure to delete this local ref. PyMem_Free(jargs); // re pin array if needed if (foundArray) { for (pos = 0; pos < self->lenParameters; pos++) { PyObject *param = PyTuple_GetItem(args, pos); if (param && pyjarray_check(param)) { pyjarray_pin((PyJArrayObject *) param); } } } (*env)->PopLocalFrame(env, NULL); return pobj; EXIT_ERROR: PyMem_Free(jargs); (*env)->PopLocalFrame(env, NULL); return NULL; }
// 1 if successful, 0 if failed. int pyjmethod_init(JNIEnv *env, PyJmethod_Object *self) { jmethodID methodId; jobject returnType = NULL; jobjectArray paramArray = NULL; jclass modClass = NULL; jint modifier = -1; jboolean isStatic = JNI_FALSE; jclass rmethodClass = NULL; // use a local frame so we don't have to worry too much about local refs. // make sure if this method errors out, that this is poped off again (*env)->PushLocalFrame(env, 20); if(process_java_exception(env)) return 0; rmethodClass = (*env)->GetObjectClass(env, self->rmethod); if(process_java_exception(env) || !rmethodClass) goto EXIT_ERROR; // ------------------------------ get methodid methodId = (*env)->FromReflectedMethod(env, self->rmethod); if(process_java_exception(env) || !methodId) goto EXIT_ERROR; self->methodId = methodId; // ------------------------------ get return type if(methodGetType == 0) { methodGetType = (*env)->GetMethodID(env, rmethodClass, "getReturnType", "()Ljava/lang/Class;"); if(process_java_exception(env) || !methodGetType) goto EXIT_ERROR; } returnType = (*env)->CallObjectMethod(env, self->rmethod, methodGetType); if(process_java_exception(env) || !returnType) goto EXIT_ERROR; self->returnTypeId = get_jtype(env, returnType); if(process_java_exception(env)) goto EXIT_ERROR; // ------------------------------ get parameter array if(methodGetParmTypes == 0) { methodGetParmTypes = (*env)->GetMethodID(env, rmethodClass, "getParameterTypes", "()[Ljava/lang/Class;"); if(process_java_exception(env) || !methodGetParmTypes) goto EXIT_ERROR; } paramArray = (jobjectArray) (*env)->CallObjectMethod(env, self->rmethod, methodGetParmTypes); if(process_java_exception(env) || !paramArray) goto EXIT_ERROR; self->parameters = (*env)->NewGlobalRef(env, paramArray); self->lenParameters = (*env)->GetArrayLength(env, paramArray); // ------------------------------ get isStatic if(self->isStatic != 1) { // may already know that // call getModifers() if(methodGetModifiers == 0) { methodGetModifiers = (*env)->GetMethodID(env, rmethodClass, "getModifiers", "()I"); if(process_java_exception(env) || !methodGetModifiers) goto EXIT_ERROR; } modifier = (*env)->CallIntMethod(env, self->rmethod, methodGetModifiers); if(process_java_exception(env) || !modifier) goto EXIT_ERROR; modClass = (*env)->FindClass(env, "java/lang/reflect/Modifier"); if(process_java_exception(env) || !modClass) goto EXIT_ERROR; // caching this methodid caused a crash on the mac methodId = (*env)->GetStaticMethodID(env, modClass, "isStatic", "(I)Z"); if(process_java_exception(env) || !methodId) goto EXIT_ERROR; isStatic = (*env)->CallStaticBooleanMethod(env, modClass, methodId, modifier); if(process_java_exception(env)) goto EXIT_ERROR; if(isStatic == JNI_TRUE) self->isStatic = 1; else self->isStatic = 0; } // is static (*env)->PopLocalFrame(env, NULL); return 1; EXIT_ERROR: (*env)->PopLocalFrame(env, NULL); if(!PyErr_Occurred()) PyErr_SetString(PyExc_RuntimeError, "Unknown"); return -1; }
// 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; }
// called internally to make new PyJObject instances PyObject* pyjobject_new(JNIEnv *env, jobject obj) { PyJObject *pyjob; jclass objClz; int jtype; if (!subtypes_initialized) { pyjobject_init_subtypes(); } if (!obj) { PyErr_Format(PyExc_RuntimeError, "Invalid object."); return NULL; } objClz = (*env)->GetObjectClass(env, obj); /* * There exist situations where a Java method signature has a return * type of Object but actually returns a Class or array. Also if you * call Jep.set(String, Object[]) it should be treated as an array, not * an object. Hence this check here to build the optimal jep type in * the interpreter regardless of signature. */ jtype = get_jtype(env, objClz); if (jtype == JARRAY_ID) { return pyjarray_new(env, obj); } else if (jtype == JCLASS_ID) { return pyjobject_new_class(env, obj); } else { #if JEP_NUMPY_ENABLED /* * check for jep/NDArray and autoconvert to numpy.ndarray instead of * pyjobject */ if (jndarray_check(env, obj)) { return convert_jndarray_pyndarray(env, obj); } if (PyErr_Occurred()) { return NULL; } #endif // check for some of our extensions to pyjobject if ((*env)->IsInstanceOf(env, obj, JITERABLE_TYPE)) { if ((*env)->IsInstanceOf(env, obj, JCOLLECTION_TYPE)) { if ((*env)->IsInstanceOf(env, obj, JLIST_TYPE)) { pyjob = (PyJObject*) pyjlist_new(); } else { // a Collection we have less support for pyjob = (PyJObject*) pyjcollection_new(); } } else { // an Iterable we have less support for pyjob = (PyJObject*) pyjiterable_new(); } } else if ((*env)->IsInstanceOf(env, obj, JMAP_TYPE)) { pyjob = (PyJObject*) pyjmap_new(); } else if ((*env)->IsInstanceOf(env, obj, JITERATOR_TYPE)) { pyjob = (PyJObject*) pyjiterator_new(); } else if ((*env)->IsInstanceOf(env, obj, JNUMBER_TYPE)) { pyjob = (PyJObject*) pyjnumber_new(); } else { pyjob = PyObject_NEW(PyJObject, &PyJObject_Type); } } pyjob->object = (*env)->NewGlobalRef(env, obj); pyjob->clazz = (*env)->NewGlobalRef(env, (*env)->GetObjectClass(env, obj)); pyjob->attr = PyList_New(0); pyjob->methods = PyList_New(0); pyjob->fields = PyList_New(0); pyjob->finishAttr = 0; if (pyjobject_init(env, pyjob)) { return (PyObject *) pyjob; } return NULL; }
static int pyjfield_init(JNIEnv *env, PyJfield_Object *self) { jfieldID fieldId; jclass rfieldClass = NULL; jobject fieldType = NULL; jclass modClass = NULL; jint modifier = -1; jboolean isStatic = JNI_TRUE; // use a local frame so we don't have to worry too much about local refs. // make sure if this method errors out, that this is poped off again (*env)->PushLocalFrame(env, 20); if(process_java_exception(env)) return 0; rfieldClass = (*env)->GetObjectClass(env, self->rfield); if(process_java_exception(env) || !rfieldClass) goto EXIT_ERROR; // ------------------------------ get fieldid fieldId = (*env)->FromReflectedField(env, self->rfield); if(process_java_exception(env) || !fieldId) goto EXIT_ERROR; self->fieldId = fieldId; // ------------------------------ get return type if(classGetType == 0) { classGetType = (*env)->GetMethodID(env, rfieldClass, "getType", "()Ljava/lang/Class;"); if(process_java_exception(env) || !classGetType) goto EXIT_ERROR; } fieldType = (*env)->CallObjectMethod(env, self->rfield, classGetType); if(process_java_exception(env) || !fieldType) goto EXIT_ERROR; { self->fieldTypeId = get_jtype(env, fieldType); if(process_java_exception(env)) goto EXIT_ERROR; } // ------------------------------ get isStatic // call getModifers() if(classGetMod == 0) { classGetMod = (*env)->GetMethodID(env, rfieldClass, "getModifiers", "()I"); if(process_java_exception(env) || !classGetMod) goto EXIT_ERROR; } modifier = (*env)->CallIntMethod(env, self->rfield, classGetMod); if(process_java_exception(env)) goto EXIT_ERROR; modClass = (*env)->FindClass(env, "java/lang/reflect/Modifier"); if(process_java_exception(env) || !modClass) goto EXIT_ERROR; if(modIsStatic == 0) { modIsStatic = (*env)->GetStaticMethodID(env, modClass, "isStatic", "(I)Z"); if(process_java_exception(env) || !modIsStatic) goto EXIT_ERROR; } isStatic = (*env)->CallStaticBooleanMethod(env, modClass, modIsStatic, modifier); if(process_java_exception(env)) goto EXIT_ERROR; if(!self->pyjobject->object && !isStatic) { PyErr_SetString(PyExc_TypeError, "Field is not static."); goto EXIT_ERROR; } if(isStatic == JNI_TRUE) self->isStatic = 1; else self->isStatic = 0; (*env)->PopLocalFrame(env, NULL); self->init = 1; return 1; EXIT_ERROR: (*env)->PopLocalFrame(env, NULL); if(!PyErr_Occurred()) PyErr_SetString(PyExc_RuntimeError, "Unknown"); return 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; }
callback* create_callback(JNIEnv* env, jobject obj, jobject method, jobjectArray param_types, jclass return_type, callconv_t calling_convention, jboolean direct) { callback* cb; ffi_abi abi = FFI_DEFAULT_ABI; ffi_abi java_abi = FFI_DEFAULT_ABI; ffi_type* ffi_rtype; ffi_status status; jsize argc; JavaVM* vm; int rtype; char msg[64]; int i; int cvt = 0; const char* throw_type = NULL; const char* throw_msg = NULL; if ((*env)->GetJavaVM(env, &vm) != JNI_OK) { throwByName(env, EUnsatisfiedLink, "Can't get Java VM"); return NULL; } argc = (*env)->GetArrayLength(env, param_types); cb = (callback *)malloc(sizeof(callback)); cb->closure = ffi_closure_alloc(sizeof(ffi_closure), &cb->x_closure); cb->object = (*env)->NewWeakGlobalRef(env, obj); cb->methodID = (*env)->FromReflectedMethod(env, method); cb->vm = vm; cb->arg_types = (ffi_type**)malloc(sizeof(ffi_type*) * argc); cb->java_arg_types = (ffi_type**)malloc(sizeof(ffi_type*) * (argc + 3)); cb->arg_jtypes = (char*)malloc(sizeof(char) * argc); cb->flags = (int *)malloc(sizeof(int) * argc); cb->rflag = CVT_DEFAULT; cb->arg_classes = (jobject*)malloc(sizeof(jobject) * argc); cb->direct = direct; cb->java_arg_types[0] = cb->java_arg_types[1] = cb->java_arg_types[2] = &ffi_type_pointer; for (i=0;i < argc;i++) { int jtype; jclass cls = (*env)->GetObjectArrayElement(env, param_types, i); if ((cb->flags[i] = get_conversion_flag(env, cls)) != CVT_DEFAULT) { cb->arg_classes[i] = (*env)->NewWeakGlobalRef(env, cls); cvt = 1; } jtype = get_jtype(env, cls); if (jtype == -1) { snprintf(msg, sizeof(msg), "Unsupported argument at index %d", i); throw_type = EIllegalArgument; throw_msg = msg; goto failure_cleanup; } cb->arg_jtypes[i] = (char)jtype; cb->java_arg_types[i+3] = cb->arg_types[i] = get_ffi_type(env, cls, cb->arg_jtypes[i]); if (cb->flags[i] == CVT_NATIVE_MAPPED || cb->flags[i] == CVT_POINTER_TYPE || cb->flags[i] == CVT_INTEGER_TYPE) { jclass ncls; ncls = getNativeType(env, cls); jtype = get_jtype(env, ncls); if (jtype == -1) { snprintf(msg, sizeof(msg), "Unsupported NativeMapped argument native type at argument %d", i); throw_type = EIllegalArgument; throw_msg = msg; goto failure_cleanup; } cb->arg_jtypes[i] = (char)jtype; cb->java_arg_types[i+3] = &ffi_type_pointer; cb->arg_types[i] = get_ffi_type(env, ncls, cb->arg_jtypes[i]); } if (cb->arg_types[i]->type == FFI_TYPE_FLOAT) { // Java method is varargs, so promote floats to double cb->java_arg_types[i+3] = &ffi_type_double; cb->flags[i] = CVT_FLOAT; cvt = 1; } else if (cb->java_arg_types[i+3]->type == FFI_TYPE_STRUCT) { // All callback structure arguments are passed as a jobject cb->java_arg_types[i+3] = &ffi_type_pointer; } } if (!direct || !cvt) { free(cb->flags); cb->flags = NULL; free(cb->arg_classes); cb->arg_classes = NULL; } if (direct) { cb->rflag = get_conversion_flag(env, return_type); if (cb->rflag == CVT_NATIVE_MAPPED || cb->rflag == CVT_INTEGER_TYPE || cb->rflag == CVT_POINTER_TYPE) { return_type = getNativeType(env, return_type); } } #if defined(_WIN32) && !defined(_WIN64) if (calling_convention == CALLCONV_STDCALL) { abi = FFI_STDCALL; } java_abi = FFI_STDCALL; #endif // _WIN32 rtype = get_jtype(env, return_type); if (rtype == -1) { throw_type = EIllegalArgument; throw_msg = "Unsupported return type"; goto failure_cleanup; } ffi_rtype = get_ffi_rtype(env, return_type, (char)rtype); if (!ffi_rtype) { throw_type = EIllegalArgument; throw_msg = "Error in return type"; goto failure_cleanup; } status = ffi_prep_cif(&cb->cif, abi, argc, ffi_rtype, cb->arg_types); if (!ffi_error(env, "callback setup", status)) { ffi_type* java_ffi_rtype = ffi_rtype; if (cb->rflag == CVT_STRUCTURE_BYVAL || cb->rflag == CVT_NATIVE_MAPPED || cb->rflag == CVT_POINTER_TYPE || cb->rflag == CVT_INTEGER_TYPE) { // Java method returns a jobject, not a struct java_ffi_rtype = &ffi_type_pointer; rtype = '*'; } switch(rtype) { case 'V': cb->fptr = (*env)->CallVoidMethod; break; case 'Z': cb->fptr = (*env)->CallBooleanMethod; break; case 'B': cb->fptr = (*env)->CallByteMethod; break; case 'S': cb->fptr = (*env)->CallShortMethod; break; case 'C': cb->fptr = (*env)->CallCharMethod; break; case 'I': cb->fptr = (*env)->CallIntMethod; break; case 'J': cb->fptr = (*env)->CallLongMethod; break; case 'F': cb->fptr = (*env)->CallFloatMethod; break; case 'D': cb->fptr = (*env)->CallDoubleMethod; break; default: cb->fptr = (*env)->CallObjectMethod; break; } status = ffi_prep_cif(&cb->java_cif, java_abi, argc+3, java_ffi_rtype, cb->java_arg_types); if (!ffi_error(env, "callback setup (2)", status)) { ffi_prep_closure_loc(cb->closure, &cb->cif, callback_dispatch, cb, cb->x_closure); return cb; } } failure_cleanup: free_callback(env, cb); if (throw_type) { throwByName(env, throw_type, msg); } return NULL; }
callback* create_callback(JNIEnv* env, jobject obj, jobject method, jobjectArray param_types, jclass return_type, callconv_t calling_convention, jint options, jstring encoding) { jboolean direct = options & CB_OPTION_DIRECT; jboolean in_dll = options & CB_OPTION_IN_DLL; callback* cb; ffi_abi abi = FFI_DEFAULT_ABI; ffi_abi java_abi = FFI_DEFAULT_ABI; ffi_type* ffi_rtype; ffi_status status; jsize argc; JavaVM* vm; int rtype; char msg[MSG_SIZE]; int i; int cvt = 0; const char* throw_type = NULL; const char* throw_msg = NULL; if ((*env)->GetJavaVM(env, &vm) != JNI_OK) { throwByName(env, EUnsatisfiedLink, "Can't get Java VM to create native callback"); return NULL; } argc = (*env)->GetArrayLength(env, param_types); cb = (callback *)malloc(sizeof(callback)); cb->closure = ffi_closure_alloc(sizeof(ffi_closure), &cb->x_closure); cb->saved_x_closure = cb->x_closure; cb->object = (*env)->NewWeakGlobalRef(env, obj); cb->methodID = (*env)->FromReflectedMethod(env, method); cb->vm = vm; cb->arg_types = (ffi_type**)malloc(sizeof(ffi_type*) * argc); cb->java_arg_types = (ffi_type**)malloc(sizeof(ffi_type*) * (argc + 3)); cb->arg_jtypes = (char*)malloc(sizeof(char) * argc); cb->conversion_flags = (int *)malloc(sizeof(int) * argc); cb->rflag = CVT_DEFAULT; cb->arg_classes = (jobject*)malloc(sizeof(jobject) * argc); cb->direct = direct; cb->java_arg_types[0] = cb->java_arg_types[1] = cb->java_arg_types[2] = &ffi_type_pointer; cb->encoding = newCStringUTF8(env, encoding); for (i=0;i < argc;i++) { int jtype; jclass cls = (*env)->GetObjectArrayElement(env, param_types, i); if ((cb->conversion_flags[i] = get_conversion_flag(env, cls)) != CVT_DEFAULT) { cb->arg_classes[i] = (*env)->NewWeakGlobalRef(env, cls); cvt = 1; } else { cb->arg_classes[i] = NULL; } jtype = get_jtype(env, cls); if (jtype == -1) { snprintf(msg, sizeof(msg), "Unsupported callback argument at index %d", i); throw_type = EIllegalArgument; throw_msg = msg; goto failure_cleanup; } cb->arg_jtypes[i] = (char)jtype; cb->java_arg_types[i+3] = cb->arg_types[i] = get_ffi_type(env, cls, cb->arg_jtypes[i]); if (!cb->java_arg_types[i+3]) { goto failure_cleanup; } if (cb->conversion_flags[i] == CVT_NATIVE_MAPPED || cb->conversion_flags[i] == CVT_POINTER_TYPE || cb->conversion_flags[i] == CVT_INTEGER_TYPE) { jclass ncls; ncls = getNativeType(env, cls); jtype = get_jtype(env, ncls); if (jtype == -1) { snprintf(msg, sizeof(msg), "Unsupported NativeMapped callback argument native type at argument %d", i); throw_type = EIllegalArgument; throw_msg = msg; goto failure_cleanup; } cb->arg_jtypes[i] = (char)jtype; cb->java_arg_types[i+3] = &ffi_type_pointer; cb->arg_types[i] = get_ffi_type(env, ncls, cb->arg_jtypes[i]); if (!cb->arg_types[i]) { goto failure_cleanup; } } // Java callback method is called using varargs, so promote floats to // double where appropriate for the platform if (cb->arg_types[i]->type == FFI_TYPE_FLOAT) { cb->java_arg_types[i+3] = &ffi_type_double; cb->conversion_flags[i] = CVT_FLOAT; cvt = 1; } else if (cb->java_arg_types[i+3]->type == FFI_TYPE_STRUCT) { // All callback structure arguments are passed as a jobject cb->java_arg_types[i+3] = &ffi_type_pointer; } } if (!direct || !cvt) { free(cb->conversion_flags); cb->conversion_flags = NULL; free(cb->arg_classes); cb->arg_classes = NULL; } if (direct) { cb->rflag = get_conversion_flag(env, return_type); if (cb->rflag == CVT_NATIVE_MAPPED || cb->rflag == CVT_INTEGER_TYPE || cb->rflag == CVT_POINTER_TYPE) { return_type = getNativeType(env, return_type); } } #if defined(_WIN32) && !defined(_WIN64) && !defined(_WIN32_WCE) if (calling_convention == CALLCONV_STDCALL) { abi = FFI_STDCALL; } // Calling into Java on win32 *always* uses stdcall java_abi = FFI_STDCALL; #endif // _WIN32 rtype = get_jtype(env, return_type); if (rtype == -1) { throw_type = EIllegalArgument; throw_msg = "Unsupported callback return type"; goto failure_cleanup; } ffi_rtype = get_ffi_rtype(env, return_type, (char)rtype); if (!ffi_rtype) { throw_type = EIllegalArgument; throw_msg = "Error in callback return type"; goto failure_cleanup; } status = ffi_prep_cif(&cb->cif, abi, argc, ffi_rtype, cb->arg_types); if (!ffi_error(env, "callback setup", status)) { ffi_type* java_ffi_rtype = ffi_rtype; if (cb->rflag == CVT_STRUCTURE_BYVAL || cb->rflag == CVT_NATIVE_MAPPED || cb->rflag == CVT_POINTER_TYPE || cb->rflag == CVT_INTEGER_TYPE) { // Java method returns a jobject, not a struct java_ffi_rtype = &ffi_type_pointer; rtype = '*'; } switch(rtype) { #define OFFSETOF(ENV,METHOD) ((size_t)((char *)&(*(ENV))->METHOD - (char *)(*(ENV)))) case 'V': cb->fptr_offset = OFFSETOF(env, CallVoidMethod); break; case 'Z': cb->fptr_offset = OFFSETOF(env, CallBooleanMethod); break; case 'B': cb->fptr_offset = OFFSETOF(env, CallByteMethod); break; case 'S': cb->fptr_offset = OFFSETOF(env, CallShortMethod); break; case 'C': cb->fptr_offset = OFFSETOF(env, CallCharMethod); break; case 'I': cb->fptr_offset = OFFSETOF(env, CallIntMethod); break; case 'J': cb->fptr_offset = OFFSETOF(env, CallLongMethod); break; case 'F': cb->fptr_offset = OFFSETOF(env, CallFloatMethod); break; case 'D': cb->fptr_offset = OFFSETOF(env, CallDoubleMethod); break; default: cb->fptr_offset = OFFSETOF(env, CallObjectMethod); break; } status = ffi_prep_cif_var(&cb->java_cif, java_abi, 2, argc+3, java_ffi_rtype, cb->java_arg_types); if (!ffi_error(env, "callback setup (2)", status)) { ffi_prep_closure_loc(cb->closure, &cb->cif, callback_dispatch, cb, cb->x_closure); #ifdef DLL_FPTRS // Find an available function pointer and assign it if (in_dll) { for (i=0;i < DLL_FPTRS;i++) { if (fn[i] == NULL) { fn[i] = cb->x_closure; cb->x_closure = dll_fptrs[i]; break; } } if (i == DLL_FPTRS) { throw_type = EOutOfMemory; throw_msg = "No more DLL callback slots available"; goto failure_cleanup; } } #endif return cb; } } failure_cleanup: free_callback(env, cb); if (throw_type) { throwByName(env, throw_type, throw_msg); } return NULL; }