X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=org.simantics.pythonlink.win32.x86_64%2Fsrc%2Fsclpy.c;h=b2308bcdd3b4af57192c0fbd5a3dfa1ad6801e9f;hb=d556dfd2739c43551c894dd636bf8e0735fbad55;hp=1cc36d8e98ff4552c66eb9113f72570692375026;hpb=94a19dbaf6ef04f3476ac6d51c5b8f29f237cf23;p=simantics%2Fpython.git diff --git a/org.simantics.pythonlink.win32.x86_64/src/sclpy.c b/org.simantics.pythonlink.win32.x86_64/src/sclpy.c index 1cc36d8..b2308bc 100644 --- a/org.simantics.pythonlink.win32.x86_64/src/sclpy.c +++ b/org.simantics.pythonlink.win32.x86_64/src/sclpy.c @@ -1,20 +1,21 @@ -/////////////////////////////////////////////////////// -// // -// 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 // -// // -/////////////////////////////////////////////////////// +/******************************************************************************* + * Copyright (c) 2017-2019 Association for Decentralized Information Management in + * Industry THTH ry. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VTT Technical Research Centre - Initial API and implementation + * Semantum Oy - Improvements + *******************************************************************************/ #include "sclpy.h" #include -jint throwException( JNIEnv *env, char *className, char *message ) +jint throwException( JNIEnv *env, char *className, const char *message ) { jclass exClass = (*env)->FindClass( env, className); if (exClass == NULL) { @@ -24,46 +25,158 @@ jint throwException( JNIEnv *env, char *className, char *message ) return (*env)->ThrowNew( env, exClass, message ); } -jint throwPythonException( JNIEnv *env, char *message ) { +jint throwPythonException( JNIEnv *env, const char *message ) { return throwException( env, PYTHON_EXCEPTION, message ); } -jint throwIllegalArgumentException( JNIEnv *env, char *message ) { +jint throwIllegalArgumentException( JNIEnv *env, const char *message ) { return throwException( env, ILLEGAL_ARGUMENT_EXCEPTION, message ); } +// Returns a borrowed reference. +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) { + wchar_t *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_InitializeEx(0); + + { + 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"); + } else { + PySys_SetObject("stdout", m); + PySys_SetObject("stderr", m); + Py_DECREF(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) { - Py_Initialize(); - - hasNumpy = _import_array(); - hasNumpy = hasNumpy != -1; - main_ts = PyEval_SaveThread(); + return 0; } sprintf(name, "SCL_%d", ++moduleCount); PyEval_RestoreThread(main_ts); { - PyObject *module = PyModule_New(name); - PyObject *main = PyImport_AddModule("__main__"); + 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); + PyState_AddModule(module, modDef); + { + PyObject *builtin = PyImport_AddModule("builtins"); + PyObject *dict = PyModule_GetDict(module); + Py_DECREF(module); + PyDict_SetItemString(dict, "__builtin__", builtin); + PyDict_SetItemString(dict, "__builtins__", builtin); - PyDict_Merge(PyModule_GetDict(module), PyModule_GetDict(main), 0); - PyEval_SaveThread(); - return (jlong)module; + PyEval_SaveThread(); + return (jlong)modDef; + } } } -JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_deleteContextImpl(JNIEnv *env, jobject thisObj, jlong contextID) { - PyObject *module = (PyObject*)contextID; +JNIEXPORT void JNICALL +Java_org_simantics_pythonlink_PythonContext_deleteContextImpl( + JNIEnv *env, jobject thisObj, jlong contextID) { + PyModuleDef *modDef = (PyModuleDef*)contextID; PyEval_RestoreThread(main_ts); - Py_XDECREF(module); + PyState_RemoveModule(modDef); + free((char*)modDef->m_name); + free(modDef); PyEval_SaveThread(); } @@ -99,6 +212,7 @@ PyObject *getPythonStringList(JNIEnv *env, jobjectArray value) { PyList_SetItem(result, i, getPythonString(env, (jstring)item)); } else { + Py_INCREF(Py_None); PyList_SetItem(result, i, Py_None); } } @@ -311,8 +425,12 @@ PyObject *getPythonRecordObject(JNIEnv *env, jobject object, jobject binding) { 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); + PyObject + *key = getPythonString(env, fieldName), + *item = getPythonObject(env, componentObject, componentBinding); + PyDict_SetItem(result, key, item); + Py_DECREF(key); + Py_DECREF(item); } return result; @@ -335,8 +453,10 @@ PyObject *getPythonArrayObject(JNIEnv *env, jobject object, jobject binding) { jobject item = (*env)->CallObjectMethod(env, binding, getMethod, object, i); if (item != NULL) PyList_SetItem(result, i, getPythonObject(env, item, componentBinding)); - else + else { + Py_INCREF(Py_None); PyList_SetItem(result, i, Py_None); + } } return result; @@ -365,7 +485,12 @@ PyObject *getPythonMapObject(JNIEnv *env, jobject object, jobject binding) { 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)); + PyObject + *pkey = getPythonObject(env, key, keyBinding), + *pitem = getPythonObject(env, item, valueBinding); + PyDict_SetItem(result, pkey, pitem); + Py_DECREF(pkey); + Py_DECREF(pitem); } (*env)->DeleteLocalRef(env, keys); @@ -496,6 +621,7 @@ PyObject *getPythonObject(JNIEnv *env, jobject value, jobject binding) { } } +// Steals refs to name & value. void setPythonVariable(PyObject *module, PyObject *name, PyObject *value) { if (name && value) { PyDict_SetItem(PyModule_GetDict(module), name, value); @@ -585,9 +711,11 @@ jobjectArray pythonSequenceAsStringArray(JNIEnv *env, PyObject *seq) { 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; } @@ -606,10 +734,12 @@ jdoubleArray pythonSequenceAsDoubleArray(JNIEnv *env, PyObject *seq) { for (i = 0; i < jlen; i++) { PyObject *item = PySequence_GetItem(seq, i); if (PyFloat_Check(item)) { - double value = PyFloat_AsDouble(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; } @@ -649,6 +779,7 @@ jobjectArray pythonSequenceAsObjectArray(JNIEnv *env, PyObject *seq) { 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); } @@ -666,9 +797,11 @@ jbooleanArray pythonSequenceAsBooleanArray(JNIEnv *env, PyObject *seq) { 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; } @@ -688,9 +821,11 @@ jintArray pythonSequenceAsIntegerArray(JNIEnv *env, PyObject *seq) { 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; } @@ -710,9 +845,11 @@ jlongArray pythonSequenceAsLongArray(JNIEnv *env, PyObject *seq) { 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; } @@ -785,445 +922,294 @@ jobject pythonDictionaryAsMap(JNIEnv *env, PyObject *dict) { return map; } -JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonBooleanVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, jboolean value) { - PyObject *module = (PyObject*)contextID; - - PyObject *pythonName = getPythonString(env, variableName); - PyObject *val = getPythonBool(value); - - setPythonVariable(module, pythonName, val); -} - -JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonBooleanArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, jbooleanArray value) { - PyObject *module = (PyObject*)contextID; - - PyObject *pythonName = getPythonString(env, variableName); - PyObject *val = getPythonBooleanList(env, value); - - setPythonVariable(module, pythonName, val); -} - -JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonLongVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, jlong value) { - PyObject *module = (PyObject*)contextID; - - PyObject *pythonName = getPythonString(env, variableName); - PyObject *val = PyLong_FromLongLong(value); - - setPythonVariable(module, pythonName, val); -} - -JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonIntegerArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, jintArray value) { - PyObject *module = (PyObject*)contextID; - - PyObject *pythonName = getPythonString(env, variableName); - PyObject *val = getPythonIntegerList(env, value); - - setPythonVariable(module, pythonName, val); -} - -JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonLongArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, jlongArray value) { - PyObject *module = (PyObject*)contextID; - - PyObject *pythonName = getPythonString(env, variableName); - PyObject *val = getPythonLongList(env, value); - - setPythonVariable(module, pythonName, val); -} - -JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonDoubleVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, jdouble value) { - PyObject *module = (PyObject*)contextID; - - PyObject *pythonName = getPythonString(env, variableName); - PyObject *val = PyFloat_FromDouble(value); - - setPythonVariable(module, pythonName, val); -} - -JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonFloatArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, jfloatArray value) { - PyObject *module = (PyObject*)contextID; - - PyObject *pythonName = getPythonString(env, variableName); - PyObject *val = getPythonFloatList(env, value); - - setPythonVariable(module, pythonName, val); -} - -JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonDoubleArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, jdoubleArray value) { - PyObject *module = (PyObject*)contextID; - - PyObject *pythonName = getPythonString(env, variableName); - PyObject *val = getPythonDoubleList(env, value); - - setPythonVariable(module, pythonName, val); -} - -JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonStringVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, jstring value) { - PyObject *module = (PyObject*)contextID; - - PyObject *pythonName = getPythonString(env, variableName); - PyObject *val = getPythonString(env, value); - - setPythonVariable(module, pythonName, val); -} - -JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonStringArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, jobjectArray value) { - PyObject *module = (PyObject*)contextID; - - PyObject *pythonName = getPythonString(env, variableName); - PyObject *val = getPythonStringList(env, value); - - setPythonVariable(module, pythonName, val); -} - -JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonNDArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, jobject value) { - PyObject *module = (PyObject*)contextID; +#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 = (PyObject*)contextID; - - PyObject *pythonName = getPythonString(env, variableName); - PyObject *val = getPythonObject(env, value, binding); - - setPythonVariable(module, pythonName, val); -} - -JNIEXPORT jint JNICALL Java_org_simantics_pythonlink_PythonContext_executePythonStatementImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring statement) { - PyObject *module = (PyObject*)contextID; - - const char *utfchars = (*env)->GetStringUTFChars(env, statement, NULL); +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); - PyErr_Clear(); - { - PyObject *globals; - - globals = PyModule_GetDict(module); - - { - PyObject *result = PyRun_String(utfchars, Py_file_input, globals, globals); - - PyObject *exceptionType = PyErr_Occurred(); - if (exceptionType != NULL) { - PyObject *exception, *traceback; - PyErr_Fetch(&exceptionType, &exception, &traceback); - - { - PyObject *tracebackModule = PyImport_ImportModule("traceback"); - if (tracebackModule != NULL) { - PyObject *formatExc = PyDict_GetItemString(PyModule_GetDict(tracebackModule), "format_exception"); - if (formatExc != NULL) { - PyObject *args = PyTuple_Pack(3, exceptionType, exception, traceback); - PyObject *message = PyObject_CallObject(formatExc, args); - 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 { - throwPythonException(env, "Internal error, no message"); - } - Py_DECREF(args); - Py_DECREF(formatExc); - } - else { - throwPythonException(env, "Internal error, no format_exc function"); - } - Py_DECREF(tracebackModule); - } - else { - throwPythonException(env, "Internal error, no traceback module"); - } - } - } - - PyEval_SaveThread(); - (*env)->ReleaseStringUTFChars(env, statement, utfchars); - - return result != NULL ? 0 : 1; - } - } + module = getModule(contextID); + setPythonVariable(module, getPythonString(env, variableName), + getPythonObject(env, value, binding)); + PyEval_SaveThread(); } -JNIEXPORT jstring JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonStringVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) { - PyObject *module = (PyObject*)contextID; - - PyObject *pythonName = getPythonString(env, variableName); - - PyObject *value = PyDict_GetItem(PyModule_GetDict(module), pythonName); - if (value == NULL) { - throwPythonException(env, "Python variable not found"); - return 0; - } - - if (!PyUnicode_Check(value)) { - throwPythonException(env, "Python variable not a string"); - return 0; +static PyObject *getExceptionMessage(PyObject *exceptionType, PyObject *exception, PyObject *traceback) { + PyObject *formatExc = NULL, *args = NULL; + PyObject *tracebackModule = PyImport_ImportModule("traceback"); + if (!tracebackModule) { + fputs("Python: No traceback module\n", stderr); + return NULL; } - { - jstring result = pythonStringAsJavaString(env, value); - return result; + PyErr_NormalizeException(&exceptionType, &exception, &traceback); + + if (exception && traceback) { + formatExc = PyDict_GetItemString(PyModule_GetDict(tracebackModule), "format_exception"); + args = PyTuple_Pack(3, exceptionType, exception, traceback); } -} - -JNIEXPORT jobjectArray JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonStringArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) { - PyObject *module = (PyObject*)contextID; - - PyObject *pythonName = getPythonString(env, variableName); - - PyObject *value = PyDict_GetItem(PyModule_GetDict(module), pythonName); - if (value == NULL) { - throwPythonException(env, "Python variable not found"); - return 0; + else if (exception) { + formatExc = PyDict_GetItemString(PyModule_GetDict(tracebackModule), "format_exception_only"); + args = PyTuple_Pack(2, exceptionType, exception); } - if (!PySequence_Check(value)) { - throwPythonException(env, "Python variable not a sequence"); - return 0; - } + if (formatExc != NULL && args != NULL) { + PyObject *result = PyObject_CallObject(formatExc, args); + if (!result) { + fputs("Python: No result from format_exception\n", stderr); + // Fallback to a direct string representation of the exception object + result = PyObject_Str(exception); + } + Py_XDECREF(args); + // Py_XDECREF(formatExc) - Borrowed reference + Py_DECREF(tracebackModule); - { - jobjectArray result = pythonSequenceAsStringArray(env, value); return result; } -} - -JNIEXPORT jboolean JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonBooleanVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) { - PyObject *module = (PyObject*)contextID; + else { + if (!formatExc) fputs("Python: No format_exception\n", stderr); - PyObject *pythonName = getPythonString(env, variableName); + Py_XDECREF(args); + // Py_XDECREF(formatExc) - Borrowed reference + Py_DECREF(tracebackModule); - PyObject *value = PyDict_GetItem(PyModule_GetDict(module), pythonName); - if (value == NULL) { - throwPythonException(env, "Python variable not found"); - return 0; - } - - if (!PyBool_Check(value)) { - throwPythonException(env, "Python variable not a boolean"); - return 0; + return NULL; } - - return value == Py_True; } -JNIEXPORT jbooleanArray JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonBooleanArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) { - PyObject *module = (PyObject*)contextID; +static void throwExceptionType(JNIEnv *env, PyObject *exceptionType) { + PyObject *ty_name = exceptionType ? PyObject_GetAttrString(exceptionType, "__name__") : NULL; + PyObject *str = ty_name ? PyUnicode_AsEncodedString(ty_name, "utf-8", "ignore") : NULL; - PyObject *pythonName = getPythonString(env, variableName); + throwPythonException(env, str ? PyBytes_AsString(str) : "Internal error - no exception type"); - PyObject *value = PyDict_GetItem(PyModule_GetDict(module), pythonName); - if (value == NULL) { - throwPythonException(env, "Python variable not found"); - return 0; - } - - if (!PySequence_Check(value)) { - throwPythonException(env, "Python variable not a sequence"); - return 0; - } - - { - jbooleanArray result = pythonSequenceAsBooleanArray(env, value); - return result; - } + Py_XDECREF(str); + Py_XDECREF(ty_name); } -JNIEXPORT jlong JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonLongVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) { - PyObject *module = (PyObject*)contextID; - - PyObject *pythonName = getPythonString(env, variableName); - - PyObject *value = PyDict_GetItem(PyModule_GetDict(module), pythonName); - if (value == NULL) { - throwPythonException(env, "Python variable not found"); - return 0; - } - - if (!PyLong_Check(value)) { - throwPythonException(env, "Python variable not an integer"); - return 0; - } +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(); { - jlong result = PyLong_AsLongLong(value); - return result; - } -} + PyObject *module = getModule(contextID); -JNIEXPORT jintArray JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonIntegerArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) { - PyObject *module = (PyObject*)contextID; + PyObject *globals; - PyObject *pythonName = getPythonString(env, variableName); + globals = PyModule_GetDict(module); - PyObject *value = PyDict_GetItem(PyModule_GetDict(module), pythonName); - if (value == NULL) { - throwPythonException(env, "Python variable not found"); - return NULL; - } + currentEnv = env; - if (!PySequence_Check(value)) { - throwPythonException(env, "Python variable not a sequence"); - return NULL; - } + { + // PyObject *result = PyRun_String(utfchars, Py_file_input, globals, globals); - Not available in Py_LIMITED_API + PyObject *code = Py_CompileString(utfchars, "SCL_INPUT", Py_file_input); + PyObject *result = code ? PyEval_EvalCode(code,globals, globals) : NULL; + PyObject *exceptionType = PyErr_Occurred(); - { - jintArray result = pythonSequenceAsIntegerArray(env, value); - return result; - } -} + if (exceptionType != NULL) { + PyObject *exception, *traceback, *message; + PyErr_Fetch(&exceptionType, &exception, &traceback); -JNIEXPORT jlongArray JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonLongArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) { - PyObject *module = (PyObject*)contextID; + message = getExceptionMessage(exceptionType, exception, traceback); + if (message != NULL) { + if (PyList_Check(message)) { + PyObject *emptyStr = PyUnicode_FromString(""); + PyObject *temp = PyUnicode_Join(emptyStr, message); + if (temp) { + Py_DECREF(message); + message = temp; + } - PyObject *pythonName = getPythonString(env, variableName); + Py_DECREF(emptyStr); + } - PyObject *value = PyDict_GetItem(PyModule_GetDict(module), pythonName); - if (value == NULL) { - throwPythonException(env, "Python variable not found"); - return NULL; - } + if (!PyUnicode_Check(message)) { + PyObject *temp = PyObject_Str(message); + if (temp) { + Py_DECREF(message); + message = temp; + } + } - if (!PySequence_Check(value)) { - throwPythonException(env, "Python variable not a sequence"); - return NULL; - } + PyObject* str = PyUnicode_AsEncodedString(message, "utf-8", "ignore"); + Py_DECREF(message); - { - jlongArray result = pythonSequenceAsLongArray(env, value); - return result; - } -} + if (str != NULL) { + throwPythonException(env, PyBytes_AsString(str)); + Py_DECREF(str); + } + else { + fputs("Python: Encoding message string failed\n", stderr); + throwExceptionType(env, exceptionType); + } + } + else { + fputs("Python: No exception message\n", stderr); + throwExceptionType(env, exceptionType); + } + } -JNIEXPORT jdouble JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonDoubleVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) { - PyObject *module = (PyObject*)contextID; + Py_XDECREF(result); + Py_XDECREF(code); - PyObject *pythonName = getPythonString(env, variableName); + PyEval_SaveThread(); + (*env)->ReleaseStringUTFChars(env, statement, utfchars); - PyObject *value = PyDict_GetItem(PyModule_GetDict(module), pythonName); - if (value == NULL) { - throwPythonException(env, "Python variable not found"); - return 0.0; - } + currentEnv = NULL; - if (!PyFloat_Check(value)) { - throwPythonException(env, "Python variable not a float"); - return 0.0; - } - - { - jdouble result = PyFloat_AsDouble(value); - return result; + return result != NULL ? 0 : 1; + } } } -JNIEXPORT jdoubleArray JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonDoubleArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) { - PyObject *module = (PyObject*)contextID; - - PyObject *pythonName = getPythonString(env, variableName); - - PyObject *value = PyDict_GetItem(PyModule_GetDict(module), pythonName); - if (value == NULL) { - throwPythonException(env, "Python variable not found"); - return NULL; - } - - if (!PySequence_Check(value)) { - throwPythonException(env, "Python variable not a sequence"); - return NULL; - } +// 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); - { - jdoubleArray result = pythonSequenceAsDoubleArray(env, value); - return result; - } -} + 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; \ + } -JNIEXPORT jobject JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonNDArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) { - PyObject *module = (PyObject*)contextID; +#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; } - { - PyObject *pythonName = getPythonString(env, variableName); - - PyObject *value = PyDict_GetItem(PyModule_GetDict(module), pythonName); - if (value == NULL) { - throwPythonException(env, "Python variable not found"); - 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"); - return NULL; - } - - if (PyArray_TYPE((PyArrayObject*)value) != NPY_DOUBLE) { - throwPythonException(env, "Only ndarrays of type double are supported"); - return NULL; - } - - { - jobject result = pythonArrayAsNDArray(env, (PyArrayObject *)value); - return result; + } else if (PyArray_TYPE((PyArrayObject*)value) != NPY_DOUBLE) { + throwPythonException( + env, "Only ndarrays of type double are supported"); + } else { + result = pythonArrayAsNDArray(env, (PyArrayObject *)value); } - } -} - -JNIEXPORT jobject JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonVariantVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) { - PyObject *module = (PyObject*)contextID; - - PyObject *pythonName = getPythonString(env, variableName); - - PyObject *value = PyDict_GetItem(PyModule_GetDict(module), pythonName); - if (value == NULL) { - throwPythonException(env, "Python variable not found"); - return NULL; - } - - hasNumpy = _import_array() != -1; - - { - jobject result = pythonObjectAsObject(env, value); - return result; - } + } while (0); + PyEval_SaveThread(); + return result; } -JNIEXPORT jint JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonVariableTypeImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) { - PyObject *module = (PyObject*)contextID; - PyObject *dict = PyModule_GetDict(module); +#define python_anything_goes(value) 1 - PyObject *pythonName = getPythonString(env, variableName); - - if (!PyDict_Contains(dict, pythonName)) { - return 0; - } +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 = PyDict_GetItem(dict, pythonName); - - jint result; - + PyObject *value = getPythonValue(env, contextID, variableName); if (PyBool_Check(value)) result = 1; else if (PyLong_Check(value)) @@ -1242,28 +1228,36 @@ JNIEXPORT jint JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonVari result = 8; else result = -1; - - return result; } + PyEval_SaveThread(); + return result; } -JNIEXPORT jobjectArray JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonVariableNamesImpl(JNIEnv *env, jobject thisObj, jlong contextID) { - PyObject *module = (PyObject*)contextID; - PyObject *dict = PyModule_GetDict(module); - - PyObject *keys = PyDict_Keys(dict); - Py_ssize_t size = PyList_Size(keys); +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); - jobjectArray result = (*env)->NewObjectArray(env, (jsize)size, (*env)->FindClass(env, STRING_CLASS), NULL); + PyObject *keys = PyDict_Keys(dict); + Py_ssize_t size = PyList_Size(keys); + Py_ssize_t i; - Py_ssize_t i; - for (i = 0; i < size; i++) { - jstring javaName = pythonStringAsJavaString(env, PyList_GetItem(keys, i)); - (*env)->SetObjectArrayElement(env, result, (jint)i, javaName); - } + result = (*env)->NewObjectArray( + env, (jsize)size, (*env)->FindClass(env, STRING_CLASS), NULL); - Py_XDECREF(keys); + 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; }