]> gerrit.simantics Code Review - simantics/python.git/blob - org.simantics.pythonlink.win32.x86_64/src/sclpy.c
Several updates to Pyhthon-link behaviour.
[simantics/python.git] / org.simantics.pythonlink.win32.x86_64 / src / sclpy.c
1 /*******************************************************************************
2  * Copyright (c) 2017-2019 Association for Decentralized Information Management in
3  * Industry THTH ry.
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
8  *
9  * Contributors:
10  *     VTT Technical Research Centre - Initial API and implementation
11  *     Semantum Oy - Improvements
12  *******************************************************************************/
13
14 #include "sclpy.h"
15
16 #include <windows.h>
17
18 jint throwException( JNIEnv *env, char *className, const char *message )
19 {
20     jclass exClass = (*env)->FindClass( env, className);
21     if (exClass == NULL) {
22         return 0;
23     }
24
25     return (*env)->ThrowNew( env, exClass, message );
26 }
27
28 jint throwPythonException( JNIEnv *env, const char *message ) {
29         return throwException( env, PYTHON_EXCEPTION, message );
30 }
31
32 jint throwIllegalArgumentException( JNIEnv *env, const char *message ) {
33         return throwException( env, ILLEGAL_ARGUMENT_EXCEPTION, message );
34 }
35
36 // Returns a borrowed reference.
37 PyObject* getModule(jlong contextID) {
38         return PyState_FindModule((PyModuleDef*) contextID);
39 //      return PyImport_AddModule("__main__");
40 }
41
42 int moduleCount = 0;
43 int hasNumpy = 0;
44 PyThreadState *main_ts = 0;
45
46 static JNIEnv *currentEnv = NULL;
47 jobject sclWriter = NULL;
48
49 static PyObject *
50 writeToSCL(PyObject *self, PyObject *args)
51 {
52     if (currentEnv != NULL && sclWriter != NULL) {
53         wchar_t *what;
54                 Py_ssize_t length;
55         JNIEnv *env = currentEnv;
56
57                 if (!PyArg_ParseTuple(args, "u#", &what, &length))
58                         Py_RETURN_NONE;
59
60                 {
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);
66
67                                 (*env)->SetCharArrayRegion(env, chars, 0, length, what);
68                                 Py_BEGIN_ALLOW_THREADS
69                                 (*env)->CallVoidMethod(env, sclWriter, writeMethod, chars, 0, length);
70                                 Py_END_ALLOW_THREADS
71                         } else {
72                                 //TODO
73                         }
74                 }
75     }
76
77         Py_RETURN_NONE;
78 }
79
80 static PyObject *
81 flushSCL(PyObject *self, PyObject *args)
82 {
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
88                         Py_RETURN_NONE;
89         }
90
91         {
92                         jclass writerClass = (*env)->FindClass(env, WRITER_CLASS);
93                         jmethodID flushMethod = (*env)->GetMethodID(env, writerClass, "flush", "()V");
94
95                 Py_BEGIN_ALLOW_THREADS
96                         (*env)->CallVoidMethod(env, sclWriter, flushMethod);
97                         Py_END_ALLOW_THREADS
98         }
99     }
100
101     Py_RETURN_NONE;
102 }
103
104 static PyMethodDef sclWriterMethods[] = {
105     {"write", writeToSCL, METH_VARARGS, "Write something."},
106         {"flush", flushSCL, METH_VARARGS, "Flush output."},
107     {NULL, NULL, 0, NULL}
108 };
109
110 JNIEXPORT void JNICALL
111 Java_org_simantics_pythonlink_PythonContext_initializePython(
112         JNIEnv *env, jobject thisObj, jobject writer) {
113     Py_InitializeEx(0);
114
115     {
116         static struct PyModuleDef moduledef = {
117             PyModuleDef_HEAD_INIT, "sclwriter", NULL, -1, sclWriterMethods
118         };
119         PyObject *m = PyModule_Create(&moduledef);
120
121         sclWriter = (*env)->NewGlobalRef(env, writer);
122
123         if (m == NULL) {
124             throwException(env, PYTHON_EXCEPTION,
125                            "Failed to create SCL writer module");
126         } else {
127             PySys_SetObject("stdout", m);
128             PySys_SetObject("stderr", m);
129             Py_DECREF(m);
130         }
131     }
132
133         hasNumpy = _import_array();
134         hasNumpy = hasNumpy != -1;
135
136         main_ts = PyEval_SaveThread();
137 }
138
139 JNIEXPORT jlong JNICALL Java_org_simantics_pythonlink_PythonContext_createContextImpl(JNIEnv *env, jobject thisObj) {
140         char name[16];
141
142         if (!main_ts) {
143                 return 0;
144         }
145
146         sprintf(name, "SCL_%d", ++moduleCount);
147
148         PyEval_RestoreThread(main_ts);
149         {
150                 PyObject *module;
151                 PyModuleDef *modDef = malloc(sizeof(PyModuleDef));
152                 memset(modDef, 0, sizeof(PyModuleDef));
153                 modDef->m_name = strdup(name);
154                 modDef->m_doc = NULL;
155                 modDef->m_size = -1;
156
157                 module = PyModule_Create(modDef);
158                 PyState_AddModule(module, modDef);
159                 {
160                         PyObject *builtin = PyImport_AddModule("builtins");
161                         PyObject *dict = PyModule_GetDict(module);
162                         Py_DECREF(module);
163                         PyDict_SetItemString(dict, "__builtin__", builtin);
164                         PyDict_SetItemString(dict, "__builtins__", builtin);
165
166                         PyEval_SaveThread();
167                         return (jlong)modDef;
168                 }
169         }
170 }
171
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);
179         free(modDef);
180         PyEval_SaveThread();
181 }
182
183 PyObject *getPythonBool(jboolean value) {
184         if (value) {
185                 Py_RETURN_TRUE;
186         }
187         else {
188                 Py_RETURN_FALSE;
189         }
190 }
191
192 PyObject *getPythonString(JNIEnv *env, jstring string) {
193         jsize len = (*env)->GetStringLength(env, string);
194         const jchar *chars = (*env)->GetStringChars(env, string, NULL);
195
196         PyObject *value = PyUnicode_DecodeUTF16((char*)chars, 2*len, NULL, NULL);
197
198         (*env)->ReleaseStringChars(env, string, chars);
199         return value;
200 }
201
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);
206         jint i;
207
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));
213                 }
214                 else {
215                         Py_INCREF(Py_None);
216                         PyList_SetItem(result, i, Py_None);
217                 }
218         }
219
220         (*env)->ReleaseIntArrayElements(env, value, values, JNI_ABORT);
221         return result;
222 }
223
224 PyObject *getPythonBooleanList(JNIEnv *env, jbooleanArray value) {
225         jsize nitems = (*env)->GetArrayLength(env, value);
226         jboolean *values = (*env)->GetBooleanArrayElements(env, value, NULL);
227         jint i;
228
229         PyObject *result = PyList_New(nitems);
230         for (i = 0; i < nitems; i++) {
231                 PyList_SetItem(result, i, getPythonBool(values[i]));
232         }
233
234         (*env)->ReleaseBooleanArrayElements(env, value, values, JNI_ABORT);
235         return result;
236 }
237
238 PyObject *getPythonByteArray(JNIEnv *env, jbyteArray value) {
239         jint len = (*env)->GetArrayLength(env, value);
240         jbyte *values = (*env)->GetByteArrayElements(env, value, NULL);
241
242         PyObject *result = PyByteArray_FromStringAndSize(values, len);
243
244         (*env)->ReleaseByteArrayElements(env, value, values, JNI_ABORT);
245         return result;
246 }
247
248 PyObject *getPythonIntegerList(JNIEnv *env, jintArray value) {
249         jsize nitems = (*env)->GetArrayLength(env, value);
250         jint *values = (*env)->GetIntArrayElements(env, value, NULL);
251         jint i;
252
253         PyObject *result = PyList_New(nitems);
254         for (i = 0; i < nitems; i++) {
255                 PyList_SetItem(result, i, PyLong_FromLong(values[i]));
256         }
257
258         (*env)->ReleaseIntArrayElements(env, value, values, JNI_ABORT);
259         return result;
260 }
261
262 PyObject *getPythonLongList(JNIEnv *env, jlongArray value) {
263         jsize nitems = (*env)->GetArrayLength(env, value);
264         jlong *values = (*env)->GetLongArrayElements(env, value, NULL);
265         jint i;
266
267         PyObject *result = PyList_New(nitems);
268         for (i = 0; i < nitems; i++) {
269                 PyList_SetItem(result, i, PyLong_FromLongLong(values[i]));
270         }
271
272         (*env)->ReleaseLongArrayElements(env, value, values, JNI_ABORT);
273         return result;
274 }
275
276 PyObject *getPythonFloatList(JNIEnv *env, jfloatArray value) {
277         jsize nitems = (*env)->GetArrayLength(env, value);
278         float *values = (*env)->GetFloatArrayElements(env, value, NULL);
279         jint i;
280
281         PyObject *result = PyList_New(nitems);
282         for (i = 0; i < nitems; i++) {
283                 PyList_SetItem(result, i, PyFloat_FromDouble((double)values[i]));
284         }
285
286         (*env)->ReleaseFloatArrayElements(env, value, values, JNI_ABORT);
287         return result;
288 }
289
290 PyObject *getPythonDoubleList(JNIEnv *env, jdoubleArray value) {
291         jsize nitems = (*env)->GetArrayLength(env, value);
292         double *values = (*env)->GetDoubleArrayElements(env, value, NULL);
293         jint i;
294
295         PyObject *result = PyList_New(nitems);
296         for (i = 0; i < nitems; i++) {
297                 PyList_SetItem(result, i, PyFloat_FromDouble(values[i]));
298         }
299
300         (*env)->ReleaseDoubleArrayElements(env, value, values, JNI_ABORT);
301         return result;
302 }
303
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");
308
309         jintArray jdims = (*env)->CallObjectMethod(env, value, dimsMethod);
310         jsize ndims = (*env)->GetArrayLength(env, jdims);
311         jint *dims = (*env)->GetIntArrayElements(env, jdims, NULL);
312
313         jdoubleArray jvalues = (*env)->CallObjectMethod(env, value, getValuesMethod);
314         jsize len = (*env)->GetArrayLength(env, jvalues);
315         jdouble *values = (*env)->GetDoubleArrayElements(env, jvalues, NULL);
316
317         npy_intp *pyDims = (npy_intp*)malloc(ndims * sizeof(npy_intp));
318
319         jint i, nelem = ndims > 0 ? 1 : 0;
320         for (i = 0; i < ndims; i++) {
321                 nelem *= dims[i];
322                 pyDims[i] = dims[i];
323         }
324
325         len = min(len, nelem);
326
327         {
328                 PyObject *array = PyArray_EMPTY(ndims, pyDims, NPY_DOUBLE, 0);
329                 double *data = (double *)PyArray_DATA((PyArrayObject*)array);
330
331                 memcpy(data, values, len * sizeof(double));
332
333                 free(pyDims);
334
335                 (*env)->ReleaseDoubleArrayElements(env, jvalues, values, JNI_ABORT);
336                 (*env)->ReleaseIntArrayElements(env, jdims, dims, JNI_ABORT);
337
338                 return array;
339         }
340 }
341
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");
345
346         jboolean bvalue = (*env)->CallBooleanMethod(env, binding, getValueMethod, object);
347         return getPythonBool(bvalue);
348 }
349
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");
353
354         jbyte v = (*env)->CallByteMethod(env, binding, getValueMethod, object);
355         return PyLong_FromLong(v);
356 }
357
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");
361
362         jint v = (*env)->CallIntMethod(env, binding, getValueMethod, object);
363         return PyLong_FromLong(v);
364 }
365
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");
369
370         jlong v = (*env)->CallLongMethod(env, binding, getValueMethod, object);
371         return PyLong_FromLongLong(v);
372 }
373
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");
377
378         jfloat v = (*env)->CallFloatMethod(env, binding, getValueMethod, object);
379         return PyFloat_FromDouble(v);
380 }
381
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");
385
386         jdouble v = (*env)->CallDoubleMethod(env, binding, getValueMethod, object);
387         return PyFloat_FromDouble(v);
388 }
389
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 ";");
393
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);
397
398         PyObject *value = PyUnicode_DecodeUTF16((char*)chars, 2*len, NULL, NULL);
399
400         (*env)->ReleaseStringChars(env, string, chars);
401         return value;
402 }
403
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 ";");
409
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");
413
414         jclass componentClass = (*env)->FindClass(env, COMPONENT_CLASS);
415         jfieldID nameField = (*env)->GetFieldID(env, componentClass, "name", "L" STRING_CLASS ";");
416
417         jobject type = (*env)->CallObjectMethod(env, binding, typeMethod);
418         jint n = (*env)->CallIntMethod(env, type, getComponentCount);
419         jint i;
420
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);
427
428                 PyObject
429                         *key = getPythonString(env, fieldName),
430                         *item = getPythonObject(env, componentObject, componentBinding);
431                 PyDict_SetItem(result, key, item);
432                 Py_DECREF(key);
433                 Py_DECREF(item);
434         }
435
436         return result;
437 }
438
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 ";");
444
445         jobject componentBinding = (*env)->CallObjectMethod(env, binding, componentBindingMethod);
446
447         jint size = (*env)->CallIntMethod(env, binding, sizeMethod, object);
448
449         PyObject *result = PyList_New(size);
450
451         jint i;
452         for (i = 0; i < size; i++) {
453                 jobject item = (*env)->CallObjectMethod(env, binding, getMethod, object, i);
454                 if (item != NULL)
455                         PyList_SetItem(result, i, getPythonObject(env, item, componentBinding));
456                 else {
457                         Py_INCREF(Py_None);
458                         PyList_SetItem(result, i, Py_None);
459                 }
460         }
461
462         return result;
463 }
464
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");
472
473         jobject keyBinding = (*env)->CallObjectMethod(env, binding, getKeyBindingMethod);
474         jobject valueBinding = (*env)->CallObjectMethod(env, binding, getValueBindingMethod);
475
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);
479
480         PyObject *result = PyDict_New();
481         jint i;
482
483         (*env)->CallVoidMethod(env, binding, getAllMethod, object, keys, values);
484
485         for (i = 0; i < size; i++) {
486                 jobject key = (*env)->GetObjectArrayElement(env, keys, i);
487                 jobject item = (*env)->GetObjectArrayElement(env, values, i);
488                 PyObject
489                         *pkey = getPythonObject(env, key, keyBinding),
490                         *pitem = getPythonObject(env, item, valueBinding);
491                 PyDict_SetItem(result, pkey, pitem);
492                 Py_DECREF(pkey);
493                 Py_DECREF(pitem);
494         }
495
496         (*env)->DeleteLocalRef(env, keys);
497         (*env)->DeleteLocalRef(env, values);
498
499         return result;
500 }
501
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");
505
506         jboolean hasValue = (*env)->CallBooleanMethod(env, binding, hasValueMethod, object);
507
508         if (hasValue) {
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 ";");
511
512                 jobject componentBinding = (*env)->CallObjectMethod(env, binding, componentBindingMethod);
513                 jobject value = (*env)->CallObjectMethod(env, binding, getValueMethod, object);
514
515                 return getPythonObject(env, value, componentBinding);
516         }
517         else {
518                 return Py_None;
519         }
520 }
521
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 ";");
528
529         jclass unionType = (*env)->FindClass(env, UNIONTYPE_CLASS);
530         jmethodID getTypeComponent = (*env)->GetMethodID(env, unionType, "getComponent", "(I)L" COMPONENT_CLASS ";");
531
532         jclass componentClass = (*env)->FindClass(env, COMPONENT_CLASS);
533         jfieldID nameField = (*env)->GetFieldID(env, componentClass, "name", "L" STRING_CLASS ";");
534
535         jint tag = (*env)->CallIntMethod(env, binding, getTagMethod, object);
536         jobject value = (*env)->CallObjectMethod(env, binding, getValueMethod, object);
537
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);
541
542         jobject componentBinding = (*env)->CallObjectMethod(env, binding, getComponentBinding, tag);
543
544         PyObject *result = PyTuple_New(2);
545         PyTuple_SetItem(result, 0, getPythonString(env, compName));
546         PyTuple_SetItem(result, 1, getPythonObject(env, value, componentBinding));
547
548         return result;
549 }
550
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 ";");
555
556         jobject content = (*env)->CallObjectMethod(env, binding, getContentMethod, object);
557         jobject contentBinding = (*env)->CallObjectMethod(env, binding, getContentBindingMethod, object);
558
559         return getPythonObject(env, content, contentBinding);
560 }
561
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);
576
577         if (value == NULL)
578                 return Py_None;
579
580         if ((*env)->IsInstanceOf(env, binding, booleanBinding)) {
581                 return getPythonBooleanObject(env, value, binding);
582         }
583         else if ((*env)->IsInstanceOf(env, binding, byteBinding)) {
584                 return getPythonByteObject(env, value, binding);
585         }
586         else if ((*env)->IsInstanceOf(env, binding, integerBinding)) {
587                 return getPythonIntegerObject(env, value, binding);
588         }
589         else if ((*env)->IsInstanceOf(env, binding, longBinding)) {
590                 return getPythonLongObject(env, value, binding);
591         }
592         else if ((*env)->IsInstanceOf(env, binding, floatBinding)) {
593                 return getPythonFloatObject(env, value, binding);
594         }
595         else if ((*env)->IsInstanceOf(env, binding, doubleBinding)) {
596                 return getPythonDoubleObject(env, value, binding);
597         }
598         else if ((*env)->IsInstanceOf(env, binding, stringBinding)) {
599                 return getPythonStringObject(env, value, binding);
600         }
601         else if ((*env)->IsInstanceOf(env, binding, recordBinding)) {
602                 return getPythonRecordObject(env, value, binding);
603         }
604         else if ((*env)->IsInstanceOf(env, binding, arrayBinding)) {
605                 return getPythonArrayObject(env, value, binding);
606         }
607         else if ((*env)->IsInstanceOf(env, binding, mapBinding)) {
608                 return getPythonMapObject(env, value, binding);
609         }
610         else if ((*env)->IsInstanceOf(env, binding, optionalBinding)) {
611                 return getPythonOptionalObject(env, value, binding);
612         }
613         else if ((*env)->IsInstanceOf(env, binding, untionBinding)) {
614                 return getPythonUnionObject(env, value, binding);
615         }
616         else if ((*env)->IsInstanceOf(env, binding, variantBinding)) {
617                 return getPythonVariantObject(env, value, binding);
618         }
619         else {
620                 return Py_None;
621         }
622 }
623
624 // Steals refs to name & value.
625 void setPythonVariable(PyObject *module, PyObject *name, PyObject *value) {
626         if (name && value) {
627                 PyDict_SetItem(PyModule_GetDict(module), name, value);
628         }
629
630         Py_XDECREF(name);
631         Py_XDECREF(value);
632 }
633
634 static npy_intp nContiguous(int d, int nd, npy_intp *strides, npy_intp *dims, npy_intp *ncont) {
635         if (d == nd) {
636                 ncont[d] = 1;
637                 return 1;
638         }
639         else {
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;
642                 return ncont[d];
643         }
644 }
645
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) {
647         if (ncont[d] > 0) {
648                 (*env)->SetDoubleArrayRegion(env, array, (jint)*offset, (jint)ncont[d], data);
649                 *offset += ncont[d];
650         }
651         else {
652                 int i;
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);
655                 }
656         }
657 }
658
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;");
662
663         return (*env)->CallStaticObjectMethod(env, booleanClass, valueOfMethod, (jboolean)(value == Py_True));
664 }
665
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;");
669
670         return (*env)->CallStaticObjectMethod(env, longClass, valueOfMethod, PyLong_AsLongLong(value));
671 }
672
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;");
676
677         return (*env)->CallStaticObjectMethod(env, doubleClass, valueOfMethod, PyFloat_AsDouble(value));
678 }
679
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);
684
685         (*env)->SetByteArrayRegion(env, result, 0, size, bytes);
686
687         return result;
688 }
689
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);
694
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);
697
698         Py_XDECREF(utf16Value);
699
700         return result;
701 }
702
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);
707
708         jint i;
709
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);
714                         Py_DECREF(item);
715                         (*env)->SetObjectArrayElement(env, array, i, value);
716                 }
717                 else {
718                         Py_DECREF(item);
719                         throwPythonException(env, "List item not a string");
720                         return NULL;
721                 }
722         }
723
724         return array;
725 }
726
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);
731
732         jint i;
733
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);
738                         Py_DECREF(item);
739                         (*env)->SetDoubleArrayRegion(env, array, i, 1, &value);
740                 }
741                 else {
742                         Py_DECREF(item);
743                         throwPythonException(env, "List item not a floating point value");
744                         return NULL;
745                 }
746         }
747
748         return array;
749 }
750
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);
768         else
769                 return NULL;
770 }
771
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);
776
777         jint i;
778
779         for (i = 0; i < jlen; i++) {
780                 PyObject *item = PySequence_GetItem(seq, i);
781                 jobject object = pythonObjectAsObject(env, item);
782                 Py_DECREF(item);
783                 (*env)->SetObjectArrayElement(env, array, i, object);
784         }
785
786         return array;
787 }
788
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);
793
794         jint i;
795
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;
800                         Py_DECREF(item);
801                         (*env)->SetBooleanArrayRegion(env, array, i, 1, &value);
802                 }
803                 else {
804                         Py_DECREF(item);
805                         throwPythonException(env, "List item not a boolean");
806                         return NULL;
807                 }
808         }
809
810         return array;
811 }
812
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);
817
818         jint i;
819
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);
824                         Py_DECREF(item);
825                         (*env)->SetIntArrayRegion(env, array, i, 1, &value);
826                 }
827                 else {
828                         Py_DECREF(item);
829                         throwPythonException(env, "List item not an integer");
830                         return NULL;
831                 }
832         }
833
834         return array;
835 }
836
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);
841
842         jint i;
843
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);
848                         Py_DECREF(item);
849                         (*env)->SetLongArrayRegion(env, array, i, 1, &value);
850                 }
851                 else {
852                         Py_DECREF(item);
853                         throwPythonException(env, "List item not an integer");
854                         return NULL;
855                 }
856         }
857
858         return array;
859 }
860
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");
864
865         int ndims = PyArray_NDIM(array);
866         npy_intp *dims = PyArray_DIMS(array);
867
868         npy_intp len = PyArray_Size((PyObject*)array);
869         double *values = (double*)PyArray_DATA(array);
870
871         jboolean isFortran = PyArray_ISFORTRAN(array) != 0;
872
873         int i;
874
875         if (len > JAVA_MAXINT) {
876                 throwPythonException(env, "Array too large");
877                 return NULL;
878         }
879
880         {
881                 jintArray jdims = (*env)->NewIntArray(env, ndims);
882                 jdoubleArray jvalues = (*env)->NewDoubleArray(env, (jsize)len);
883
884                 for (i = 0; i < ndims; i++) {
885                         jint dim = (jint)dims[i];
886                         (*env)->SetIntArrayRegion(env, jdims, i, 1, &dim);
887                 }
888
889                 if (PyArray_IS_C_CONTIGUOUS(array)) {
890                         (*env)->SetDoubleArrayRegion(env, jvalues, 0, (jsize)len, values);
891                 }
892                 else {
893                         npy_intp offset = 0;
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);
898                         free(ncont);
899                 }
900
901                 return (*env)->NewObject(env, ndarrayClass, constructor, jdims, jvalues, isFortran);
902         }
903 }
904
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 ";");
909
910         Py_ssize_t size = PyDict_Size(dict);
911         jobject map = (*env)->NewObject(env, hashmapClass, constructor, (jint)size);
912
913         PyObject *key, *value;
914         Py_ssize_t pos = 0;
915
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);
920         }
921
922         return map;
923 }
924
925 #define DEF_SETTER(typename, jtype, j2py)                               \
926     JNIEXPORT void JNICALL                                              \
927     Java_org_simantics_pythonlink_PythonContext_setPython##typename     \
928     ##VariableImpl(                                                     \
929         JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, \
930         jtype value) {                                                  \
931             PyObject *module;                                           \
932                                                                         \
933             PyEval_RestoreThread(main_ts);                              \
934             module = getModule(contextID);                              \
935             setPythonVariable(module, getPythonString(env, variableName), \
936                               j2py(env, value));                        \
937             PyEval_SaveThread();                                        \
938     }
939
940 #define getPythonBoolean(env, value) getPythonBool(value)
941 #define getPythonLong(env, value) PyLong_FromLongLong(value)
942 #define getPythonDouble(env, value) PyFloat_FromDouble(value)
943
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)
954
955 JNIEXPORT void JNICALL
956 Java_org_simantics_pythonlink_PythonContext_setPythonNDArrayVariableImpl(
957                 JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName,
958                 jobject value) {
959         if (!hasNumpy) {
960                 throwPythonException(env, "Importing numpy failed");
961                 return;
962         }
963
964         PyEval_RestoreThread(main_ts);
965         {
966                 PyObject *module = getModule(contextID);
967                 PyObject *pythonName = getPythonString(env, variableName);
968                 PyObject *val = getPythonNDArray(env, value);
969
970                 setPythonVariable(module, pythonName, val);
971         }
972         PyEval_SaveThread();
973 }
974
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) {
979         PyObject *module;
980
981         PyEval_RestoreThread(main_ts);
982         module = getModule(contextID);
983         setPythonVariable(module, getPythonString(env, variableName),
984                                           getPythonObject(env, value, binding));
985         PyEval_SaveThread();
986 }
987
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);
993                 return NULL;
994         }
995
996         PyErr_NormalizeException(&exceptionType, &exception, &traceback);
997         
998         if (exception && traceback) {
999                 formatExc = PyDict_GetItemString(PyModule_GetDict(tracebackModule), "format_exception");
1000                 args = PyTuple_Pack(3, exceptionType, exception, traceback);
1001         }
1002         else if (exception) {
1003                 formatExc = PyDict_GetItemString(PyModule_GetDict(tracebackModule), "format_exception_only");
1004                 args = PyTuple_Pack(2, exceptionType, exception);
1005         }
1006
1007         if (formatExc != NULL && args != NULL) {
1008                 PyObject *result = PyObject_CallObject(formatExc, args);
1009                 if (!result) {
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);
1013                 }
1014                 Py_XDECREF(args);
1015                 // Py_XDECREF(formatExc) - Borrowed reference
1016                 Py_DECREF(tracebackModule);
1017
1018                 return result;
1019         }
1020         else {
1021                 if (!formatExc) fputs("Python: No format_exception\n", stderr);
1022
1023                 Py_XDECREF(args);
1024                 // Py_XDECREF(formatExc) - Borrowed reference
1025                 Py_DECREF(tracebackModule);
1026
1027                 return NULL;
1028         }
1029 }
1030
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;
1034
1035         throwPythonException(env, str ? PyBytes_AsString(str) : "Internal error - no exception type");
1036
1037         Py_XDECREF(str);
1038         Py_XDECREF(ty_name);
1039 }
1040
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);
1045
1046         PyEval_RestoreThread(main_ts);
1047         PyErr_Clear();
1048         {
1049                 PyObject *module = getModule(contextID);
1050
1051                 PyObject *globals;
1052
1053                 globals = PyModule_GetDict(module);
1054
1055                 currentEnv = env;
1056
1057                 {
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();
1062
1063                         if (exceptionType != NULL) {
1064                                 PyObject *exception, *traceback, *message;
1065                                 PyErr_Fetch(&exceptionType, &exception, &traceback);
1066
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);
1072                                                 if (temp) {
1073                                                         Py_DECREF(message);
1074                                                         message = temp;
1075                                                 }
1076
1077                                                 Py_DECREF(emptyStr);
1078                                         }
1079
1080                                         if (!PyUnicode_Check(message)) {
1081                                                 PyObject *temp = PyObject_Str(message);
1082                                                 if (temp) {
1083                                                         Py_DECREF(message);
1084                                                         message = temp;
1085                                                 }
1086                                         }
1087
1088                                         PyObject* str = PyUnicode_AsEncodedString(message, "utf-8", "ignore");
1089                                         Py_DECREF(message);
1090
1091                                         if (str != NULL) {
1092                                                 throwPythonException(env, PyBytes_AsString(str));
1093                                                 Py_DECREF(str);
1094                                         }
1095                                         else {
1096                                                 fputs("Python: Encoding message string failed\n", stderr);
1097                                                 throwExceptionType(env, exceptionType);
1098                                         }
1099                                 }
1100                                 else {
1101                                         fputs("Python: No exception message\n", stderr);
1102                                         throwExceptionType(env, exceptionType);
1103                                 }
1104                         }
1105
1106                         Py_XDECREF(result);
1107                         Py_XDECREF(code);
1108
1109                         PyEval_SaveThread();
1110                         (*env)->ReleaseStringUTFChars(env, statement, utfchars);
1111
1112                         currentEnv = NULL;
1113
1114                         return result != NULL ? 0 : 1;
1115                 }
1116         }
1117 }
1118
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);
1125
1126     Py_DECREF(pythonName);
1127     if (value == NULL) {
1128         throwPythonException(env, "Python variable not found");
1129     }
1130     return value;
1131 }
1132
1133 #define DEF_GETTER(typename, jtype, check, desc, py2j)                  \
1134     JNIEXPORT jtype JNICALL                                             \
1135     Java_org_simantics_pythonlink_PythonContext_getPython##typename     \
1136     ##VariableImpl(                                                     \
1137             JNIEnv *env, jobject thisObj, jlong contextID,              \
1138             jstring variableName) {                                     \
1139         jtype result = 0;                                               \
1140         PyEval_RestoreThread(main_ts);                                  \
1141         do {                                                            \
1142             PyObject *value = getPythonValue(env, contextID, variableName); \
1143             if (value == 0) break;                                      \
1144             if (check(value)) {                                         \
1145                 result = py2j(env, value);                              \
1146             } else {                                                    \
1147                 throwPythonException(env, "Python variable not " desc); \
1148             }                                                           \
1149         } while (0);                                                    \
1150         PyEval_SaveThread();                                            \
1151         return result;                                                  \
1152     }
1153
1154 #define pythonBoolAsJboolean(env, value) ((value) == Py_True)
1155 #define pythonLongAsJlong(env, value) PyLong_AsLongLong(value)
1156 #define pythonFloatAsJdouble(env, value) PyFloat_AsDouble(value)
1157
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)
1173
1174 JNIEXPORT jobject JNICALL
1175 Java_org_simantics_pythonlink_PythonContext_getPythonNDArrayVariableImpl(
1176                 JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) {
1177         jobject result = NULL;
1178
1179         if (!hasNumpy) {
1180                 throwPythonException(env, "Importing numpy failed");
1181                 return NULL;
1182         }
1183
1184         PyEval_RestoreThread(main_ts);
1185         do {
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");
1193                 } else {
1194                         result = pythonArrayAsNDArray(env, (PyArrayObject *)value);
1195                 }
1196         } while (0);
1197         PyEval_SaveThread();
1198         return result;
1199 }
1200
1201 #define python_anything_goes(value) 1
1202
1203 DEF_GETTER(Variant, jobject, python_anything_goes, "frabjous",
1204                    pythonObjectAsObject)
1205
1206 JNIEXPORT jint JNICALL
1207 Java_org_simantics_pythonlink_PythonContext_getPythonVariableTypeImpl(
1208                 JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) {
1209         jint result = 0;
1210         PyEval_RestoreThread(main_ts);
1211         {
1212                 PyObject *value = getPythonValue(env, contextID, variableName);
1213                 if (PyBool_Check(value))
1214                         result = 1;
1215                 else if (PyLong_Check(value))
1216                         result = 2;
1217                 else if (PyFloat_Check(value))
1218                         result = 3;
1219                 else if (PyUnicode_Check(value))
1220                         result = 4;
1221                 else if (PyByteArray_Check(value))
1222                         result = 5;
1223                 else if (PyDict_Check(value))
1224                         result = 6;
1225                 else if (hasNumpy && PyArray_Check(value))
1226                         result = 7;
1227                 else if (PySequence_Check(value))
1228                         result = 8;
1229                 else
1230                         result = -1;
1231         }
1232         PyEval_SaveThread();
1233         return result;
1234 }
1235
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);
1241         {
1242                 PyObject *module = getModule(contextID);
1243                 PyObject *dict = PyModule_GetDict(module);
1244
1245                 PyObject *keys = PyDict_Keys(dict);
1246                 Py_ssize_t size = PyList_Size(keys);
1247                 Py_ssize_t i;
1248
1249                 result = (*env)->NewObjectArray(
1250                                 env, (jsize)size, (*env)->FindClass(env, STRING_CLASS), NULL);
1251
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);
1256                 }
1257
1258                 Py_XDECREF(keys);
1259         }
1260         PyEval_SaveThread();
1261         return result;
1262 }
1263
1264 BOOL __stdcall DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
1265 //extern "C" DLL_EXPORT BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
1266 {
1267     switch (fdwReason)
1268     {
1269         case DLL_PROCESS_ATTACH:
1270             // attach to process
1271             // return FALSE to fail DLL load
1272             break;
1273
1274         case DLL_PROCESS_DETACH:
1275             // detach from process
1276             break;
1277
1278         case DLL_THREAD_ATTACH:
1279             // attach to thread
1280             break;
1281
1282         case DLL_THREAD_DETACH:
1283             // detach from thread
1284             break;
1285     }
1286     return TRUE; // succesful
1287 }