Mercurial > pub > dyncall > bindings
comparison python/pydc/pydc.c @ 54:918dab7a6606
- added callback support (comes with some bigger refactoring)
- allow CPython's Py{CObject,Capsule} to be used as 'p'ointers
author | Tassilo Philipp |
---|---|
date | Tue, 02 Feb 2021 20:42:02 +0100 |
parents | 03b6934cdd63 |
children | 2e8a56976bf8 |
comparison
equal
deleted
inserted
replaced
53:6387d39ecce2 | 54:918dab7a6606 |
---|---|
11 *****************************************************************************/ | 11 *****************************************************************************/ |
12 | 12 |
13 #include <Python.h> | 13 #include <Python.h> |
14 #include "dynload.h" | 14 #include "dynload.h" |
15 #include <limits.h> | 15 #include <limits.h> |
16 #include <assert.h> | |
16 | 17 |
17 | 18 |
18 | 19 |
19 #if ( (PY_VERSION_HEX < 0x02070000) \ | 20 #if ( (PY_VERSION_HEX < 0x02070000) \ |
20 || ((PY_VERSION_HEX >= 0x03000000) \ | 21 || ((PY_VERSION_HEX >= 0x03000000) \ |
21 && (PY_VERSION_HEX < 0x03010000)) ) | 22 && (PY_VERSION_HEX < 0x03010000)) ) |
22 # define DcPyCObject_FromVoidPtr(ptr, dtor) PyCObject_FromVoidPtr((ptr), (dtor)) // !new ref! | 23 # define DcPyCObject_FromVoidPtr(ptr, dtor) PyCObject_FromVoidPtr((ptr), (dtor)) // !new ref! |
23 # define DcPyCObject_AsVoidPtr(ppobj) PyCObject_AsVoidPtr((ppobj)) | 24 # define DcPyCObject_AsVoidPtr(ppobj) PyCObject_AsVoidPtr((ppobj)) |
24 # define DcPyCObject_SetVoidPtr(ppobj, ptr) PyCObject_SetVoidPtr((ppobj), (ptr)) | 25 # define DcPyCObject_SetVoidPtr(ppobj, ptr) PyCObject_SetVoidPtr((ppobj), (ptr)) |
26 # define DcPyCObject_Check(ppobj) PyCObject_Check((ppobj)) | |
25 #else | 27 #else |
26 # define USE_CAPSULE_API | 28 # define USE_CAPSULE_API |
27 # define DcPyCObject_FromVoidPtr(ptr, dtor) PyCapsule_New((ptr), NULL, (dtor)) // !new ref! | 29 # define DcPyCObject_FromVoidPtr(ptr, dtor) PyCapsule_New((ptr), NULL, (dtor)) // !new ref! |
28 # define DcPyCObject_AsVoidPtr(ppobj) PyCapsule_GetPointer((ppobj), NULL) | 30 # define DcPyCObject_AsVoidPtr(ppobj) PyCapsule_GetPointer((ppobj), NULL) |
29 # define DcPyCObject_SetVoidPtr(ppobj, ptr) //@@@ unsure what to do, cannot/shouldn't call this with a null pointer as this wants to call the dtor, so not doing anything: PyCapsule_SetPointer((ppobj), (ptr)) // this might need to call the dtor to behave like PyCObject_SetVoidPtr? | 31 # define DcPyCObject_SetVoidPtr(ppobj, ptr) //@@@ unsure what to do, cannot/shouldn't call this with a null pointer as this wants to call the dtor, so not doing anything: PyCapsule_SetPointer((ppobj), (ptr)) // this might need to call the dtor to behave like PyCObject_SetVoidPtr? |
32 # define DcPyCObject_Check(ppobj) PyCapsule_CheckExact((ppobj)) | |
30 #endif | 33 #endif |
31 | 34 |
32 #if(PY_VERSION_HEX >= 0x03030000) | 35 #if(PY_VERSION_HEX >= 0x03030000) |
33 # define PYUNICODE_CACHES_UTF8 | 36 # define PYUNICODE_CACHES_UTF8 |
34 #endif | 37 #endif |
52 #endif | 55 #endif |
53 | 56 |
54 /* PyCObject destructor callback for libhandle */ | 57 /* PyCObject destructor callback for libhandle */ |
55 | 58 |
56 #if defined(USE_CAPSULE_API) | 59 #if defined(USE_CAPSULE_API) |
57 void free_library(PyObject* capsule) | 60 static void free_library(PyObject* capsule) |
58 { | 61 { |
59 void* libhandle = PyCapsule_GetPointer(capsule, NULL); | 62 void* libhandle = PyCapsule_GetPointer(capsule, NULL); |
60 #else | 63 #else |
61 void free_library(void* libhandle) | 64 static void free_library(void* libhandle) |
62 { | 65 { |
63 #endif | 66 #endif |
64 if (libhandle != 0) | 67 if (libhandle != 0) |
65 dlFreeLibrary(libhandle); | 68 dlFreeLibrary(libhandle); |
66 } | 69 } |
164 } | 167 } |
165 | 168 |
166 | 169 |
167 #include "dyncall.h" | 170 #include "dyncall.h" |
168 #include "dyncall_signature.h" | 171 #include "dyncall_signature.h" |
172 | |
173 | |
174 /* helpers */ | |
175 | |
176 static inline PyObject* py2dcchar(DCchar* c, PyObject* po, int u, int pos) | |
177 { | |
178 if ( PyUnicode_Check(po) ) | |
179 { | |
180 #if (PY_VERSION_HEX < 0x03030000) | |
181 Py_UNICODE cu; | |
182 if (PyUnicode_GET_SIZE(po) != 1) | |
183 #else | |
184 Py_UCS4 cu; | |
185 if (PyUnicode_GET_LENGTH(po) != 1) | |
186 #endif | |
187 return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a str with length of 1 (a char string)", pos ); | |
188 | |
189 #if (PY_VERSION_HEX < 0x03030000) | |
190 cu = PyUnicode_AS_UNICODE(po)[0]; | |
191 #else | |
192 cu = PyUnicode_ReadChar(po, 0); | |
193 #endif | |
194 // check against UCHAR_MAX in every case b/c Py_UCS4 is unsigned | |
195 if ( (cu > UCHAR_MAX)) | |
196 return PyErr_Format( PyExc_RuntimeError, "arg %d out of range - expecting a char code", pos ); | |
197 *c = (DCchar) cu; | |
198 return po; | |
199 } | |
200 | |
201 if ( DcPyString_Check(po) ) | |
202 { | |
203 size_t l; | |
204 char* s; | |
205 l = DcPyString_GET_SIZE(po); | |
206 if (l != 1) | |
207 return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a str with length of 1 (a char string)", pos ); | |
208 s = DcPyString_AsString(po); | |
209 *c = (DCchar) s[0]; | |
210 return po; | |
211 } | |
212 | |
213 if ( DcPyInt_Check(po) ) | |
214 { | |
215 long l = DcPyInt_AsLong(po); | |
216 if (u && (l < 0 || l > UCHAR_MAX)) | |
217 return PyErr_Format( PyExc_RuntimeError, "arg %d out of range - expecting 0 <= arg <= %d, got %ld", pos, UCHAR_MAX, l ); | |
218 if (!u && (l < CHAR_MIN || l > CHAR_MAX)) | |
219 return PyErr_Format( PyExc_RuntimeError, "arg %d out of range - expecting %d <= arg <= %d, got %ld", pos, CHAR_MIN, CHAR_MAX, l ); | |
220 *c = (DCchar) l; | |
221 return po; | |
222 } | |
223 | |
224 return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a char", pos ); | |
225 } | |
226 | |
227 static inline PyObject* py2dcshort(DCshort* s, PyObject* po, int u, int pos) | |
228 { | |
229 long l; | |
230 if ( !DcPyInt_Check(po) ) | |
231 return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting an int", pos ); | |
232 l = DcPyInt_AS_LONG(po); | |
233 if (u && (l < 0 || l > USHRT_MAX)) | |
234 return PyErr_Format( PyExc_RuntimeError, "arg %d out of range - expecting 0 <= arg <= %d, got %ld", pos, USHRT_MAX, l ); | |
235 if (!u && (l < SHRT_MIN || l > SHRT_MAX)) | |
236 return PyErr_Format( PyExc_RuntimeError, "arg %d out of range - expecting %d <= arg <= %d, got %ld", pos, SHRT_MIN, SHRT_MAX, l ); | |
237 | |
238 *s = (DCshort)l; | |
239 return po; | |
240 } | |
241 | |
242 static inline PyObject* py2dclonglong(DClonglong* ll, PyObject* po, int pos) | |
243 { | |
244 #if PY_MAJOR_VERSION < 3 | |
245 if ( PyInt_Check(po) ) { | |
246 *ll = (DClonglong) PyInt_AS_LONG(po); | |
247 return po; | |
248 } | |
249 #endif | |
250 if ( !PyLong_Check(po) ) | |
251 return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting " EXPECT_LONG_TYPE_STR, pos ); | |
252 | |
253 *ll = (DClonglong) PyLong_AsLongLong(po); | |
254 return po; | |
255 } | |
256 | |
257 static inline PyObject* py2dcpointer(DCpointer* p, PyObject* po, int pos) | |
258 { | |
259 if ( PyByteArray_Check(po) ) { | |
260 *p = (DCpointer) PyByteArray_AsString(po); // adds an extra '\0', but that's ok | |
261 return po; | |
262 } | |
263 #if PY_MAJOR_VERSION < 3 | |
264 if ( PyInt_Check(po) ) { | |
265 *p = (DCpointer) PyInt_AS_LONG(po); | |
266 return po; | |
267 } | |
268 #endif | |
269 if ( PyLong_Check(po) ) { | |
270 *p = (DCpointer) PyLong_AsVoidPtr(po); | |
271 return po; | |
272 } | |
273 if ( po == Py_None ) { | |
274 *p = NULL; | |
275 return po; | |
276 } | |
277 if ( DcPyCObject_Check(po) ) { | |
278 *p = DcPyCObject_AsVoidPtr(po); | |
279 return po; | |
280 } | |
281 | |
282 return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a promoting pointer-type (int), mutable array (bytearray) or callback func handle (int, created with new_callback())", pos ); | |
283 } | |
284 | |
169 | 285 |
170 DCCallVM* gpCall = NULL; | 286 DCCallVM* gpCall = NULL; |
171 | 287 |
172 // helper to temporarily copy string arguments | 288 // helper to temporarily copy string arguments |
173 #define NUM_AUX_STRS 64 | 289 #define NUM_AUX_STRS 64 |
254 | 370 |
255 case DC_SIGCHAR_CHAR: | 371 case DC_SIGCHAR_CHAR: |
256 case DC_SIGCHAR_UCHAR: | 372 case DC_SIGCHAR_UCHAR: |
257 { | 373 { |
258 DCchar c; | 374 DCchar c; |
259 if ( PyUnicode_Check(po) ) | 375 if(!py2dcchar(&c, po, ch == DC_SIGCHAR_UCHAR, pos)) |
260 { | 376 return NULL; |
261 #if (PY_VERSION_HEX < 0x03030000) | |
262 Py_UNICODE cu; | |
263 if (PyUnicode_GET_SIZE(po) != 1) | |
264 #else | |
265 Py_UCS4 cu; | |
266 if (PyUnicode_GET_LENGTH(po) != 1) | |
267 #endif | |
268 return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a str with length of 1 (a char string)", pos ); | |
269 | |
270 #if (PY_VERSION_HEX < 0x03030000) | |
271 cu = PyUnicode_AS_UNICODE(po)[0]; | |
272 #else | |
273 cu = PyUnicode_ReadChar(po, 0); | |
274 #endif | |
275 // check against UCHAR_MAX in every case b/c Py_UCS4 is unsigned | |
276 if ( (cu > UCHAR_MAX)) | |
277 return PyErr_Format( PyExc_RuntimeError, "arg %d out of range - expecting a char code", pos ); | |
278 c = (DCchar) cu; | |
279 } | |
280 else if ( DcPyString_Check(po) ) | |
281 { | |
282 size_t l; | |
283 char* s; | |
284 l = DcPyString_GET_SIZE(po); | |
285 if (l != 1) | |
286 return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a str with length of 1 (a char string)", pos ); | |
287 s = DcPyString_AsString(po); | |
288 c = (DCchar) s[0]; | |
289 } | |
290 else if ( DcPyInt_Check(po) ) | |
291 { | |
292 long l = DcPyInt_AsLong(po); | |
293 if (ch == DC_SIGCHAR_CHAR && (l < CHAR_MIN || l > CHAR_MAX)) | |
294 return PyErr_Format( PyExc_RuntimeError, "arg %d out of range - expecting %d <= arg <= %d, got %ld", pos, CHAR_MIN, CHAR_MAX, l ); | |
295 if (ch == DC_SIGCHAR_UCHAR && (l < 0 || l > UCHAR_MAX)) | |
296 return PyErr_Format( PyExc_RuntimeError, "arg %d out of range - expecting 0 <= arg <= %d, got %ld", pos, UCHAR_MAX, l ); | |
297 c = (DCchar) l; | |
298 } | |
299 else | |
300 return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a char", pos ); | |
301 dcArgChar(gpCall, c); | 377 dcArgChar(gpCall, c); |
302 } | 378 } |
303 break; | 379 break; |
304 | 380 |
305 case DC_SIGCHAR_SHORT: | 381 case DC_SIGCHAR_SHORT: |
306 { | |
307 long l; | |
308 if ( !DcPyInt_Check(po) ) | |
309 return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting an int", pos ); | |
310 l = DcPyInt_AS_LONG(po); | |
311 if (l < SHRT_MIN || l > SHRT_MAX) | |
312 return PyErr_Format( PyExc_RuntimeError, "arg %d out of range - expecting %d <= arg <= %d, got %ld", pos, SHRT_MIN, SHRT_MAX, l ); | |
313 dcArgShort(gpCall, (DCshort)l); | |
314 } | |
315 break; | |
316 | |
317 case DC_SIGCHAR_USHORT: | 382 case DC_SIGCHAR_USHORT: |
318 { | 383 { |
319 long l; | 384 DCshort s; |
320 if ( !DcPyInt_Check(po) ) | 385 if(!py2dcshort(&s, po, ch == DC_SIGCHAR_USHORT, pos)) |
321 return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting an int", pos ); | 386 return NULL; |
322 l = DcPyInt_AS_LONG(po); | 387 dcArgShort(gpCall, s); |
323 if (l < 0 || l > USHRT_MAX) | |
324 return PyErr_Format( PyExc_RuntimeError, "arg %d out of range - expecting 0 <= arg <= %d, got %ld", pos, USHRT_MAX, l ); | |
325 dcArgShort(gpCall, (DCshort)l); | |
326 } | 388 } |
327 break; | 389 break; |
328 | 390 |
329 case DC_SIGCHAR_INT: | 391 case DC_SIGCHAR_INT: |
330 case DC_SIGCHAR_UINT: | 392 case DC_SIGCHAR_UINT: |
340 dcArgLong(gpCall, (DClong) PyLong_AsLong(po)); | 402 dcArgLong(gpCall, (DClong) PyLong_AsLong(po)); |
341 break; | 403 break; |
342 | 404 |
343 case DC_SIGCHAR_LONGLONG: | 405 case DC_SIGCHAR_LONGLONG: |
344 case DC_SIGCHAR_ULONGLONG: | 406 case DC_SIGCHAR_ULONGLONG: |
345 #if PY_MAJOR_VERSION < 3 | 407 { |
346 if ( PyInt_Check(po) ) | 408 DClonglong ll; |
347 dcArgLongLong(gpCall, (DClonglong) PyInt_AS_LONG(po)); | 409 if(!py2dclonglong(&ll, po, pos)) |
348 else | 410 return NULL; |
349 #endif | 411 dcArgLongLong(gpCall, ll); |
350 if ( !PyLong_Check(po) ) | 412 } |
351 return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting " EXPECT_LONG_TYPE_STR, pos ); | |
352 dcArgLongLong(gpCall, (DClonglong)PyLong_AsLongLong(po)); | |
353 break; | 413 break; |
354 | 414 |
355 case DC_SIGCHAR_FLOAT: | 415 case DC_SIGCHAR_FLOAT: |
356 if (!PyFloat_Check(po)) | 416 if (!PyFloat_Check(po)) |
357 return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a float", pos ); | 417 return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a float", pos ); |
362 if (!PyFloat_Check(po)) | 422 if (!PyFloat_Check(po)) |
363 return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a float", pos ); | 423 return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a float", pos ); |
364 dcArgDouble(gpCall, PyFloat_AsDouble(po)); | 424 dcArgDouble(gpCall, PyFloat_AsDouble(po)); |
365 break; | 425 break; |
366 | 426 |
367 case DC_SIGCHAR_POINTER: // this will only accept integers or mutable array types (meaning only bytearray) | 427 case DC_SIGCHAR_POINTER: // this will only accept integers, mutable array types (meaning only bytearray) or tuples describing a callback |
368 { | 428 { |
369 DCpointer p; | 429 DCpointer p; |
370 if ( PyByteArray_Check(po) ) | 430 if(!py2dcpointer(&p, po, pos)) |
371 p = (DCpointer) PyByteArray_AsString(po); // adds an extra '\0', but that's ok | 431 return NULL; |
372 #if PY_MAJOR_VERSION < 3 | 432 dcArgPointer(gpCall, p); |
373 else if ( PyInt_Check(po) ) | 433 } |
374 p = (DCpointer) PyInt_AS_LONG(po); | 434 break; |
375 #endif | |
376 else if ( PyLong_Check(po) ) | |
377 p = (DCpointer) PyLong_AsVoidPtr(po); | |
378 else if ( po == Py_None ) | |
379 p = NULL; | |
380 else | |
381 return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a promoting pointer-type (int, bytearray)", pos ); | |
382 dcArgPointer(gpCall, p); | |
383 } | |
384 break; | |
385 | 435 |
386 case DC_SIGCHAR_STRING: // strings are considered to be immutable objects | 436 case DC_SIGCHAR_STRING: // strings are considered to be immutable objects |
387 { | 437 { |
388 PyObject* bo = NULL; | 438 PyObject* bo = NULL; |
389 const char* p; | 439 const char* p; |
390 size_t s; | 440 size_t s; |
391 if ( PyUnicode_Check(po) ) | 441 if ( PyUnicode_Check(po) ) |
392 { | 442 { |
393 if(n_str_aux >= NUM_AUX_STRS) | |
394 return PyErr_Format( PyExc_RuntimeError, "too many arguments (implementation limit of %d new UTF-8 string references reached) - abort", n_str_aux ); | |
395 | |
396 #if defined(PYUNICODE_CACHES_UTF8) | 443 #if defined(PYUNICODE_CACHES_UTF8) |
397 p = PyUnicode_AsUTF8(po); | 444 p = PyUnicode_AsUTF8(po); |
398 #else | 445 #else |
399 // w/o PyUnicode_AsUTF8(), which caches the UTF-8 representation, itself, create new ref we'll dec below | 446 // w/o PyUnicode_AsUTF8(), which caches the UTF-8 representation, itself, create new ref we'll dec below |
400 if((bo = PyUnicode_AsEncodedString(po, "utf-8", "strict"))) // !new ref! | 447 if((bo = PyUnicode_AsEncodedString(po, "utf-8", "strict"))) // !new ref! |
405 else if ( PyByteArray_Check(po) ) | 452 else if ( PyByteArray_Check(po) ) |
406 p = (DCpointer) PyByteArray_AsString(po); // adds an extra '\0', but that's ok //@@@ not sure if allowed to modify | 453 p = (DCpointer) PyByteArray_AsString(po); // adds an extra '\0', but that's ok //@@@ not sure if allowed to modify |
407 else | 454 else |
408 return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a str", pos ); | 455 return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a str", pos ); |
409 | 456 |
410 // p points in any case to a buffer that shouldn't be modified, so pass a copy to dyncall (cleaned up after call) | 457 if(n_str_aux >= NUM_AUX_STRS) |
458 return PyErr_Format( PyExc_RuntimeError, "too many arguments (implementation limit of %d new UTF-8 string references reached) - abort", n_str_aux ); | |
459 | |
460 // p points in every case to a buffer that shouldn't be modified, so pass a copy to dyncall (cleaned up after call) | |
411 s = strlen(p)+1; | 461 s = strlen(p)+1; |
412 str_aux[n_str_aux] = malloc(s); | 462 str_aux[n_str_aux] = malloc(s); |
413 strncpy(str_aux[n_str_aux], p, s); | 463 strncpy(str_aux[n_str_aux], p, s); |
414 Py_XDECREF(bo); | 464 Py_XDECREF(bo); |
415 dcArgPointer(gpCall, (DCpointer)str_aux[n_str_aux++]); | 465 dcArgPointer(gpCall, (DCpointer)str_aux[n_str_aux++]); |
447 case DC_SIGCHAR_FLOAT: return Py_BuildValue("f", dcCallFloat (gpCall, pfunc)); // !new ref! | 497 case DC_SIGCHAR_FLOAT: return Py_BuildValue("f", dcCallFloat (gpCall, pfunc)); // !new ref! |
448 case DC_SIGCHAR_DOUBLE: return Py_BuildValue("d", dcCallDouble (gpCall, pfunc)); // !new ref! | 498 case DC_SIGCHAR_DOUBLE: return Py_BuildValue("d", dcCallDouble (gpCall, pfunc)); // !new ref! |
449 case DC_SIGCHAR_STRING: return Py_BuildValue("s", dcCallPointer (gpCall, pfunc)); // !new ref! | 499 case DC_SIGCHAR_STRING: return Py_BuildValue("s", dcCallPointer (gpCall, pfunc)); // !new ref! |
450 case DC_SIGCHAR_POINTER: return Py_BuildValue("n", dcCallPointer (gpCall, pfunc)); // !new ref! | 500 case DC_SIGCHAR_POINTER: return Py_BuildValue("n", dcCallPointer (gpCall, pfunc)); // !new ref! |
451 default: return PyErr_Format(PyExc_RuntimeError, "invalid return type signature"); | 501 default: return PyErr_Format(PyExc_RuntimeError, "invalid return type signature"); |
502 // @@@ this could be handled via array lookups of a 256b array instead of switch/case, then share it with callback code if it makes sense | |
452 } | 503 } |
453 | 504 |
454 #if !defined(PYUNICODE_CACHES_UTF8) | 505 #if !defined(PYUNICODE_CACHES_UTF8) |
455 Py_XDECREF(sig_obj); | 506 Py_XDECREF(sig_obj); |
456 #endif | 507 #endif |
467 free(str_aux[i]); | 518 free(str_aux[i]); |
468 return o; | 519 return o; |
469 } | 520 } |
470 | 521 |
471 | 522 |
523 #include "dyncall_callback.h" | |
524 #include "dyncall_args.h" | |
525 | |
526 | |
527 /* PyCObject destructor callback for callback obj */ | |
528 | |
529 #if defined(USE_CAPSULE_API) | |
530 static void free_callback(PyObject* capsule) | |
531 { | |
532 void* cb = PyCapsule_GetPointer(capsule, NULL); | |
533 #else | |
534 static void free_callback(void* cb) | |
535 { | |
536 #endif | |
537 if (cb != 0) | |
538 dcbFreeCallback(cb); | |
539 } | |
540 | |
541 | |
542 struct callback_userdata { | |
543 PyObject* f; | |
544 char sig[]; | |
545 }; | |
546 | |
547 /* generic callback handler dispatching to python */ | |
548 static char handle_py_callbacks(DCCallback* pcb, DCArgs* args, DCValue* result, void* userdata) | |
549 { | |
550 | |
551 struct callback_userdata* x = (struct callback_userdata*)userdata; | |
552 const char* sig_ptr = x->sig; | |
553 | |
554 Py_ssize_t n_args = ((PyCodeObject*)PyFunction_GetCode(x->f))->co_argcount; | |
555 Py_ssize_t pos = 0; | |
556 PyObject* py_args = PyTuple_New(n_args); // !new ref! | |
557 PyObject* po; | |
558 char ch; | |
559 | |
560 if(py_args) | |
561 { | |
562 // @@@ we could do the below actually by using dyncall itself, piecing together python's sig string and then dcCallPointer(vm, Py_BuildValue, ...) | |
563 for (ch = *sig_ptr; ch != '\0' && ch != DC_SIGCHAR_ENDARG && pos < n_args; ch = *++sig_ptr) | |
564 { | |
565 switch(ch) | |
566 { | |
567 case DC_SIGCHAR_CC_PREFIX: assert(*(sig_ptr+1) == DC_SIGCHAR_CC_DEFAULT); /* not handling callbacks to anything but default callconf */ break; | |
568 case DC_SIGCHAR_BOOL: PyTuple_SET_ITEM(py_args, pos++, PyBool_FromLong(dcbArgBool (args))); break; // !new ref! (but "stolen" by SET_ITEM) | |
569 case DC_SIGCHAR_CHAR: PyTuple_SET_ITEM(py_args, pos++, Py_BuildValue("b", dcbArgChar (args))); break; // !new ref! (but "stolen" by SET_ITEM) | |
570 case DC_SIGCHAR_UCHAR: PyTuple_SET_ITEM(py_args, pos++, Py_BuildValue("B", dcbArgUChar (args))); break; // !new ref! (but "stolen" by SET_ITEM) | |
571 case DC_SIGCHAR_SHORT: PyTuple_SET_ITEM(py_args, pos++, Py_BuildValue("h", dcbArgShort (args))); break; // !new ref! (but "stolen" by SET_ITEM) | |
572 case DC_SIGCHAR_USHORT: PyTuple_SET_ITEM(py_args, pos++, Py_BuildValue("H", dcbArgUShort (args))); break; // !new ref! (but "stolen" by SET_ITEM) | |
573 case DC_SIGCHAR_INT: PyTuple_SET_ITEM(py_args, pos++, Py_BuildValue("i", dcbArgInt (args))); break; // !new ref! (but "stolen" by SET_ITEM) | |
574 case DC_SIGCHAR_UINT: PyTuple_SET_ITEM(py_args, pos++, Py_BuildValue("I", dcbArgUInt (args))); break; // !new ref! (but "stolen" by SET_ITEM) | |
575 case DC_SIGCHAR_LONG: PyTuple_SET_ITEM(py_args, pos++, Py_BuildValue("l", dcbArgLong (args))); break; // !new ref! (but "stolen" by SET_ITEM) | |
576 case DC_SIGCHAR_ULONG: PyTuple_SET_ITEM(py_args, pos++, Py_BuildValue("k", dcbArgULong (args))); break; // !new ref! (but "stolen" by SET_ITEM) | |
577 case DC_SIGCHAR_LONGLONG: PyTuple_SET_ITEM(py_args, pos++, Py_BuildValue("L", dcbArgLongLong (args))); break; // !new ref! (but "stolen" by SET_ITEM) | |
578 case DC_SIGCHAR_ULONGLONG: PyTuple_SET_ITEM(py_args, pos++, Py_BuildValue("K", dcbArgULongLong(args))); break; // !new ref! (but "stolen" by SET_ITEM) | |
579 case DC_SIGCHAR_FLOAT: PyTuple_SET_ITEM(py_args, pos++, Py_BuildValue("f", dcbArgFloat (args))); break; // !new ref! (but "stolen" by SET_ITEM) | |
580 case DC_SIGCHAR_DOUBLE: PyTuple_SET_ITEM(py_args, pos++, Py_BuildValue("d", dcbArgDouble (args))); break; // !new ref! (but "stolen" by SET_ITEM) | |
581 case DC_SIGCHAR_STRING: PyTuple_SET_ITEM(py_args, pos++, Py_BuildValue("s", dcbArgPointer (args))); break; // !new ref! (but "stolen" by SET_ITEM) | |
582 case DC_SIGCHAR_POINTER: PyTuple_SET_ITEM(py_args, pos++, Py_BuildValue("n", dcbArgPointer (args))); break; // !new ref! (but "stolen" by SET_ITEM) | |
583 default: /* will lead to "signature not matching" error */ pos = n_args; break; | |
584 // @@@ this could be handled via array lookups of a 256b array instead of switch/case, then share it with call code (for returns) if it makes sense | |
585 } | |
586 } | |
587 | |
588 | |
589 // we must be at end of sigstring, here | |
590 if(ch == ')') | |
591 { | |
592 po = PyEval_CallObject(x->f, py_args); | |
593 if(po) | |
594 { | |
595 // return value type | |
596 ch = *++sig_ptr; | |
597 | |
598 // @@@ copypasta from above, as a bit different, NO error handling right now, NO handling of 'Z', ... | |
599 switch(ch) | |
600 { | |
601 case DC_SIGCHAR_BOOL: | |
602 if ( !PyBool_Check(po) ) | |
603 PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a bool", -1 ); | |
604 else | |
605 result->B = ((Py_True == po) ? DC_TRUE : DC_FALSE); | |
606 break; | |
607 | |
608 case DC_SIGCHAR_CHAR: | |
609 case DC_SIGCHAR_UCHAR: | |
610 py2dcchar(&result->c, po, ch == DC_SIGCHAR_UCHAR, -1); | |
611 break; | |
612 | |
613 case DC_SIGCHAR_SHORT: | |
614 case DC_SIGCHAR_USHORT: | |
615 py2dcshort(&result->s, po, ch == DC_SIGCHAR_USHORT, -1); | |
616 break; | |
617 | |
618 case DC_SIGCHAR_INT: | |
619 case DC_SIGCHAR_UINT: | |
620 if ( !DcPyInt_Check(po) ) | |
621 PyErr_Format( PyExc_RuntimeError, "arg %d - expecting an int", -1 ); | |
622 else | |
623 result->i = (DCint) DcPyInt_AS_LONG(po); | |
624 break; | |
625 | |
626 case DC_SIGCHAR_LONG: | |
627 case DC_SIGCHAR_ULONG: | |
628 if ( !DcPyInt_Check(po) ) | |
629 PyErr_Format( PyExc_RuntimeError, "arg %d - expecting an int", -1 ); | |
630 else | |
631 result->j = (DClong) PyLong_AsLong(po); | |
632 break; | |
633 | |
634 case DC_SIGCHAR_LONGLONG: | |
635 case DC_SIGCHAR_ULONGLONG: | |
636 py2dclonglong(&result->l, po, -1); | |
637 break; | |
638 | |
639 case DC_SIGCHAR_FLOAT: | |
640 if (!PyFloat_Check(po)) | |
641 PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a float", -1 ); | |
642 else | |
643 result->f = (float)PyFloat_AsDouble(po); | |
644 break; | |
645 | |
646 case DC_SIGCHAR_DOUBLE: | |
647 if (!PyFloat_Check(po)) | |
648 PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a float", -1 ); | |
649 else | |
650 result->d = PyFloat_AsDouble(po); | |
651 break; | |
652 | |
653 case DC_SIGCHAR_POINTER: // this will only accept integers, mutable array types (meaning only bytearray) or tuples describing a callback | |
654 py2dcpointer(&result->p, po, -1); | |
655 break; | |
656 } | |
657 | |
658 | |
659 Py_DECREF(po); | |
660 } | |
661 else | |
662 PyErr_SetString(PyExc_RuntimeError, "callback error: unknown error calling back python callback function"); | |
663 } | |
664 else | |
665 PyErr_Format(PyExc_RuntimeError, "callback error: python callback doesn't match signature argument count or signature wrong (invalid sig char or return type not specified)"); | |
666 | |
667 Py_DECREF(py_args); | |
668 } | |
669 else | |
670 PyErr_SetString(PyExc_RuntimeError, "callback error: unknown error creating python arg tuple"); | |
671 | |
672 // as callbacks might be called repeatedly we don't want the error indicator to pollute other calls, so print | |
673 if(PyErr_Occurred) { | |
674 PyErr_Print(); | |
675 return 'v'; // used as return char for errors @@@ unsure if smart, but it would at least indicate that no return value was set | |
676 } | |
677 | |
678 return ch; | |
679 } | |
680 | |
681 | |
682 /* new callback object function */ | |
683 | |
684 static PyObject* | |
685 pydc_new_callback(PyObject* self, PyObject* args) | |
686 { | |
687 PyObject* f; | |
688 const char* sig; | |
689 struct callback_userdata* ud; | |
690 DCCallback* cb; | |
691 | |
692 if (!PyArg_ParseTuple(args, "sO", &sig, &f) || !PyFunction_Check(f)) | |
693 return PyErr_Format(PyExc_RuntimeError, "argument mismatch"); | |
694 | |
695 // pass signature and f (as borrowed ptr) in userdata; not incrementing f's refcount, | |
696 // b/c we can probably expect user making sure callback exists when its needed/called | |
697 ud = malloc(sizeof(struct callback_userdata) + strlen(sig)+1); | |
698 cb = dcbNewCallback(sig, handle_py_callbacks, ud); | |
699 if(!cb) { | |
700 free(ud); | |
701 Py_RETURN_NONE; | |
702 } | |
703 | |
704 ud->f = f; | |
705 strcpy(ud->sig, sig); | |
706 return DcPyCObject_FromVoidPtr(cb, &free_callback); // !new ref! | |
707 } | |
708 | |
709 /* free callback object function */ | |
710 | |
711 static PyObject* | |
712 pydc_free_callback(PyObject* self, PyObject* args) | |
713 { | |
714 PyObject* pcobj; | |
715 void* cb; | |
716 | |
717 if (!PyArg_ParseTuple(args, "O", &pcobj)) | |
718 return PyErr_Format(PyExc_RuntimeError, "argument mismatch"); | |
719 | |
720 cb = DcPyCObject_AsVoidPtr(pcobj); | |
721 if (!cb) | |
722 return PyErr_Format(PyExc_RuntimeError, "cbhandle is NULL"); | |
723 | |
724 free(dcbGetUserData(cb)); // free helper struct callback_userdata | |
725 | |
726 dcbFreeCallback(cb); | |
727 DcPyCObject_SetVoidPtr(pcobj, NULL); | |
728 | |
729 //don't think I need to release it, as the pyobj is not equivalent to the held handle | |
730 //Py_XDECREF(pcobj); // release ref from pydc_load() | |
731 | |
732 Py_RETURN_NONE; | |
733 } | |
734 | |
735 | |
736 | |
737 | |
472 // module deinit | 738 // module deinit |
473 static void deinit_pydc(void* x) | 739 static void deinit_pydc(void* x) |
474 { | 740 { |
475 if(gpCall) { | 741 if(gpCall) { |
476 dcFree(gpCall); | 742 dcFree(gpCall); |
497 | 763 |
498 PyMODINIT_FUNC | 764 PyMODINIT_FUNC |
499 PY_MOD_INIT_FUNC_NAME(void) | 765 PY_MOD_INIT_FUNC_NAME(void) |
500 { | 766 { |
501 static PyMethodDef pydcMethods[] = { | 767 static PyMethodDef pydcMethods[] = { |
502 {"load", pydc_load, METH_VARARGS, "load library" }, | 768 {"load", pydc_load, METH_VARARGS, "load library" }, |
503 {"find", pydc_find, METH_VARARGS, "find symbols" }, | 769 {"find", pydc_find, METH_VARARGS, "find symbols" }, |
504 {"free", pydc_free, METH_VARARGS, "free library" }, | 770 {"free", pydc_free, METH_VARARGS, "free library" }, |
505 {"get_path", pydc_get_path, METH_VARARGS, "get library path"}, | 771 {"get_path", pydc_get_path, METH_VARARGS, "get library path" }, |
506 {"call", pydc_call, METH_VARARGS, "call function" }, | 772 {"call", pydc_call, METH_VARARGS, "call function" }, |
773 {"new_callback", pydc_new_callback, METH_VARARGS, "new callback obj" }, // @@@ doc: only functions, not every callable, and only with positional args | |
774 {"free_callback", pydc_free_callback, METH_VARARGS, "free callback obj"}, | |
507 {NULL,NULL,0,NULL} | 775 {NULL,NULL,0,NULL} |
508 }; | 776 }; |
509 | 777 |
510 PyObject* m; | 778 PyObject* m; |
511 #if PY_MAJOR_VERSION >= 3 | 779 #if PY_MAJOR_VERSION >= 3 |
514 #else | 782 #else |
515 m = Py_InitModule3(PYDC_MOD_NAME_STR, pydcMethods, PYDC_MOD_DESC_STR); | 783 m = Py_InitModule3(PYDC_MOD_NAME_STR, pydcMethods, PYDC_MOD_DESC_STR); |
516 // NOTE: there is no way to pass a pointer to deinit_pydc - see PEP 3121 for details | 784 // NOTE: there is no way to pass a pointer to deinit_pydc - see PEP 3121 for details |
517 #endif | 785 #endif |
518 | 786 |
787 /* we convert pointers to python ints via Py_BuildValue('n', ...) which expects Py_ssize_t */ | |
788 assert(sizeof(Py_ssize_t) >= sizeof(void*)); | |
789 | |
519 if(m) | 790 if(m) |
520 gpCall = dcNewCallVM(4096); //@@@ one shared callvm for the entire module, this is not reentrant | 791 gpCall = dcNewCallVM(4096); //@@@ one shared callvm for the entire module, this is not reentrant |
521 | 792 |
522 #if PY_MAJOR_VERSION >= 3 | 793 #if PY_MAJOR_VERSION >= 3 |
523 return m; | 794 return m; |