1 /*******************************************************************************
2 * Copyright (c) 2017-2019 Association for Decentralized Information Management in
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre - Initial API and implementation
11 * Semantum Oy - Improvements
12 *******************************************************************************/
18 jint throwException( JNIEnv *env, char *className, const char *message )
20 jclass exClass = (*env)->FindClass( env, className);
21 if (exClass == NULL) {
25 return (*env)->ThrowNew( env, exClass, message );
28 jint throwPythonException( JNIEnv *env, const char *message ) {
29 return throwException( env, PYTHON_EXCEPTION, message );
32 jint throwIllegalArgumentException( JNIEnv *env, const char *message ) {
33 return throwException( env, ILLEGAL_ARGUMENT_EXCEPTION, message );
36 // Returns a borrowed reference.
37 PyObject* getModule(jlong contextID) {
38 return PyState_FindModule((PyModuleDef*) contextID);
39 // return PyImport_AddModule("__main__");
44 PyThreadState *main_ts = 0;
46 static JNIEnv *currentEnv = NULL;
47 jobject sclWriter = NULL;
50 writeToSCL(PyObject *self, PyObject *args)
52 if (currentEnv != NULL && sclWriter != NULL) {
55 JNIEnv *env = currentEnv;
57 if (!PyArg_ParseTuple(args, "u#", &what, &length))
61 PyThreadState *my_ts = PyThreadState_Get();
62 if (my_ts == main_ts) {
63 jclass writerClass = (*env)->FindClass(env, WRITER_CLASS);
64 jmethodID writeMethod = (*env)->GetMethodID(env, writerClass, "write", "([CII)V");
65 jcharArray chars = (*env)->NewCharArray(env, (jsize)length);
67 (*env)->SetCharArrayRegion(env, chars, 0, length, what);
68 Py_BEGIN_ALLOW_THREADS
69 (*env)->CallVoidMethod(env, sclWriter, writeMethod, chars, 0, length);
81 flushSCL(PyObject *self, PyObject *args)
83 if (currentEnv != NULL && sclWriter != NULL) {
84 JNIEnv *env = currentEnv;
85 PyThreadState *my_ts = PyThreadState_Get();
86 if (my_ts != main_ts) {
87 // TODO: Process calls from other threads
92 jclass writerClass = (*env)->FindClass(env, WRITER_CLASS);
93 jmethodID flushMethod = (*env)->GetMethodID(env, writerClass, "flush", "()V");
95 Py_BEGIN_ALLOW_THREADS
96 (*env)->CallVoidMethod(env, sclWriter, flushMethod);
104 static PyMethodDef sclWriterMethods[] = {
105 {"write", writeToSCL, METH_VARARGS, "Write something."},
106 {"flush", flushSCL, METH_VARARGS, "Flush output."},
107 {NULL, NULL, 0, NULL}
110 JNIEXPORT void JNICALL
111 Java_org_simantics_pythonlink_PythonContext_initializePython(
112 JNIEnv *env, jobject thisObj, jobject writer) {
116 static struct PyModuleDef moduledef = {
117 PyModuleDef_HEAD_INIT, "sclwriter", NULL, -1, sclWriterMethods
119 PyObject *m = PyModule_Create(&moduledef);
121 sclWriter = (*env)->NewGlobalRef(env, writer);
124 throwException(env, PYTHON_EXCEPTION,
125 "Failed to create SCL writer module");
127 PySys_SetObject("stdout", m);
128 PySys_SetObject("stderr", m);
133 hasNumpy = _import_array();
134 hasNumpy = hasNumpy != -1;
136 main_ts = PyEval_SaveThread();
139 JNIEXPORT jlong JNICALL Java_org_simantics_pythonlink_PythonContext_createContextImpl(JNIEnv *env, jobject thisObj) {
146 sprintf(name, "SCL_%d", ++moduleCount);
148 PyEval_RestoreThread(main_ts);
151 PyModuleDef *modDef = malloc(sizeof(PyModuleDef));
152 memset(modDef, 0, sizeof(PyModuleDef));
153 modDef->m_name = strdup(name);
154 modDef->m_doc = NULL;
157 module = PyModule_Create(modDef);
158 PyState_AddModule(module, modDef);
160 PyObject *builtin = PyImport_AddModule("builtins");
161 PyObject *dict = PyModule_GetDict(module);
163 PyDict_SetItemString(dict, "__builtin__", builtin);
164 PyDict_SetItemString(dict, "__builtins__", builtin);
167 return (jlong)modDef;
172 JNIEXPORT void JNICALL
173 Java_org_simantics_pythonlink_PythonContext_deleteContextImpl(
174 JNIEnv *env, jobject thisObj, jlong contextID) {
175 PyModuleDef *modDef = (PyModuleDef*)contextID;
176 PyEval_RestoreThread(main_ts);
177 PyState_RemoveModule(modDef);
178 free((char*)modDef->m_name);
183 PyObject *getPythonBool(jboolean value) {
192 PyObject *getPythonString(JNIEnv *env, jstring string) {
193 jsize len = (*env)->GetStringLength(env, string);
194 const jchar *chars = (*env)->GetStringChars(env, string, NULL);
196 PyObject *value = PyUnicode_DecodeUTF16((char*)chars, 2*len, NULL, NULL);
198 (*env)->ReleaseStringChars(env, string, chars);
202 PyObject *getPythonStringList(JNIEnv *env, jobjectArray value) {
203 jsize nitems = (*env)->GetArrayLength(env, value);
204 jint *values = (*env)->GetIntArrayElements(env, value, NULL);
205 jclass stringClass = (*env)->FindClass(env, STRING_CLASS);
208 PyObject *result = PyList_New(nitems);
209 for (i = 0; i < nitems; i++) {
210 jobject item = (*env)->GetObjectArrayElement(env, value, i);
211 if (item != NULL && (*env)->IsInstanceOf(env, item, stringClass)) {
212 PyList_SetItem(result, i, getPythonString(env, (jstring)item));
216 PyList_SetItem(result, i, Py_None);
220 (*env)->ReleaseIntArrayElements(env, value, values, JNI_ABORT);
224 PyObject *getPythonBooleanList(JNIEnv *env, jbooleanArray value) {
225 jsize nitems = (*env)->GetArrayLength(env, value);
226 jboolean *values = (*env)->GetBooleanArrayElements(env, value, NULL);
229 PyObject *result = PyList_New(nitems);
230 for (i = 0; i < nitems; i++) {
231 PyList_SetItem(result, i, getPythonBool(values[i]));
234 (*env)->ReleaseBooleanArrayElements(env, value, values, JNI_ABORT);
238 PyObject *getPythonByteArray(JNIEnv *env, jbyteArray value) {
239 jint len = (*env)->GetArrayLength(env, value);
240 jbyte *values = (*env)->GetByteArrayElements(env, value, NULL);
242 PyObject *result = PyByteArray_FromStringAndSize(values, len);
244 (*env)->ReleaseByteArrayElements(env, value, values, JNI_ABORT);
248 PyObject *getPythonIntegerList(JNIEnv *env, jintArray value) {
249 jsize nitems = (*env)->GetArrayLength(env, value);
250 jint *values = (*env)->GetIntArrayElements(env, value, NULL);
253 PyObject *result = PyList_New(nitems);
254 for (i = 0; i < nitems; i++) {
255 PyList_SetItem(result, i, PyLong_FromLong(values[i]));
258 (*env)->ReleaseIntArrayElements(env, value, values, JNI_ABORT);
262 PyObject *getPythonLongList(JNIEnv *env, jlongArray value) {
263 jsize nitems = (*env)->GetArrayLength(env, value);
264 jlong *values = (*env)->GetLongArrayElements(env, value, NULL);
267 PyObject *result = PyList_New(nitems);
268 for (i = 0; i < nitems; i++) {
269 PyList_SetItem(result, i, PyLong_FromLongLong(values[i]));
272 (*env)->ReleaseLongArrayElements(env, value, values, JNI_ABORT);
276 PyObject *getPythonFloatList(JNIEnv *env, jfloatArray value) {
277 jsize nitems = (*env)->GetArrayLength(env, value);
278 float *values = (*env)->GetFloatArrayElements(env, value, NULL);
281 PyObject *result = PyList_New(nitems);
282 for (i = 0; i < nitems; i++) {
283 PyList_SetItem(result, i, PyFloat_FromDouble((double)values[i]));
286 (*env)->ReleaseFloatArrayElements(env, value, values, JNI_ABORT);
290 PyObject *getPythonDoubleList(JNIEnv *env, jdoubleArray value) {
291 jsize nitems = (*env)->GetArrayLength(env, value);
292 double *values = (*env)->GetDoubleArrayElements(env, value, NULL);
295 PyObject *result = PyList_New(nitems);
296 for (i = 0; i < nitems; i++) {
297 PyList_SetItem(result, i, PyFloat_FromDouble(values[i]));
300 (*env)->ReleaseDoubleArrayElements(env, value, values, JNI_ABORT);
304 PyObject *getPythonNDArray(JNIEnv *env, jobject value) {
305 jclass ndarrayClass = (*env)->FindClass(env, NDARRAY_CLASS);
306 jmethodID dimsMethod = (*env)->GetMethodID(env, ndarrayClass, "dims", "()[I");
307 jmethodID getValuesMethod = (*env)->GetMethodID(env, ndarrayClass, "getValues", "()[D");
309 jintArray jdims = (*env)->CallObjectMethod(env, value, dimsMethod);
310 jsize ndims = (*env)->GetArrayLength(env, jdims);
311 jint *dims = (*env)->GetIntArrayElements(env, jdims, NULL);
313 jdoubleArray jvalues = (*env)->CallObjectMethod(env, value, getValuesMethod);
314 jsize len = (*env)->GetArrayLength(env, jvalues);
315 jdouble *values = (*env)->GetDoubleArrayElements(env, jvalues, NULL);
317 npy_intp *pyDims = (npy_intp*)malloc(ndims * sizeof(npy_intp));
319 jint i, nelem = ndims > 0 ? 1 : 0;
320 for (i = 0; i < ndims; i++) {
325 len = min(len, nelem);
328 PyObject *array = PyArray_EMPTY(ndims, pyDims, NPY_DOUBLE, 0);
329 double *data = (double *)PyArray_DATA((PyArrayObject*)array);
331 memcpy(data, values, len * sizeof(double));
335 (*env)->ReleaseDoubleArrayElements(env, jvalues, values, JNI_ABORT);
336 (*env)->ReleaseIntArrayElements(env, jdims, dims, JNI_ABORT);
342 PyObject *getPythonBooleanObject(JNIEnv *env, jobject object, jobject binding) {
343 jclass bindingClass = (*env)->FindClass(env, BOOLEANBINDING_CLASS);
344 jmethodID getValueMethod = (*env)->GetMethodID(env, bindingClass, "getValue_", "(L" OBJECT_CLASS ";)Z");
346 jboolean bvalue = (*env)->CallBooleanMethod(env, binding, getValueMethod, object);
347 return getPythonBool(bvalue);
350 PyObject *getPythonByteObject(JNIEnv *env, jobject object, jobject binding) {
351 jclass bindingClass = (*env)->FindClass(env, BYTEBINDING_CLASS);
352 jmethodID getValueMethod = (*env)->GetMethodID(env, bindingClass, "getValue_", "(L" OBJECT_CLASS ";)B");
354 jbyte v = (*env)->CallByteMethod(env, binding, getValueMethod, object);
355 return PyLong_FromLong(v);
358 PyObject *getPythonIntegerObject(JNIEnv *env, jobject object, jobject binding) {
359 jclass bindingClass = (*env)->FindClass(env, INTEGERBINDING_CLASS);
360 jmethodID getValueMethod = (*env)->GetMethodID(env, bindingClass, "getValue_", "(L" OBJECT_CLASS ";)I");
362 jint v = (*env)->CallIntMethod(env, binding, getValueMethod, object);
363 return PyLong_FromLong(v);
366 PyObject *getPythonLongObject(JNIEnv *env, jobject object, jobject binding) {
367 jclass bindingClass = (*env)->FindClass(env, LONGBINDING_CLASS);
368 jmethodID getValueMethod = (*env)->GetMethodID(env, bindingClass, "getValue_", "(L" OBJECT_CLASS ";)J");
370 jlong v = (*env)->CallLongMethod(env, binding, getValueMethod, object);
371 return PyLong_FromLongLong(v);
374 PyObject *getPythonFloatObject(JNIEnv *env, jobject object, jobject binding) {
375 jclass bindingClass = (*env)->FindClass(env, FLOATBINDING_CLASS);
376 jmethodID getValueMethod = (*env)->GetMethodID(env, bindingClass, "getValue_", "(L" OBJECT_CLASS ";)F");
378 jfloat v = (*env)->CallFloatMethod(env, binding, getValueMethod, object);
379 return PyFloat_FromDouble(v);
382 PyObject *getPythonDoubleObject(JNIEnv *env, jobject object, jobject binding) {
383 jclass bindingClass = (*env)->FindClass(env, DOUBLEBINDING_CLASS);
384 jmethodID getValueMethod = (*env)->GetMethodID(env, bindingClass, "getValue_", "(L" OBJECT_CLASS ";)D");
386 jdouble v = (*env)->CallDoubleMethod(env, binding, getValueMethod, object);
387 return PyFloat_FromDouble(v);
390 PyObject *getPythonStringObject(JNIEnv *env, jobject object, jobject binding) {
391 jclass bindingClass = (*env)->FindClass(env, STRINGBINDING_CLASS);
392 jmethodID getValueMethod = (*env)->GetMethodID(env, bindingClass, "getValue", "(L" OBJECT_CLASS ";)L" STRING_CLASS ";");
394 jobject string = (*env)->CallObjectMethod(env, binding, getValueMethod, object);
395 jsize len = (*env)->GetStringLength(env, string);
396 const jchar *chars = (*env)->GetStringChars(env, string, NULL);
398 PyObject *value = PyUnicode_DecodeUTF16((char*)chars, 2*len, NULL, NULL);
400 (*env)->ReleaseStringChars(env, string, chars);
404 PyObject *getPythonRecordObject(JNIEnv *env, jobject object, jobject binding) {
405 jclass bindingClass = (*env)->FindClass(env, RECORDBINDING_CLASS);
406 jmethodID typeMethod = (*env)->GetMethodID(env, bindingClass, "type", "()L" RECORDTYPE_CLASS ";");
407 jmethodID getComponent = (*env)->GetMethodID(env, bindingClass, "getComponent", "(L" OBJECT_CLASS ";I)L" OBJECT_CLASS ";");
408 jmethodID getComponentBinding = (*env)->GetMethodID(env, bindingClass, "getComponentBinding", "(I)L" BINDING_CLASS ";");
410 jclass recordType = (*env)->FindClass(env, RECORDTYPE_CLASS);
411 jmethodID getTypeComponent = (*env)->GetMethodID(env, recordType, "getComponent", "(I)L" COMPONENT_CLASS ";");
412 jmethodID getComponentCount = (*env)->GetMethodID(env, recordType, "getComponentCount", "()I");
414 jclass componentClass = (*env)->FindClass(env, COMPONENT_CLASS);
415 jfieldID nameField = (*env)->GetFieldID(env, componentClass, "name", "L" STRING_CLASS ";");
417 jobject type = (*env)->CallObjectMethod(env, binding, typeMethod);
418 jint n = (*env)->CallIntMethod(env, type, getComponentCount);
421 PyObject *result = PyDict_New();
422 for (i = 0; i < n; i++) {
423 jobject recordTypeComponent = (*env)->CallObjectMethod(env, type, getComponent, i);
424 jstring fieldName = (jstring)(*env)->GetObjectField(env, recordTypeComponent, nameField);
425 jobject componentObject = (*env)->CallObjectMethod(env, binding, getComponent, object, i);
426 jobject componentBinding = (*env)->CallObjectMethod(env, binding, getComponentBinding, i);
429 *key = getPythonString(env, fieldName),
430 *item = getPythonObject(env, componentObject, componentBinding);
431 PyDict_SetItem(result, key, item);
439 PyObject *getPythonArrayObject(JNIEnv *env, jobject object, jobject binding) {
440 jclass bindingClass = (*env)->FindClass(env, ARRAYBINDING_CLASS);
441 jmethodID componentBindingMethod = (*env)->GetMethodID(env, bindingClass, "getComponentBinding", "()L" BINDING_CLASS ";");
442 jmethodID sizeMethod = (*env)->GetMethodID(env, bindingClass, "size", "(L" OBJECT_CLASS ";)I");
443 jmethodID getMethod = (*env)->GetMethodID(env, bindingClass, "get", "(L" OBJECT_CLASS ";I)L" OBJECT_CLASS ";");
445 jobject componentBinding = (*env)->CallObjectMethod(env, binding, componentBindingMethod);
447 jint size = (*env)->CallIntMethod(env, binding, sizeMethod, object);
449 PyObject *result = PyList_New(size);
452 for (i = 0; i < size; i++) {
453 jobject item = (*env)->CallObjectMethod(env, binding, getMethod, object, i);
455 PyList_SetItem(result, i, getPythonObject(env, item, componentBinding));
458 PyList_SetItem(result, i, Py_None);
465 PyObject *getPythonMapObject(JNIEnv *env, jobject object, jobject binding) {
466 jclass objectClass = (*env)->FindClass(env, OBJECT_CLASS);
467 jclass bindingClass = (*env)->FindClass(env, MAPBINDING_CLASS);
468 jmethodID getKeyBindingMethod = (*env)->GetMethodID(env, bindingClass, "getKeyBinding", "()L" BINDING_CLASS ";");
469 jmethodID getValueBindingMethod = (*env)->GetMethodID(env, bindingClass, "getValueBinding", "()L" BINDING_CLASS ";");
470 jmethodID sizeMethod = (*env)->GetMethodID(env, bindingClass, "size", "(L" OBJECT_CLASS ";)I");
471 jmethodID getAllMethod = (*env)->GetMethodID(env, bindingClass, "getAll", "(L" OBJECT_CLASS ";[L" OBJECT_CLASS ";[L" OBJECT_CLASS ";)V");
473 jobject keyBinding = (*env)->CallObjectMethod(env, binding, getKeyBindingMethod);
474 jobject valueBinding = (*env)->CallObjectMethod(env, binding, getValueBindingMethod);
476 jint size = (*env)->CallIntMethod(env, binding, sizeMethod, object);
477 jobjectArray keys = (*env)->NewObjectArray(env, size, objectClass, NULL);
478 jobjectArray values = (*env)->NewObjectArray(env, size, objectClass, NULL);
480 PyObject *result = PyDict_New();
483 (*env)->CallVoidMethod(env, binding, getAllMethod, object, keys, values);
485 for (i = 0; i < size; i++) {
486 jobject key = (*env)->GetObjectArrayElement(env, keys, i);
487 jobject item = (*env)->GetObjectArrayElement(env, values, i);
489 *pkey = getPythonObject(env, key, keyBinding),
490 *pitem = getPythonObject(env, item, valueBinding);
491 PyDict_SetItem(result, pkey, pitem);
496 (*env)->DeleteLocalRef(env, keys);
497 (*env)->DeleteLocalRef(env, values);
502 PyObject *getPythonOptionalObject(JNIEnv *env, jobject object, jobject binding) {
503 jclass bindingClass = (*env)->FindClass(env, OPTIONALBINDING_CLASS);
504 jmethodID hasValueMethod = (*env)->GetMethodID(env, bindingClass, "hasValue", "(L" OBJECT_CLASS ";)Z");
506 jboolean hasValue = (*env)->CallBooleanMethod(env, binding, hasValueMethod, object);
509 jmethodID componentBindingMethod = (*env)->GetMethodID(env, bindingClass, "getComponentBinding", "()L" BINDING_CLASS ";");
510 jmethodID getValueMethod = (*env)->GetMethodID(env, bindingClass, "hasValue", "(L" OBJECT_CLASS ";)L" OBJECT_CLASS ";");
512 jobject componentBinding = (*env)->CallObjectMethod(env, binding, componentBindingMethod);
513 jobject value = (*env)->CallObjectMethod(env, binding, getValueMethod, object);
515 return getPythonObject(env, value, componentBinding);
522 PyObject *getPythonUnionObject(JNIEnv *env, jobject object, jobject binding) {
523 jclass bindingClass = (*env)->FindClass(env, UNIONBINDING_CLASS);
524 jmethodID typeMethod = (*env)->GetMethodID(env, bindingClass, "type", "()L" RECORDTYPE_CLASS ";");
525 jmethodID getTagMethod = (*env)->GetMethodID(env, bindingClass, "getTag", "(L" OBJECT_CLASS ";)I");
526 jmethodID getValueMethod = (*env)->GetMethodID(env, bindingClass, "getValue", "(L" OBJECT_CLASS ";)L" OBJECT_CLASS ";");
527 jmethodID getComponentBinding = (*env)->GetMethodID(env, bindingClass, "getComponentBinding", "(I)L" BINDING_CLASS ";");
529 jclass unionType = (*env)->FindClass(env, UNIONTYPE_CLASS);
530 jmethodID getTypeComponent = (*env)->GetMethodID(env, unionType, "getComponent", "(I)L" COMPONENT_CLASS ";");
532 jclass componentClass = (*env)->FindClass(env, COMPONENT_CLASS);
533 jfieldID nameField = (*env)->GetFieldID(env, componentClass, "name", "L" STRING_CLASS ";");
535 jint tag = (*env)->CallIntMethod(env, binding, getTagMethod, object);
536 jobject value = (*env)->CallObjectMethod(env, binding, getValueMethod, object);
538 jobject type = (*env)->CallObjectMethod(env, binding, typeMethod);
539 jobject typeComponent = (*env)->CallObjectMethod(env, type, getTypeComponent, tag);
540 jstring compName = (*env)->GetObjectField(env, typeComponent, nameField);
542 jobject componentBinding = (*env)->CallObjectMethod(env, binding, getComponentBinding, tag);
544 PyObject *result = PyTuple_New(2);
545 PyTuple_SetItem(result, 0, getPythonString(env, compName));
546 PyTuple_SetItem(result, 1, getPythonObject(env, value, componentBinding));
551 PyObject *getPythonVariantObject(JNIEnv *env, jobject object, jobject binding) {
552 jclass bindingClass = (*env)->FindClass(env, VARIANTBINDING_CLASS);
553 jmethodID getContentMethod = (*env)->GetMethodID(env, bindingClass, "getContent", "(L" OBJECT_CLASS ";)L" OBJECT_CLASS ";");
554 jmethodID getContentBindingMethod = (*env)->GetMethodID(env, bindingClass, "getContentBinding", "(L" OBJECT_CLASS ";)L" BINDING_CLASS ";");
556 jobject content = (*env)->CallObjectMethod(env, binding, getContentMethod, object);
557 jobject contentBinding = (*env)->CallObjectMethod(env, binding, getContentBindingMethod, object);
559 return getPythonObject(env, content, contentBinding);
562 PyObject *getPythonObject(JNIEnv *env, jobject value, jobject binding) {
563 jclass booleanBinding = (*env)->FindClass(env, BOOLEANBINDING_CLASS);
564 jclass byteBinding = (*env)->FindClass(env, BYTEBINDING_CLASS);
565 jclass integerBinding = (*env)->FindClass(env, INTEGERBINDING_CLASS);
566 jclass longBinding = (*env)->FindClass(env, LONGBINDING_CLASS);
567 jclass floatBinding = (*env)->FindClass(env, FLOATBINDING_CLASS);
568 jclass doubleBinding = (*env)->FindClass(env, DOUBLEBINDING_CLASS);
569 jclass stringBinding = (*env)->FindClass(env, STRINGBINDING_CLASS);
570 jclass recordBinding = (*env)->FindClass(env, RECORDBINDING_CLASS);
571 jclass arrayBinding = (*env)->FindClass(env, ARRAYBINDING_CLASS);
572 jclass mapBinding = (*env)->FindClass(env, MAPBINDING_CLASS);
573 jclass optionalBinding = (*env)->FindClass(env, OPTIONALBINDING_CLASS);
574 jclass untionBinding = (*env)->FindClass(env, UNIONBINDING_CLASS);
575 jclass variantBinding = (*env)->FindClass(env, VARIANTBINDING_CLASS);
580 if ((*env)->IsInstanceOf(env, binding, booleanBinding)) {
581 return getPythonBooleanObject(env, value, binding);
583 else if ((*env)->IsInstanceOf(env, binding, byteBinding)) {
584 return getPythonByteObject(env, value, binding);
586 else if ((*env)->IsInstanceOf(env, binding, integerBinding)) {
587 return getPythonIntegerObject(env, value, binding);
589 else if ((*env)->IsInstanceOf(env, binding, longBinding)) {
590 return getPythonLongObject(env, value, binding);
592 else if ((*env)->IsInstanceOf(env, binding, floatBinding)) {
593 return getPythonFloatObject(env, value, binding);
595 else if ((*env)->IsInstanceOf(env, binding, doubleBinding)) {
596 return getPythonDoubleObject(env, value, binding);
598 else if ((*env)->IsInstanceOf(env, binding, stringBinding)) {
599 return getPythonStringObject(env, value, binding);
601 else if ((*env)->IsInstanceOf(env, binding, recordBinding)) {
602 return getPythonRecordObject(env, value, binding);
604 else if ((*env)->IsInstanceOf(env, binding, arrayBinding)) {
605 return getPythonArrayObject(env, value, binding);
607 else if ((*env)->IsInstanceOf(env, binding, mapBinding)) {
608 return getPythonMapObject(env, value, binding);
610 else if ((*env)->IsInstanceOf(env, binding, optionalBinding)) {
611 return getPythonOptionalObject(env, value, binding);
613 else if ((*env)->IsInstanceOf(env, binding, untionBinding)) {
614 return getPythonUnionObject(env, value, binding);
616 else if ((*env)->IsInstanceOf(env, binding, variantBinding)) {
617 return getPythonVariantObject(env, value, binding);
624 // Steals refs to name & value.
625 void setPythonVariable(PyObject *module, PyObject *name, PyObject *value) {
627 PyDict_SetItem(PyModule_GetDict(module), name, value);
634 static npy_intp nContiguous(int d, int nd, npy_intp *strides, npy_intp *dims, npy_intp *ncont) {
640 npy_intp n = nContiguous(d+1, nd, strides, dims, ncont);
641 ncont[d] = n > 0 && strides[d] == sizeof(double) * n ? dims[d] * n : 0;
646 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) {
648 (*env)->SetDoubleArrayRegion(env, array, (jint)*offset, (jint)ncont[d], data);
653 for (i = 0; i < dims[d]; i++) {
654 copyDoubleArrayValues(env, array, (double*)((char*)data + strides[d] * i), offset, d+1, nd, strides, dims, ncont);
659 jobject pythonBoolAsBooleanObject(JNIEnv *env, PyObject *value) {
660 jclass booleanClass = (*env)->FindClass(env, "java/lang/Boolean");
661 jmethodID valueOfMethod = (*env)->GetStaticMethodID(env, booleanClass, "valueOf", "(Z)Ljava/lang/Boolean;");
663 return (*env)->CallStaticObjectMethod(env, booleanClass, valueOfMethod, (jboolean)(value == Py_True));
666 jobject pythonLongAsLongObject(JNIEnv *env, PyObject *value) {
667 jclass longClass = (*env)->FindClass(env, "java/lang/Long");
668 jmethodID valueOfMethod = (*env)->GetStaticMethodID(env, longClass, "valueOf", "(J)Ljava/lang/Long;");
670 return (*env)->CallStaticObjectMethod(env, longClass, valueOfMethod, PyLong_AsLongLong(value));
673 jobject pythonFloatAsDoubleObject(JNIEnv *env, PyObject *value) {
674 jclass doubleClass = (*env)->FindClass(env, "java/lang/Double");
675 jmethodID valueOfMethod = (*env)->GetStaticMethodID(env, doubleClass, "valueOf", "(D)Ljava/lang/Double;");
677 return (*env)->CallStaticObjectMethod(env, doubleClass, valueOfMethod, PyFloat_AsDouble(value));
680 jobject pythonByteArrayAsByteArray(JNIEnv *env, PyObject *value) {
681 Py_ssize_t size = PyByteArray_Size(value);
682 jbyteArray result = (*env)->NewByteArray(env, (jsize)size);
683 char *bytes = PyByteArray_AsString(value);
685 (*env)->SetByteArrayRegion(env, result, 0, size, bytes);
690 jstring pythonStringAsJavaString(JNIEnv *env, PyObject *string) {
691 PyObject *utf16Value = PyUnicode_AsUTF16String(string);
692 Py_ssize_t len = PyBytes_Size(utf16Value) / 2;
693 char *bytes = PyBytes_AsString(utf16Value);
695 // Create Java string, skipping the byte order mark in the beginning
696 jstring result = (*env)->NewString(env, (jchar *)bytes + 1, (jsize)min(len, JAVA_MAXINT) - 1);
698 Py_XDECREF(utf16Value);
703 jobjectArray pythonSequenceAsStringArray(JNIEnv *env, PyObject *seq) {
704 Py_ssize_t len = PySequence_Size(seq);
705 jsize jlen = (jsize)min(len, JAVA_MAXINT);
706 jobjectArray array = (*env)->NewObjectArray(env, jlen, (*env)->FindClass(env, STRING_CLASS), NULL);
710 for (i = 0; i < jlen; i++) {
711 PyObject *item = PySequence_GetItem(seq, i);
712 if (PyUnicode_Check(item)) {
713 jstring value = pythonStringAsJavaString(env, item);
715 (*env)->SetObjectArrayElement(env, array, i, value);
719 throwPythonException(env, "List item not a string");
727 jdoubleArray pythonSequenceAsDoubleArray(JNIEnv *env, PyObject *seq) {
728 Py_ssize_t len = PySequence_Size(seq);
729 jsize jlen = (jsize)min(len, JAVA_MAXINT);
730 jdoubleArray array = (*env)->NewDoubleArray(env, jlen);
734 for (i = 0; i < jlen; i++) {
735 PyObject *item = PySequence_GetItem(seq, i);
736 if (PyFloat_Check(item)) {
737 jdouble value = PyFloat_AsDouble(item);
739 (*env)->SetDoubleArrayRegion(env, array, i, 1, &value);
743 throwPythonException(env, "List item not a floating point value");
751 jobject pythonObjectAsObject(JNIEnv *env, PyObject *value) {
752 if (PyBool_Check(value))
753 return pythonBoolAsBooleanObject(env, value);
754 else if (PyLong_Check(value))
755 return pythonLongAsLongObject(env, value);
756 else if (PyFloat_Check(value))
757 return pythonFloatAsDoubleObject(env, value);
758 else if (PyUnicode_Check(value))
759 return pythonStringAsJavaString(env, value);
760 else if (PyByteArray_Check(value))
761 return pythonByteArrayAsByteArray(env, value);
762 else if (PyDict_Check(value))
763 return pythonDictionaryAsMap(env, value);
764 else if (hasNumpy && PyArray_Check(value))
765 return pythonArrayAsNDArray(env, (PyArrayObject *)value);
766 else if (PySequence_Check(value))
767 return pythonSequenceAsObjectArray(env, value);
772 jobjectArray pythonSequenceAsObjectArray(JNIEnv *env, PyObject *seq) {
773 Py_ssize_t len = PySequence_Size(seq);
774 jsize jlen = (jsize)min(len, JAVA_MAXINT);
775 jobjectArray array = (*env)->NewObjectArray(env, jlen, (*env)->FindClass(env, OBJECT_CLASS), NULL);
779 for (i = 0; i < jlen; i++) {
780 PyObject *item = PySequence_GetItem(seq, i);
781 jobject object = pythonObjectAsObject(env, item);
783 (*env)->SetObjectArrayElement(env, array, i, object);
789 jbooleanArray pythonSequenceAsBooleanArray(JNIEnv *env, PyObject *seq) {
790 Py_ssize_t len = PySequence_Size(seq);
791 jsize jlen = (jsize)min(len, JAVA_MAXINT);
792 jbooleanArray array = (*env)->NewBooleanArray(env, jlen);
796 for (i = 0; i < jlen; i++) {
797 PyObject *item = PySequence_GetItem(seq, i);
798 if (PyBool_Check(item)) {
799 jboolean value = item == Py_True;
801 (*env)->SetBooleanArrayRegion(env, array, i, 1, &value);
805 throwPythonException(env, "List item not a boolean");
813 jintArray pythonSequenceAsIntegerArray(JNIEnv *env, PyObject *seq) {
814 Py_ssize_t len = PySequence_Size(seq);
815 jsize jlen = (jsize)min(len, JAVA_MAXINT);
816 jintArray array = (*env)->NewIntArray(env, jlen);
820 for (i = 0; i < jlen; i++) {
821 PyObject *item = PySequence_GetItem(seq, i);
822 if (PyLong_Check(item)) {
823 jint value = PyLong_AsLong(item);
825 (*env)->SetIntArrayRegion(env, array, i, 1, &value);
829 throwPythonException(env, "List item not an integer");
837 jlongArray pythonSequenceAsLongArray(JNIEnv *env, PyObject *seq) {
838 Py_ssize_t len = PySequence_Size(seq);
839 jsize jlen = (jsize)min(len, JAVA_MAXINT);
840 jlongArray array = (*env)->NewLongArray(env, jlen);
844 for (i = 0; i < jlen; i++) {
845 PyObject *item = PySequence_GetItem(seq, i);
846 if (PyLong_Check(item)) {
847 jlong value = PyLong_AsLongLong(item);
849 (*env)->SetLongArrayRegion(env, array, i, 1, &value);
853 throwPythonException(env, "List item not an integer");
861 jobject pythonArrayAsNDArray(JNIEnv *env, PyArrayObject *array) {
862 jclass ndarrayClass = (*env)->FindClass(env, NDARRAY_CLASS);
863 jmethodID constructor = (*env)->GetMethodID(env, ndarrayClass, "<init>", "([I[D)V");
865 int ndims = PyArray_NDIM(array);
866 npy_intp *dims = PyArray_DIMS(array);
868 npy_intp len = PyArray_Size((PyObject*)array);
869 double *values = (double*)PyArray_DATA(array);
871 jboolean isFortran = PyArray_ISFORTRAN(array) != 0;
875 if (len > JAVA_MAXINT) {
876 throwPythonException(env, "Array too large");
881 jintArray jdims = (*env)->NewIntArray(env, ndims);
882 jdoubleArray jvalues = (*env)->NewDoubleArray(env, (jsize)len);
884 for (i = 0; i < ndims; i++) {
885 jint dim = (jint)dims[i];
886 (*env)->SetIntArrayRegion(env, jdims, i, 1, &dim);
889 if (PyArray_IS_C_CONTIGUOUS(array)) {
890 (*env)->SetDoubleArrayRegion(env, jvalues, 0, (jsize)len, values);
894 npy_intp *strides = PyArray_STRIDES(array);
895 npy_intp *ncont = (npy_intp*)malloc((ndims + 1) * sizeof(npy_intp));
896 nContiguous(0, ndims, strides, dims, ncont);
897 copyDoubleArrayValues(env, jvalues, values, &offset, 0, ndims, strides, dims, ncont);
901 return (*env)->NewObject(env, ndarrayClass, constructor, jdims, jvalues, isFortran);
905 jobject pythonDictionaryAsMap(JNIEnv *env, PyObject *dict) {
906 jclass hashmapClass = (*env)->FindClass(env, "java/util/HashMap");
907 jmethodID constructor = (*env)->GetMethodID(env, hashmapClass, "<init>", "(I)V");
908 jmethodID putMethod = (*env)->GetMethodID(env, hashmapClass, "put", "(L" OBJECT_CLASS ";L" OBJECT_CLASS ";)L" OBJECT_CLASS ";");
910 Py_ssize_t size = PyDict_Size(dict);
911 jobject map = (*env)->NewObject(env, hashmapClass, constructor, (jint)size);
913 PyObject *key, *value;
916 while (PyDict_Next(dict, &pos, &key, &value)) {
917 jobject keyObject = pythonObjectAsObject(env, key);
918 jobject valueObject = pythonObjectAsObject(env, value);
919 (*env)->CallObjectMethod(env, map, putMethod, keyObject, valueObject);
925 #define DEF_SETTER(typename, jtype, j2py) \
926 JNIEXPORT void JNICALL \
927 Java_org_simantics_pythonlink_PythonContext_setPython##typename \
929 JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, \
933 PyEval_RestoreThread(main_ts); \
934 module = getModule(contextID); \
935 setPythonVariable(module, getPythonString(env, variableName), \
937 PyEval_SaveThread(); \
940 #define getPythonBoolean(env, value) getPythonBool(value)
941 #define getPythonLong(env, value) PyLong_FromLongLong(value)
942 #define getPythonDouble(env, value) PyFloat_FromDouble(value)
944 DEF_SETTER(Boolean, jboolean, getPythonBoolean)
945 DEF_SETTER(BooleanArray, jbooleanArray, getPythonBooleanList)
946 DEF_SETTER(Long, jlong, getPythonLong)
947 DEF_SETTER(IntegerArray, jintArray, getPythonIntegerList)
948 DEF_SETTER(LongArray, jlongArray, getPythonLongList)
949 DEF_SETTER(Double, jdouble, getPythonDouble)
950 DEF_SETTER(FloatArray, jfloatArray, getPythonFloatList)
951 DEF_SETTER(DoubleArray, jdoubleArray, getPythonDoubleList)
952 DEF_SETTER(String, jstring, getPythonString)
953 DEF_SETTER(StringArray, jobjectArray, getPythonStringList)
955 JNIEXPORT void JNICALL
956 Java_org_simantics_pythonlink_PythonContext_setPythonNDArrayVariableImpl(
957 JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName,
960 throwPythonException(env, "Importing numpy failed");
964 PyEval_RestoreThread(main_ts);
966 PyObject *module = getModule(contextID);
967 PyObject *pythonName = getPythonString(env, variableName);
968 PyObject *val = getPythonNDArray(env, value);
970 setPythonVariable(module, pythonName, val);
975 JNIEXPORT void JNICALL
976 Java_org_simantics_pythonlink_PythonContext_setPythonVariantVariableImpl(
977 JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName,
978 jobject value, jobject binding) {
981 PyEval_RestoreThread(main_ts);
982 module = getModule(contextID);
983 setPythonVariable(module, getPythonString(env, variableName),
984 getPythonObject(env, value, binding));
988 static PyObject *getExceptionMessage(PyObject *exceptionType, PyObject *exception, PyObject *traceback) {
989 PyObject *formatExc = NULL, *args = NULL;
990 PyObject *tracebackModule = PyImport_ImportModule("traceback");
991 if (!tracebackModule) {
992 fputs("Python: No traceback module\n", stderr);
996 PyErr_NormalizeException(&exceptionType, &exception, &traceback);
998 if (exception && traceback) {
999 formatExc = PyDict_GetItemString(PyModule_GetDict(tracebackModule), "format_exception");
1000 args = PyTuple_Pack(3, exceptionType, exception, traceback);
1002 else if (exception) {
1003 formatExc = PyDict_GetItemString(PyModule_GetDict(tracebackModule), "format_exception_only");
1004 args = PyTuple_Pack(2, exceptionType, exception);
1007 if (formatExc != NULL && args != NULL) {
1008 PyObject *result = PyObject_CallObject(formatExc, args);
1010 fputs("Python: No result from format_exception\n", stderr);
1011 // Fallback to a direct string representation of the exception object
1012 result = PyObject_Str(exception);
1015 // Py_XDECREF(formatExc) - Borrowed reference
1016 Py_DECREF(tracebackModule);
1021 if (!formatExc) fputs("Python: No format_exception\n", stderr);
1024 // Py_XDECREF(formatExc) - Borrowed reference
1025 Py_DECREF(tracebackModule);
1031 static void throwExceptionType(JNIEnv *env, PyObject *exceptionType) {
1032 PyObject *ty_name = exceptionType ? PyObject_GetAttrString(exceptionType, "__name__") : NULL;
1033 PyObject *str = ty_name ? PyUnicode_AsEncodedString(ty_name, "utf-8", "ignore") : NULL;
1035 throwPythonException(env, str ? PyBytes_AsString(str) : "Internal error - no exception type");
1038 Py_XDECREF(ty_name);
1041 JNIEXPORT jint JNICALL
1042 Java_org_simantics_pythonlink_PythonContext_executePythonStatementImpl(
1043 JNIEnv *env, jobject thisObj, jlong contextID, jstring statement) {
1044 const char *utfchars = (*env)->GetStringUTFChars(env, statement, NULL);
1046 PyEval_RestoreThread(main_ts);
1049 PyObject *module = getModule(contextID);
1053 globals = PyModule_GetDict(module);
1058 // PyObject *result = PyRun_String(utfchars, Py_file_input, globals, globals); - Not available in Py_LIMITED_API
1059 PyObject *code = Py_CompileString(utfchars, "SCL_INPUT", Py_file_input);
1060 PyObject *result = code ? PyEval_EvalCode(code,globals, globals) : NULL;
1061 PyObject *exceptionType = PyErr_Occurred();
1063 if (exceptionType != NULL) {
1064 PyObject *exception, *traceback, *message;
1065 PyErr_Fetch(&exceptionType, &exception, &traceback);
1067 message = getExceptionMessage(exceptionType, exception, traceback);
1068 if (message != NULL) {
1069 if (PyList_Check(message)) {
1070 PyObject *emptyStr = PyUnicode_FromString("");
1071 PyObject *temp = PyUnicode_Join(emptyStr, message);
1077 Py_DECREF(emptyStr);
1080 if (!PyUnicode_Check(message)) {
1081 PyObject *temp = PyObject_Str(message);
1088 PyObject* str = PyUnicode_AsEncodedString(message, "utf-8", "ignore");
1092 throwPythonException(env, PyBytes_AsString(str));
1096 fputs("Python: Encoding message string failed\n", stderr);
1097 throwExceptionType(env, exceptionType);
1101 fputs("Python: No exception message\n", stderr);
1102 throwExceptionType(env, exceptionType);
1109 PyEval_SaveThread();
1110 (*env)->ReleaseStringUTFChars(env, statement, utfchars);
1114 return result != NULL ? 0 : 1;
1119 // Returns a borrowed reference.
1120 static PyObject *getPythonValue(
1121 JNIEnv *env, jlong contextID, jstring variableName) {
1122 PyObject *module = getModule(contextID);
1123 PyObject *pythonName = getPythonString(env, variableName);
1124 PyObject *value = PyDict_GetItem(PyModule_GetDict(module), pythonName);
1126 Py_DECREF(pythonName);
1127 if (value == NULL) {
1128 throwPythonException(env, "Python variable not found");
1133 #define DEF_GETTER(typename, jtype, check, desc, py2j) \
1134 JNIEXPORT jtype JNICALL \
1135 Java_org_simantics_pythonlink_PythonContext_getPython##typename \
1137 JNIEnv *env, jobject thisObj, jlong contextID, \
1138 jstring variableName) { \
1140 PyEval_RestoreThread(main_ts); \
1142 PyObject *value = getPythonValue(env, contextID, variableName); \
1143 if (value == 0) break; \
1144 if (check(value)) { \
1145 result = py2j(env, value); \
1147 throwPythonException(env, "Python variable not " desc); \
1150 PyEval_SaveThread(); \
1154 #define pythonBoolAsJboolean(env, value) ((value) == Py_True)
1155 #define pythonLongAsJlong(env, value) PyLong_AsLongLong(value)
1156 #define pythonFloatAsJdouble(env, value) PyFloat_AsDouble(value)
1158 DEF_GETTER(String, jstring, PyUnicode_Check, "a string",
1159 pythonStringAsJavaString)
1160 DEF_GETTER(StringArray, jobjectArray, PySequence_Check, "a sequence",
1161 pythonSequenceAsStringArray)
1162 DEF_GETTER(Boolean, jboolean, PyBool_Check, "a Boolean", pythonBoolAsJboolean)
1163 DEF_GETTER(BooleanArray, jbooleanArray, PySequence_Check, "a sequence",
1164 pythonSequenceAsBooleanArray)
1165 DEF_GETTER(Long, jlong, PyLong_Check, "an integer", pythonLongAsJlong)
1166 DEF_GETTER(IntegerArray, jintArray, PySequence_Check, "a sequence",
1167 pythonSequenceAsIntegerArray)
1168 DEF_GETTER(LongArray, jlongArray, PySequence_Check, "a sequence",
1169 pythonSequenceAsLongArray)
1170 DEF_GETTER(Double, jdouble, PyFloat_Check, "a float", pythonFloatAsJdouble)
1171 DEF_GETTER(DoubleArray, jdoubleArray, PySequence_Check, "a sequence",
1172 pythonSequenceAsDoubleArray)
1174 JNIEXPORT jobject JNICALL
1175 Java_org_simantics_pythonlink_PythonContext_getPythonNDArrayVariableImpl(
1176 JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) {
1177 jobject result = NULL;
1180 throwPythonException(env, "Importing numpy failed");
1184 PyEval_RestoreThread(main_ts);
1186 PyObject *value = getPythonValue(env, contextID, variableName);
1187 if (value == NULL) break;
1188 if (!PyArray_Check(value)) {
1189 throwPythonException(env, "Python variable not an ndarray");
1190 } else if (PyArray_TYPE((PyArrayObject*)value) != NPY_DOUBLE) {
1191 throwPythonException(
1192 env, "Only ndarrays of type double are supported");
1194 result = pythonArrayAsNDArray(env, (PyArrayObject *)value);
1197 PyEval_SaveThread();
1201 #define python_anything_goes(value) 1
1203 DEF_GETTER(Variant, jobject, python_anything_goes, "frabjous",
1204 pythonObjectAsObject)
1206 JNIEXPORT jint JNICALL
1207 Java_org_simantics_pythonlink_PythonContext_getPythonVariableTypeImpl(
1208 JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) {
1210 PyEval_RestoreThread(main_ts);
1212 PyObject *value = getPythonValue(env, contextID, variableName);
1213 if (PyBool_Check(value))
1215 else if (PyLong_Check(value))
1217 else if (PyFloat_Check(value))
1219 else if (PyUnicode_Check(value))
1221 else if (PyByteArray_Check(value))
1223 else if (PyDict_Check(value))
1225 else if (hasNumpy && PyArray_Check(value))
1227 else if (PySequence_Check(value))
1232 PyEval_SaveThread();
1236 JNIEXPORT jobjectArray JNICALL
1237 Java_org_simantics_pythonlink_PythonContext_getPythonVariableNamesImpl(
1238 JNIEnv *env, jobject thisObj, jlong contextID) {
1239 jobjectArray result = NULL;
1240 PyEval_RestoreThread(main_ts);
1242 PyObject *module = getModule(contextID);
1243 PyObject *dict = PyModule_GetDict(module);
1245 PyObject *keys = PyDict_Keys(dict);
1246 Py_ssize_t size = PyList_Size(keys);
1249 result = (*env)->NewObjectArray(
1250 env, (jsize)size, (*env)->FindClass(env, STRING_CLASS), NULL);
1252 for (i = 0; i < size; i++) {
1253 jstring javaName = pythonStringAsJavaString(
1254 env, PyList_GetItem(keys, i));
1255 (*env)->SetObjectArrayElement(env, result, (jint)i, javaName);
1260 PyEval_SaveThread();
1264 BOOL __stdcall DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
1265 //extern "C" DLL_EXPORT BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
1269 case DLL_PROCESS_ATTACH:
1270 // attach to process
1271 // return FALSE to fail DLL load
1274 case DLL_PROCESS_DETACH:
1275 // detach from process
1278 case DLL_THREAD_ATTACH:
1282 case DLL_THREAD_DETACH:
1283 // detach from thread
1286 return TRUE; // succesful