/////////////////////////////////////////////////////// // // // VTT Technical Research Centre of Finland LTD // // For internal use only. Do not redistribute. // // // // Authors: // // Antton Tapani ext-antton.tapani@vtt.fi // // // // Last modified by Antton Tapani 9.2016 // // // /////////////////////////////////////////////////////// #include "sclpy.h" #include jint throwException( JNIEnv *env, char *className, const char *message ) { jclass exClass = (*env)->FindClass( env, className); if (exClass == NULL) { return 0; } return (*env)->ThrowNew( env, exClass, message ); } jint throwPythonException( JNIEnv *env, const char *message ) { return throwException( env, PYTHON_EXCEPTION, message ); } jint throwIllegalArgumentException( JNIEnv *env, const char *message ) { return throwException( env, ILLEGAL_ARGUMENT_EXCEPTION, message ); } PyObject* getModule(jlong contextID) { return PyState_FindModule((PyModuleDef*) contextID); // return PyImport_AddModule("__main__"); } int moduleCount = 0; int hasNumpy = 0; PyThreadState *main_ts = 0; static JNIEnv *currentEnv = NULL; jobject sclWriter = NULL; static PyObject * writeToSCL(PyObject *self, PyObject *args) { if (currentEnv != NULL && sclWriter != NULL) { Py_UNICODE *what; Py_ssize_t length; JNIEnv *env = currentEnv; if (!PyArg_ParseTuple(args, "u#", &what, &length)) Py_RETURN_NONE; { PyThreadState *my_ts = PyThreadState_Get(); if (my_ts == main_ts) { jclass writerClass = (*env)->FindClass(env, WRITER_CLASS); jmethodID writeMethod = (*env)->GetMethodID(env, writerClass, "write", "([CII)V"); jcharArray chars = (*env)->NewCharArray(env, (jsize)length); (*env)->SetCharArrayRegion(env, chars, 0, length, what); Py_BEGIN_ALLOW_THREADS (*env)->CallVoidMethod(env, sclWriter, writeMethod, chars, 0, length); Py_END_ALLOW_THREADS } else { //TODO } } } Py_RETURN_NONE; } static PyObject * flushSCL(PyObject *self, PyObject *args) { if (currentEnv != NULL && sclWriter != NULL) { JNIEnv *env = currentEnv; PyThreadState *my_ts = PyThreadState_Get(); if (my_ts != main_ts) { // TODO: Process calls from other threads Py_RETURN_NONE; } { jclass writerClass = (*env)->FindClass(env, WRITER_CLASS); jmethodID flushMethod = (*env)->GetMethodID(env, writerClass, "flush", "()V"); Py_BEGIN_ALLOW_THREADS (*env)->CallVoidMethod(env, sclWriter, flushMethod); Py_END_ALLOW_THREADS } } Py_RETURN_NONE; } static PyMethodDef sclWriterMethods[] = { {"write", writeToSCL, METH_VARARGS, "Write something."}, {"flush", flushSCL, METH_VARARGS, "Flush output."}, {NULL, NULL, 0, NULL} }; JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_initializePython(JNIEnv *env, jobject thisObj, jobject writer) { Py_Initialize(); { static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "sclwriter", NULL, -1, sclWriterMethods, }; PyObject *m = PyModule_Create(&moduledef); sclWriter = (*env)->NewGlobalRef(env, writer); if (m == NULL) throwException(env, PYTHON_EXCEPTION, "Failed to create SCL writer module"); PySys_SetObject("stdout", m); PySys_SetObject("stderr", m); } hasNumpy = _import_array(); hasNumpy = hasNumpy != -1; main_ts = PyEval_SaveThread(); } JNIEXPORT jlong JNICALL Java_org_simantics_pythonlink_PythonContext_createContextImpl(JNIEnv *env, jobject thisObj) { char name[16]; if (!main_ts) { return 0; } sprintf(name, "SCL_%d", ++moduleCount); PyEval_RestoreThread(main_ts); { PyObject *module; PyModuleDef *modDef = malloc(sizeof(PyModuleDef)); memset(modDef, 0, sizeof(PyModuleDef)); modDef->m_name = strdup(name); modDef->m_doc = NULL; modDef->m_size = -1; module = PyModule_Create(modDef); Py_INCREF(module); PyState_AddModule(module, modDef); { PyObject *builtin = PyImport_AddModule("builtins"); PyObject *dict = PyModule_GetDict(module); PyDict_SetItemString(dict, "__builtin__", builtin); PyDict_SetItemString(dict, "__builtins__", builtin); PyEval_SaveThread(); return (jlong)modDef; } } } JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_deleteContextImpl(JNIEnv *env, jobject thisObj, jlong contextID) { PyModuleDef *modDef = (PyModuleDef*)contextID; PyObject *module; PyEval_RestoreThread(main_ts); module = PyState_FindModule(modDef); Py_XDECREF(module); PyState_RemoveModule(modDef); free((char*)modDef->m_name); free(modDef); PyEval_SaveThread(); } PyObject *getPythonBool(jboolean value) { if (value) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; } } PyObject *getPythonString(JNIEnv *env, jstring string) { jsize len = (*env)->GetStringLength(env, string); const jchar *chars = (*env)->GetStringChars(env, string, NULL); PyObject *value = PyUnicode_DecodeUTF16((char*)chars, 2*len, NULL, NULL); (*env)->ReleaseStringChars(env, string, chars); return value; } PyObject *getPythonStringList(JNIEnv *env, jobjectArray value) { jsize nitems = (*env)->GetArrayLength(env, value); jint *values = (*env)->GetIntArrayElements(env, value, NULL); jclass stringClass = (*env)->FindClass(env, STRING_CLASS); jint i; PyObject *result = PyList_New(nitems); for (i = 0; i < nitems; i++) { jobject item = (*env)->GetObjectArrayElement(env, value, i); if (item != NULL && (*env)->IsInstanceOf(env, item, stringClass)) { PyList_SetItem(result, i, getPythonString(env, (jstring)item)); } else { PyList_SetItem(result, i, Py_None); } } (*env)->ReleaseIntArrayElements(env, value, values, JNI_ABORT); return result; } PyObject *getPythonBooleanList(JNIEnv *env, jbooleanArray value) { jsize nitems = (*env)->GetArrayLength(env, value); jboolean *values = (*env)->GetBooleanArrayElements(env, value, NULL); jint i; PyObject *result = PyList_New(nitems); for (i = 0; i < nitems; i++) { PyList_SetItem(result, i, getPythonBool(values[i])); } (*env)->ReleaseBooleanArrayElements(env, value, values, JNI_ABORT); return result; } PyObject *getPythonByteArray(JNIEnv *env, jbyteArray value) { jint len = (*env)->GetArrayLength(env, value); jbyte *values = (*env)->GetByteArrayElements(env, value, NULL); PyObject *result = PyByteArray_FromStringAndSize(values, len); (*env)->ReleaseByteArrayElements(env, value, values, JNI_ABORT); return result; } PyObject *getPythonIntegerList(JNIEnv *env, jintArray value) { jsize nitems = (*env)->GetArrayLength(env, value); jint *values = (*env)->GetIntArrayElements(env, value, NULL); jint i; PyObject *result = PyList_New(nitems); for (i = 0; i < nitems; i++) { PyList_SetItem(result, i, PyLong_FromLong(values[i])); } (*env)->ReleaseIntArrayElements(env, value, values, JNI_ABORT); return result; } PyObject *getPythonLongList(JNIEnv *env, jlongArray value) { jsize nitems = (*env)->GetArrayLength(env, value); jlong *values = (*env)->GetLongArrayElements(env, value, NULL); jint i; PyObject *result = PyList_New(nitems); for (i = 0; i < nitems; i++) { PyList_SetItem(result, i, PyLong_FromLongLong(values[i])); } (*env)->ReleaseLongArrayElements(env, value, values, JNI_ABORT); return result; } PyObject *getPythonFloatList(JNIEnv *env, jfloatArray value) { jsize nitems = (*env)->GetArrayLength(env, value); float *values = (*env)->GetFloatArrayElements(env, value, NULL); jint i; PyObject *result = PyList_New(nitems); for (i = 0; i < nitems; i++) { PyList_SetItem(result, i, PyFloat_FromDouble((double)values[i])); } (*env)->ReleaseFloatArrayElements(env, value, values, JNI_ABORT); return result; } PyObject *getPythonDoubleList(JNIEnv *env, jdoubleArray value) { jsize nitems = (*env)->GetArrayLength(env, value); double *values = (*env)->GetDoubleArrayElements(env, value, NULL); jint i; PyObject *result = PyList_New(nitems); for (i = 0; i < nitems; i++) { PyList_SetItem(result, i, PyFloat_FromDouble(values[i])); } (*env)->ReleaseDoubleArrayElements(env, value, values, JNI_ABORT); return result; } PyObject *getPythonNDArray(JNIEnv *env, jobject value) { jclass ndarrayClass = (*env)->FindClass(env, NDARRAY_CLASS); jmethodID dimsMethod = (*env)->GetMethodID(env, ndarrayClass, "dims", "()[I"); jmethodID getValuesMethod = (*env)->GetMethodID(env, ndarrayClass, "getValues", "()[D"); jintArray jdims = (*env)->CallObjectMethod(env, value, dimsMethod); jsize ndims = (*env)->GetArrayLength(env, jdims); jint *dims = (*env)->GetIntArrayElements(env, jdims, NULL); jdoubleArray jvalues = (*env)->CallObjectMethod(env, value, getValuesMethod); jsize len = (*env)->GetArrayLength(env, jvalues); jdouble *values = (*env)->GetDoubleArrayElements(env, jvalues, NULL); npy_intp *pyDims = (npy_intp*)malloc(ndims * sizeof(npy_intp)); jint i, nelem = ndims > 0 ? 1 : 0; for (i = 0; i < ndims; i++) { nelem *= dims[i]; pyDims[i] = dims[i]; } len = min(len, nelem); { PyObject *array = PyArray_EMPTY(ndims, pyDims, NPY_DOUBLE, 0); double *data = (double *)PyArray_DATA((PyArrayObject*)array); memcpy(data, values, len * sizeof(double)); free(pyDims); (*env)->ReleaseDoubleArrayElements(env, jvalues, values, JNI_ABORT); (*env)->ReleaseIntArrayElements(env, jdims, dims, JNI_ABORT); return array; } } PyObject *getPythonBooleanObject(JNIEnv *env, jobject object, jobject binding) { jclass bindingClass = (*env)->FindClass(env, BOOLEANBINDING_CLASS); jmethodID getValueMethod = (*env)->GetMethodID(env, bindingClass, "getValue_", "(L" OBJECT_CLASS ";)Z"); jboolean bvalue = (*env)->CallBooleanMethod(env, binding, getValueMethod, object); return getPythonBool(bvalue); } PyObject *getPythonByteObject(JNIEnv *env, jobject object, jobject binding) { jclass bindingClass = (*env)->FindClass(env, BYTEBINDING_CLASS); jmethodID getValueMethod = (*env)->GetMethodID(env, bindingClass, "getValue_", "(L" OBJECT_CLASS ";)B"); jbyte v = (*env)->CallByteMethod(env, binding, getValueMethod, object); return PyLong_FromLong(v); } PyObject *getPythonIntegerObject(JNIEnv *env, jobject object, jobject binding) { jclass bindingClass = (*env)->FindClass(env, INTEGERBINDING_CLASS); jmethodID getValueMethod = (*env)->GetMethodID(env, bindingClass, "getValue_", "(L" OBJECT_CLASS ";)I"); jint v = (*env)->CallIntMethod(env, binding, getValueMethod, object); return PyLong_FromLong(v); } PyObject *getPythonLongObject(JNIEnv *env, jobject object, jobject binding) { jclass bindingClass = (*env)->FindClass(env, LONGBINDING_CLASS); jmethodID getValueMethod = (*env)->GetMethodID(env, bindingClass, "getValue_", "(L" OBJECT_CLASS ";)J"); jlong v = (*env)->CallLongMethod(env, binding, getValueMethod, object); return PyLong_FromLongLong(v); } PyObject *getPythonFloatObject(JNIEnv *env, jobject object, jobject binding) { jclass bindingClass = (*env)->FindClass(env, FLOATBINDING_CLASS); jmethodID getValueMethod = (*env)->GetMethodID(env, bindingClass, "getValue_", "(L" OBJECT_CLASS ";)F"); jfloat v = (*env)->CallFloatMethod(env, binding, getValueMethod, object); return PyFloat_FromDouble(v); } PyObject *getPythonDoubleObject(JNIEnv *env, jobject object, jobject binding) { jclass bindingClass = (*env)->FindClass(env, DOUBLEBINDING_CLASS); jmethodID getValueMethod = (*env)->GetMethodID(env, bindingClass, "getValue_", "(L" OBJECT_CLASS ";)D"); jdouble v = (*env)->CallDoubleMethod(env, binding, getValueMethod, object); return PyFloat_FromDouble(v); } PyObject *getPythonStringObject(JNIEnv *env, jobject object, jobject binding) { jclass bindingClass = (*env)->FindClass(env, STRINGBINDING_CLASS); jmethodID getValueMethod = (*env)->GetMethodID(env, bindingClass, "getValue", "(L" OBJECT_CLASS ";)L" STRING_CLASS ";"); jobject string = (*env)->CallObjectMethod(env, binding, getValueMethod, object); jsize len = (*env)->GetStringLength(env, string); const jchar *chars = (*env)->GetStringChars(env, string, NULL); PyObject *value = PyUnicode_DecodeUTF16((char*)chars, 2*len, NULL, NULL); (*env)->ReleaseStringChars(env, string, chars); return value; } PyObject *getPythonRecordObject(JNIEnv *env, jobject object, jobject binding) { jclass bindingClass = (*env)->FindClass(env, RECORDBINDING_CLASS); jmethodID typeMethod = (*env)->GetMethodID(env, bindingClass, "type", "()L" RECORDTYPE_CLASS ";"); jmethodID getComponent = (*env)->GetMethodID(env, bindingClass, "getComponent", "(L" OBJECT_CLASS ";I)L" OBJECT_CLASS ";"); jmethodID getComponentBinding = (*env)->GetMethodID(env, bindingClass, "getComponentBinding", "(I)L" BINDING_CLASS ";"); jclass recordType = (*env)->FindClass(env, RECORDTYPE_CLASS); jmethodID getTypeComponent = (*env)->GetMethodID(env, recordType, "getComponent", "(I)L" COMPONENT_CLASS ";"); jmethodID getComponentCount = (*env)->GetMethodID(env, recordType, "getComponentCount", "()I"); jclass componentClass = (*env)->FindClass(env, COMPONENT_CLASS); jfieldID nameField = (*env)->GetFieldID(env, componentClass, "name", "L" STRING_CLASS ";"); jobject type = (*env)->CallObjectMethod(env, binding, typeMethod); jint n = (*env)->CallIntMethod(env, type, getComponentCount); jint i; PyObject *result = PyDict_New(); for (i = 0; i < n; i++) { jobject recordTypeComponent = (*env)->CallObjectMethod(env, type, getComponent, i); jstring fieldName = (jstring)(*env)->GetObjectField(env, recordTypeComponent, nameField); jobject componentObject = (*env)->CallObjectMethod(env, binding, getComponent, object, i); jobject componentBinding = (*env)->CallObjectMethod(env, binding, getComponentBinding, i); PyObject *item = getPythonObject(env, componentObject, componentBinding); PyDict_SetItem(result, getPythonString(env, fieldName), item); } return result; } PyObject *getPythonArrayObject(JNIEnv *env, jobject object, jobject binding) { jclass bindingClass = (*env)->FindClass(env, ARRAYBINDING_CLASS); jmethodID componentBindingMethod = (*env)->GetMethodID(env, bindingClass, "getComponentBinding", "()L" BINDING_CLASS ";"); jmethodID sizeMethod = (*env)->GetMethodID(env, bindingClass, "size", "(L" OBJECT_CLASS ";)I"); jmethodID getMethod = (*env)->GetMethodID(env, bindingClass, "get", "(L" OBJECT_CLASS ";I)L" OBJECT_CLASS ";"); jobject componentBinding = (*env)->CallObjectMethod(env, binding, componentBindingMethod); jint size = (*env)->CallIntMethod(env, binding, sizeMethod, object); PyObject *result = PyList_New(size); jint i; for (i = 0; i < size; i++) { jobject item = (*env)->CallObjectMethod(env, binding, getMethod, object, i); if (item != NULL) PyList_SetItem(result, i, getPythonObject(env, item, componentBinding)); else PyList_SetItem(result, i, Py_None); } return result; } PyObject *getPythonMapObject(JNIEnv *env, jobject object, jobject binding) { jclass objectClass = (*env)->FindClass(env, OBJECT_CLASS); jclass bindingClass = (*env)->FindClass(env, MAPBINDING_CLASS); jmethodID getKeyBindingMethod = (*env)->GetMethodID(env, bindingClass, "getKeyBinding", "()L" BINDING_CLASS ";"); jmethodID getValueBindingMethod = (*env)->GetMethodID(env, bindingClass, "getValueBinding", "()L" BINDING_CLASS ";"); jmethodID sizeMethod = (*env)->GetMethodID(env, bindingClass, "size", "(L" OBJECT_CLASS ";)I"); jmethodID getAllMethod = (*env)->GetMethodID(env, bindingClass, "getAll", "(L" OBJECT_CLASS ";[L" OBJECT_CLASS ";[L" OBJECT_CLASS ";)V"); jobject keyBinding = (*env)->CallObjectMethod(env, binding, getKeyBindingMethod); jobject valueBinding = (*env)->CallObjectMethod(env, binding, getValueBindingMethod); jint size = (*env)->CallIntMethod(env, binding, sizeMethod, object); jobjectArray keys = (*env)->NewObjectArray(env, size, objectClass, NULL); jobjectArray values = (*env)->NewObjectArray(env, size, objectClass, NULL); PyObject *result = PyDict_New(); jint i; (*env)->CallVoidMethod(env, binding, getAllMethod, object, keys, values); for (i = 0; i < size; i++) { jobject key = (*env)->GetObjectArrayElement(env, keys, i); jobject item = (*env)->GetObjectArrayElement(env, values, i); PyDict_SetItem(result, getPythonObject(env, key, keyBinding), getPythonObject(env, item, valueBinding)); } (*env)->DeleteLocalRef(env, keys); (*env)->DeleteLocalRef(env, values); return result; } PyObject *getPythonOptionalObject(JNIEnv *env, jobject object, jobject binding) { jclass bindingClass = (*env)->FindClass(env, OPTIONALBINDING_CLASS); jmethodID hasValueMethod = (*env)->GetMethodID(env, bindingClass, "hasValue", "(L" OBJECT_CLASS ";)Z"); jboolean hasValue = (*env)->CallBooleanMethod(env, binding, hasValueMethod, object); if (hasValue) { jmethodID componentBindingMethod = (*env)->GetMethodID(env, bindingClass, "getComponentBinding", "()L" BINDING_CLASS ";"); jmethodID getValueMethod = (*env)->GetMethodID(env, bindingClass, "hasValue", "(L" OBJECT_CLASS ";)L" OBJECT_CLASS ";"); jobject componentBinding = (*env)->CallObjectMethod(env, binding, componentBindingMethod); jobject value = (*env)->CallObjectMethod(env, binding, getValueMethod, object); return getPythonObject(env, value, componentBinding); } else { return Py_None; } } PyObject *getPythonUnionObject(JNIEnv *env, jobject object, jobject binding) { jclass bindingClass = (*env)->FindClass(env, UNIONBINDING_CLASS); jmethodID typeMethod = (*env)->GetMethodID(env, bindingClass, "type", "()L" RECORDTYPE_CLASS ";"); jmethodID getTagMethod = (*env)->GetMethodID(env, bindingClass, "getTag", "(L" OBJECT_CLASS ";)I"); jmethodID getValueMethod = (*env)->GetMethodID(env, bindingClass, "getValue", "(L" OBJECT_CLASS ";)L" OBJECT_CLASS ";"); jmethodID getComponentBinding = (*env)->GetMethodID(env, bindingClass, "getComponentBinding", "(I)L" BINDING_CLASS ";"); jclass unionType = (*env)->FindClass(env, UNIONTYPE_CLASS); jmethodID getTypeComponent = (*env)->GetMethodID(env, unionType, "getComponent", "(I)L" COMPONENT_CLASS ";"); jclass componentClass = (*env)->FindClass(env, COMPONENT_CLASS); jfieldID nameField = (*env)->GetFieldID(env, componentClass, "name", "L" STRING_CLASS ";"); jint tag = (*env)->CallIntMethod(env, binding, getTagMethod, object); jobject value = (*env)->CallObjectMethod(env, binding, getValueMethod, object); jobject type = (*env)->CallObjectMethod(env, binding, typeMethod); jobject typeComponent = (*env)->CallObjectMethod(env, type, getTypeComponent, tag); jstring compName = (*env)->GetObjectField(env, typeComponent, nameField); jobject componentBinding = (*env)->CallObjectMethod(env, binding, getComponentBinding, tag); PyObject *result = PyTuple_New(2); PyTuple_SetItem(result, 0, getPythonString(env, compName)); PyTuple_SetItem(result, 1, getPythonObject(env, value, componentBinding)); return result; } PyObject *getPythonVariantObject(JNIEnv *env, jobject object, jobject binding) { jclass bindingClass = (*env)->FindClass(env, VARIANTBINDING_CLASS); jmethodID getContentMethod = (*env)->GetMethodID(env, bindingClass, "getContent", "(L" OBJECT_CLASS ";)L" OBJECT_CLASS ";"); jmethodID getContentBindingMethod = (*env)->GetMethodID(env, bindingClass, "getContentBinding", "(L" OBJECT_CLASS ";)L" BINDING_CLASS ";"); jobject content = (*env)->CallObjectMethod(env, binding, getContentMethod, object); jobject contentBinding = (*env)->CallObjectMethod(env, binding, getContentBindingMethod, object); return getPythonObject(env, content, contentBinding); } PyObject *getPythonObject(JNIEnv *env, jobject value, jobject binding) { jclass booleanBinding = (*env)->FindClass(env, BOOLEANBINDING_CLASS); jclass byteBinding = (*env)->FindClass(env, BYTEBINDING_CLASS); jclass integerBinding = (*env)->FindClass(env, INTEGERBINDING_CLASS); jclass longBinding = (*env)->FindClass(env, LONGBINDING_CLASS); jclass floatBinding = (*env)->FindClass(env, FLOATBINDING_CLASS); jclass doubleBinding = (*env)->FindClass(env, DOUBLEBINDING_CLASS); jclass stringBinding = (*env)->FindClass(env, STRINGBINDING_CLASS); jclass recordBinding = (*env)->FindClass(env, RECORDBINDING_CLASS); jclass arrayBinding = (*env)->FindClass(env, ARRAYBINDING_CLASS); jclass mapBinding = (*env)->FindClass(env, MAPBINDING_CLASS); jclass optionalBinding = (*env)->FindClass(env, OPTIONALBINDING_CLASS); jclass untionBinding = (*env)->FindClass(env, UNIONBINDING_CLASS); jclass variantBinding = (*env)->FindClass(env, VARIANTBINDING_CLASS); if (value == NULL) return Py_None; if ((*env)->IsInstanceOf(env, binding, booleanBinding)) { return getPythonBooleanObject(env, value, binding); } else if ((*env)->IsInstanceOf(env, binding, byteBinding)) { return getPythonByteObject(env, value, binding); } else if ((*env)->IsInstanceOf(env, binding, integerBinding)) { return getPythonIntegerObject(env, value, binding); } else if ((*env)->IsInstanceOf(env, binding, longBinding)) { return getPythonLongObject(env, value, binding); } else if ((*env)->IsInstanceOf(env, binding, floatBinding)) { return getPythonFloatObject(env, value, binding); } else if ((*env)->IsInstanceOf(env, binding, doubleBinding)) { return getPythonDoubleObject(env, value, binding); } else if ((*env)->IsInstanceOf(env, binding, stringBinding)) { return getPythonStringObject(env, value, binding); } else if ((*env)->IsInstanceOf(env, binding, recordBinding)) { return getPythonRecordObject(env, value, binding); } else if ((*env)->IsInstanceOf(env, binding, arrayBinding)) { return getPythonArrayObject(env, value, binding); } else if ((*env)->IsInstanceOf(env, binding, mapBinding)) { return getPythonMapObject(env, value, binding); } else if ((*env)->IsInstanceOf(env, binding, optionalBinding)) { return getPythonOptionalObject(env, value, binding); } else if ((*env)->IsInstanceOf(env, binding, untionBinding)) { return getPythonUnionObject(env, value, binding); } else if ((*env)->IsInstanceOf(env, binding, variantBinding)) { return getPythonVariantObject(env, value, binding); } else { return Py_None; } } void setPythonVariable(PyObject *module, PyObject *name, PyObject *value) { if (name && value) { PyDict_SetItem(PyModule_GetDict(module), name, value); } Py_XDECREF(name); Py_XDECREF(value); } static npy_intp nContiguous(int d, int nd, npy_intp *strides, npy_intp *dims, npy_intp *ncont) { if (d == nd) { ncont[d] = 1; return 1; } else { npy_intp n = nContiguous(d+1, nd, strides, dims, ncont); ncont[d] = n > 0 && strides[d] == sizeof(double) * n ? dims[d] * n : 0; return ncont[d]; } } static void copyDoubleArrayValues(JNIEnv *env, jdoubleArray array, double *data, npy_intp *offset, int d, int nd, npy_intp *strides, npy_intp *dims, npy_intp *ncont) { if (ncont[d] > 0) { (*env)->SetDoubleArrayRegion(env, array, (jint)*offset, (jint)ncont[d], data); *offset += ncont[d]; } else { int i; for (i = 0; i < dims[d]; i++) { copyDoubleArrayValues(env, array, (double*)((char*)data + strides[d] * i), offset, d+1, nd, strides, dims, ncont); } } } jobject pythonBoolAsBooleanObject(JNIEnv *env, PyObject *value) { jclass booleanClass = (*env)->FindClass(env, "java/lang/Boolean"); jmethodID valueOfMethod = (*env)->GetStaticMethodID(env, booleanClass, "valueOf", "(Z)Ljava/lang/Boolean;"); return (*env)->CallStaticObjectMethod(env, booleanClass, valueOfMethod, (jboolean)(value == Py_True)); } jobject pythonLongAsLongObject(JNIEnv *env, PyObject *value) { jclass longClass = (*env)->FindClass(env, "java/lang/Long"); jmethodID valueOfMethod = (*env)->GetStaticMethodID(env, longClass, "valueOf", "(J)Ljava/lang/Long;"); return (*env)->CallStaticObjectMethod(env, longClass, valueOfMethod, PyLong_AsLongLong(value)); } jobject pythonFloatAsDoubleObject(JNIEnv *env, PyObject *value) { jclass doubleClass = (*env)->FindClass(env, "java/lang/Double"); jmethodID valueOfMethod = (*env)->GetStaticMethodID(env, doubleClass, "valueOf", "(D)Ljava/lang/Double;"); return (*env)->CallStaticObjectMethod(env, doubleClass, valueOfMethod, PyFloat_AsDouble(value)); } jobject pythonByteArrayAsByteArray(JNIEnv *env, PyObject *value) { Py_ssize_t size = PyByteArray_Size(value); jbyteArray result = (*env)->NewByteArray(env, (jsize)size); char *bytes = PyByteArray_AsString(value); (*env)->SetByteArrayRegion(env, result, 0, size, bytes); return result; } jstring pythonStringAsJavaString(JNIEnv *env, PyObject *string) { PyObject *utf16Value = PyUnicode_AsUTF16String(string); Py_ssize_t len = PyBytes_Size(utf16Value) / 2; char *bytes = PyBytes_AsString(utf16Value); // Create Java string, skipping the byte order mark in the beginning jstring result = (*env)->NewString(env, (jchar *)bytes + 1, (jsize)min(len, JAVA_MAXINT) - 1); Py_XDECREF(utf16Value); return result; } jobjectArray pythonSequenceAsStringArray(JNIEnv *env, PyObject *seq) { Py_ssize_t len = PySequence_Size(seq); jsize jlen = (jsize)min(len, JAVA_MAXINT); jobjectArray array = (*env)->NewObjectArray(env, jlen, (*env)->FindClass(env, STRING_CLASS), NULL); jint i; for (i = 0; i < jlen; i++) { PyObject *item = PySequence_GetItem(seq, i); if (PyUnicode_Check(item)) { jstring value = pythonStringAsJavaString(env, item); Py_DECREF(item); (*env)->SetObjectArrayElement(env, array, i, value); } else { Py_DECREF(item); throwPythonException(env, "List item not a string"); return NULL; } } return array; } jdoubleArray pythonSequenceAsDoubleArray(JNIEnv *env, PyObject *seq) { Py_ssize_t len = PySequence_Size(seq); jsize jlen = (jsize)min(len, JAVA_MAXINT); jdoubleArray array = (*env)->NewDoubleArray(env, jlen); jint i; for (i = 0; i < jlen; i++) { PyObject *item = PySequence_GetItem(seq, i); if (PyFloat_Check(item)) { jdouble value = PyFloat_AsDouble(item); Py_DECREF(item); (*env)->SetDoubleArrayRegion(env, array, i, 1, &value); } else { Py_DECREF(item); throwPythonException(env, "List item not a floating point value"); return NULL; } } return array; } jobject pythonObjectAsObject(JNIEnv *env, PyObject *value) { if (PyBool_Check(value)) return pythonBoolAsBooleanObject(env, value); else if (PyLong_Check(value)) return pythonLongAsLongObject(env, value); else if (PyFloat_Check(value)) return pythonFloatAsDoubleObject(env, value); else if (PyUnicode_Check(value)) return pythonStringAsJavaString(env, value); else if (PyByteArray_Check(value)) return pythonByteArrayAsByteArray(env, value); else if (PyDict_Check(value)) return pythonDictionaryAsMap(env, value); else if (hasNumpy && PyArray_Check(value)) return pythonArrayAsNDArray(env, (PyArrayObject *)value); else if (PySequence_Check(value)) return pythonSequenceAsObjectArray(env, value); else return NULL; } jobjectArray pythonSequenceAsObjectArray(JNIEnv *env, PyObject *seq) { Py_ssize_t len = PySequence_Size(seq); jsize jlen = (jsize)min(len, JAVA_MAXINT); jobjectArray array = (*env)->NewObjectArray(env, jlen, (*env)->FindClass(env, OBJECT_CLASS), NULL); jint i; for (i = 0; i < jlen; i++) { PyObject *item = PySequence_GetItem(seq, i); jobject object = pythonObjectAsObject(env, item); Py_DECREF(item); (*env)->SetObjectArrayElement(env, array, i, object); } return array; } jbooleanArray pythonSequenceAsBooleanArray(JNIEnv *env, PyObject *seq) { Py_ssize_t len = PySequence_Size(seq); jsize jlen = (jsize)min(len, JAVA_MAXINT); jbooleanArray array = (*env)->NewBooleanArray(env, jlen); jint i; for (i = 0; i < jlen; i++) { PyObject *item = PySequence_GetItem(seq, i); if (PyBool_Check(item)) { jboolean value = item == Py_True; Py_DECREF(item); (*env)->SetBooleanArrayRegion(env, array, i, 1, &value); } else { Py_DECREF(item); throwPythonException(env, "List item not a boolean"); return NULL; } } return array; } jintArray pythonSequenceAsIntegerArray(JNIEnv *env, PyObject *seq) { Py_ssize_t len = PySequence_Size(seq); jsize jlen = (jsize)min(len, JAVA_MAXINT); jintArray array = (*env)->NewIntArray(env, jlen); jint i; for (i = 0; i < jlen; i++) { PyObject *item = PySequence_GetItem(seq, i); if (PyLong_Check(item)) { jint value = PyLong_AsLong(item); Py_DECREF(item); (*env)->SetIntArrayRegion(env, array, i, 1, &value); } else { Py_DECREF(item); throwPythonException(env, "List item not an integer"); return NULL; } } return array; } jlongArray pythonSequenceAsLongArray(JNIEnv *env, PyObject *seq) { Py_ssize_t len = PySequence_Size(seq); jsize jlen = (jsize)min(len, JAVA_MAXINT); jlongArray array = (*env)->NewLongArray(env, jlen); jint i; for (i = 0; i < jlen; i++) { PyObject *item = PySequence_GetItem(seq, i); if (PyLong_Check(item)) { jlong value = PyLong_AsLongLong(item); Py_DECREF(item); (*env)->SetLongArrayRegion(env, array, i, 1, &value); } else { Py_DECREF(item); throwPythonException(env, "List item not an integer"); return NULL; } } return array; } jobject pythonArrayAsNDArray(JNIEnv *env, PyArrayObject *array) { jclass ndarrayClass = (*env)->FindClass(env, NDARRAY_CLASS); jmethodID constructor = (*env)->GetMethodID(env, ndarrayClass, "", "([I[D)V"); int ndims = PyArray_NDIM(array); npy_intp *dims = PyArray_DIMS(array); npy_intp len = PyArray_Size((PyObject*)array); double *values = (double*)PyArray_DATA(array); jboolean isFortran = PyArray_ISFORTRAN(array) != 0; int i; if (len > JAVA_MAXINT) { throwPythonException(env, "Array too large"); return NULL; } { jintArray jdims = (*env)->NewIntArray(env, ndims); jdoubleArray jvalues = (*env)->NewDoubleArray(env, (jsize)len); for (i = 0; i < ndims; i++) { jint dim = (jint)dims[i]; (*env)->SetIntArrayRegion(env, jdims, i, 1, &dim); } if (PyArray_IS_C_CONTIGUOUS(array)) { (*env)->SetDoubleArrayRegion(env, jvalues, 0, (jsize)len, values); } else { npy_intp offset = 0; npy_intp *strides = PyArray_STRIDES(array); npy_intp *ncont = (npy_intp*)malloc((ndims + 1) * sizeof(npy_intp)); nContiguous(0, ndims, strides, dims, ncont); copyDoubleArrayValues(env, jvalues, values, &offset, 0, ndims, strides, dims, ncont); free(ncont); } return (*env)->NewObject(env, ndarrayClass, constructor, jdims, jvalues, isFortran); } } jobject pythonDictionaryAsMap(JNIEnv *env, PyObject *dict) { jclass hashmapClass = (*env)->FindClass(env, "java/util/HashMap"); jmethodID constructor = (*env)->GetMethodID(env, hashmapClass, "", "(I)V"); jmethodID putMethod = (*env)->GetMethodID(env, hashmapClass, "put", "(L" OBJECT_CLASS ";L" OBJECT_CLASS ";)L" OBJECT_CLASS ";"); Py_ssize_t size = PyDict_Size(dict); jobject map = (*env)->NewObject(env, hashmapClass, constructor, (jint)size); PyObject *key, *value; Py_ssize_t pos = 0; while (PyDict_Next(dict, &pos, &key, &value)) { jobject keyObject = pythonObjectAsObject(env, key); jobject valueObject = pythonObjectAsObject(env, value); (*env)->CallObjectMethod(env, map, putMethod, keyObject, valueObject); } return map; } #define DEF_SETTER(typename, jtype, j2py) \ JNIEXPORT void JNICALL \ Java_org_simantics_pythonlink_PythonContext_setPython##typename \ ##VariableImpl( \ JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, \ jtype value) { \ PyObject *module; \ \ PyEval_RestoreThread(main_ts); \ module = getModule(contextID); \ setPythonVariable(module, getPythonString(env, variableName), \ j2py(env, value)); \ PyEval_SaveThread(); \ } #define getPythonBoolean(env, value) getPythonBool(value) #define getPythonLong(env, value) PyLong_FromLongLong(value) #define getPythonDouble(env, value) PyFloat_FromDouble(value) DEF_SETTER(Boolean, jboolean, getPythonBoolean) DEF_SETTER(BooleanArray, jbooleanArray, getPythonBooleanList) DEF_SETTER(Long, jlong, getPythonLong) DEF_SETTER(IntegerArray, jintArray, getPythonIntegerList) DEF_SETTER(LongArray, jlongArray, getPythonLongList) DEF_SETTER(Double, jdouble, getPythonDouble) DEF_SETTER(FloatArray, jfloatArray, getPythonFloatList) DEF_SETTER(DoubleArray, jdoubleArray, getPythonDoubleList) DEF_SETTER(String, jstring, getPythonString) DEF_SETTER(StringArray, jobjectArray, getPythonStringList) JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonNDArrayVariableImpl( JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, jobject value) { if (!hasNumpy) { throwPythonException(env, "Importing numpy failed"); return; } PyEval_RestoreThread(main_ts); { PyObject *module = getModule(contextID); PyObject *pythonName = getPythonString(env, variableName); PyObject *val = getPythonNDArray(env, value); setPythonVariable(module, pythonName, val); } PyEval_SaveThread(); } JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonVariantVariableImpl( JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, jobject value, jobject binding) { PyObject *module; PyEval_RestoreThread(main_ts); module = getModule(contextID); setPythonVariable(module, getPythonString(env, variableName), getPythonObject(env, value, binding)); PyEval_SaveThread(); } static PyObject *getExceptionMessage(PyObject *exceptionType, PyObject *exception, PyObject *traceback) { PyObject *formatExc = NULL, *args = NULL; PyObject *tracebackModule = PyImport_ImportModule("traceback"); if (!tracebackModule) { return NULL; } if (exception && traceback) { formatExc = PyDict_GetItemString(PyModule_GetDict(tracebackModule), "format_exception"); args = PyTuple_Pack(3, exceptionType, exception, traceback); } else if (exception) { formatExc = PyDict_GetItemString(PyModule_GetDict(tracebackModule), "format_exception_only"); args = PyTuple_Pack(2, exceptionType, exception); } Py_DECREF(tracebackModule); if (formatExc != NULL && args != NULL) { PyObject *result = PyObject_CallObject(formatExc, args); Py_XDECREF(args); Py_XDECREF(formatExc); return result; } else { Py_XDECREF(args); Py_XDECREF(formatExc); return NULL; } } JNIEXPORT jint JNICALL Java_org_simantics_pythonlink_PythonContext_executePythonStatementImpl( JNIEnv *env, jobject thisObj, jlong contextID, jstring statement) { const char *utfchars = (*env)->GetStringUTFChars(env, statement, NULL); PyEval_RestoreThread(main_ts); PyErr_Clear(); { PyObject *module = getModule(contextID); PyObject *globals; globals = PyModule_GetDict(module); currentEnv = env; { PyObject *result = PyRun_String(utfchars, Py_file_input, globals, globals); PyObject *exceptionType = PyErr_Occurred(); if (exceptionType != NULL) { PyObject *exception, *traceback, *message; PyErr_Fetch(&exceptionType, &exception, &traceback); message = getExceptionMessage(exceptionType, exception, traceback); if (message != NULL) { PyObject *emptyStr = PyUnicode_FromString(""); PyObject *joined = PyUnicode_Join(emptyStr, message); char *messageStr = PyUnicode_AsUTF8(joined); throwPythonException(env, messageStr); Py_DECREF(joined); Py_DECREF(emptyStr); Py_DECREF(message); } else { PyTypeObject *ty = (PyTypeObject *)exceptionType; throwPythonException( env, ty ? ty->tp_name : "Internal error, null exception type"); } Py_XDECREF(exceptionType); Py_XDECREF(exception); Py_XDECREF(traceback); } PyEval_SaveThread(); (*env)->ReleaseStringUTFChars(env, statement, utfchars); currentEnv = NULL; return result != NULL ? 0 : 1; } } } // Returns a borrowed reference. static PyObject *getPythonValue( JNIEnv *env, jlong contextID, jstring variableName) { PyObject *module = getModule(contextID); PyObject *pythonName = getPythonString(env, variableName); PyObject *value = PyDict_GetItem(PyModule_GetDict(module), pythonName); Py_DECREF(pythonName); if (value == NULL) { throwPythonException(env, "Python variable not found"); } return value; } #define DEF_GETTER(typename, jtype, check, desc, py2j) \ JNIEXPORT jtype JNICALL \ Java_org_simantics_pythonlink_PythonContext_getPython##typename \ ##VariableImpl( \ JNIEnv *env, jobject thisObj, jlong contextID, \ jstring variableName) { \ jtype result = 0; \ PyEval_RestoreThread(main_ts); \ do { \ PyObject *value = getPythonValue(env, contextID, variableName); \ if (value == 0) break; \ if (check(value)) { \ result = py2j(env, value); \ } else { \ throwPythonException(env, "Python variable not " desc); \ } \ } while (0); \ PyEval_SaveThread(); \ return result; \ } #define pythonBoolAsJboolean(env, value) ((value) == Py_True) #define pythonLongAsJlong(env, value) PyLong_AsLongLong(value) #define pythonFloatAsJdouble(env, value) PyFloat_AsDouble(value) DEF_GETTER(String, jstring, PyUnicode_Check, "a string", pythonStringAsJavaString) DEF_GETTER(StringArray, jobjectArray, PySequence_Check, "a sequence", pythonSequenceAsStringArray) DEF_GETTER(Boolean, jboolean, PyBool_Check, "a Boolean", pythonBoolAsJboolean) DEF_GETTER(BooleanArray, jbooleanArray, PySequence_Check, "a sequence", pythonSequenceAsBooleanArray) DEF_GETTER(Long, jlong, PyLong_Check, "an integer", pythonLongAsJlong) DEF_GETTER(IntegerArray, jintArray, PySequence_Check, "a sequence", pythonSequenceAsIntegerArray) DEF_GETTER(LongArray, jlongArray, PySequence_Check, "a sequence", pythonSequenceAsLongArray) DEF_GETTER(Double, jdouble, PyFloat_Check, "a float", pythonFloatAsJdouble) DEF_GETTER(DoubleArray, jdoubleArray, PySequence_Check, "a sequence", pythonSequenceAsDoubleArray) JNIEXPORT jobject JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonNDArrayVariableImpl( JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) { jobject result = NULL; if (!hasNumpy) { throwPythonException(env, "Importing numpy failed"); return NULL; } PyEval_RestoreThread(main_ts); do { PyObject *value = getPythonValue(env, contextID, variableName); if (value == NULL) break; if (!PyArray_Check(value)) { throwPythonException(env, "Python variable not an ndarray"); } else if (PyArray_TYPE((PyArrayObject*)value) != NPY_DOUBLE) { throwPythonException( env, "Only ndarrays of type double are supported"); } else { result = pythonArrayAsNDArray(env, (PyArrayObject *)value); } } while (0); PyEval_SaveThread(); return result; } #define python_anything_goes(value) 1 DEF_GETTER(Variant, jobject, python_anything_goes, "frabjous", pythonObjectAsObject) JNIEXPORT jint JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonVariableTypeImpl( JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) { jint result = 0; PyEval_RestoreThread(main_ts); { PyObject *value = getPythonValue(env, contextID, variableName); if (PyBool_Check(value)) result = 1; else if (PyLong_Check(value)) result = 2; else if (PyFloat_Check(value)) result = 3; else if (PyUnicode_Check(value)) result = 4; else if (PyByteArray_Check(value)) result = 5; else if (PyDict_Check(value)) result = 6; else if (hasNumpy && PyArray_Check(value)) result = 7; else if (PySequence_Check(value)) result = 8; else result = -1; } PyEval_SaveThread(); return result; } JNIEXPORT jobjectArray JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonVariableNamesImpl( JNIEnv *env, jobject thisObj, jlong contextID) { jobjectArray result = NULL; PyEval_RestoreThread(main_ts); { PyObject *module = getModule(contextID); PyObject *dict = PyModule_GetDict(module); PyObject *keys = PyDict_Keys(dict); Py_ssize_t size = PyList_Size(keys); Py_ssize_t i; result = (*env)->NewObjectArray( env, (jsize)size, (*env)->FindClass(env, STRING_CLASS), NULL); for (i = 0; i < size; i++) { jstring javaName = pythonStringAsJavaString( env, PyList_GetItem(keys, i)); (*env)->SetObjectArrayElement(env, result, (jint)i, javaName); } Py_XDECREF(keys); } PyEval_SaveThread(); return result; } BOOL __stdcall DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) //extern "C" DLL_EXPORT BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { switch (fdwReason) { case DLL_PROCESS_ATTACH: // attach to process // return FALSE to fail DLL load break; case DLL_PROCESS_DETACH: // detach from process break; case DLL_THREAD_ATTACH: // attach to thread break; case DLL_THREAD_DETACH: // detach from thread break; } return TRUE; // succesful }