/ext/pycall/libpython.c

https://github.com/mrkn/pycall.rb · C · 228 lines · 187 code · 41 blank · 0 comment · 16 complexity · 88f2f777b119ade0123099f95cd6ca5d MD5 · raw file

  1. #include "pycall_internal.h"
  2. static pycall_libpython_api_table_t api_table = { NULL, };
  3. static int python_string_as_bytes;
  4. pycall_libpython_api_table_t *
  5. pycall_libpython_api_table(void)
  6. {
  7. return &api_table;
  8. }
  9. struct lookup_libpython_api_args {
  10. VALUE libpython_handle;
  11. char const *name;
  12. };
  13. static VALUE
  14. lookup_libpython_api_0(struct lookup_libpython_api_args *args)
  15. {
  16. return rb_funcall(args->libpython_handle, rb_intern("sym"), 1, rb_str_new2(args->name));
  17. }
  18. static void *
  19. lookup_libpython_api(VALUE libpython_handle, char const *name)
  20. {
  21. struct lookup_libpython_api_args arg;
  22. VALUE addr;
  23. int state;
  24. arg.libpython_handle = libpython_handle;
  25. arg.name = name;
  26. addr = rb_protect((VALUE (*)(VALUE))lookup_libpython_api_0, (VALUE)&arg, &state);
  27. if (state) {
  28. rb_set_errinfo(Qnil);
  29. return NULL;
  30. }
  31. else {
  32. return NIL_P(addr) ? NULL : NUM2PTR(addr);
  33. }
  34. }
  35. #define LOOKUP_API_ENTRY(api_name) lookup_libpython_api(libpython_handle, #api_name)
  36. #define CHECK_API_ENTRY(api_name) (LOOKUP_API_ENTRY(api_name) != NULL)
  37. #define required 1
  38. #define optional 0
  39. #define INIT_API_TABLE_ENTRY2(member_name, api_name, required) do { \
  40. void *fptr = LOOKUP_API_ENTRY(api_name); \
  41. if (!fptr && required) { \
  42. rb_raise(eLibPythonFunctionNotFound, "Unable to find the required symbol in libpython: %s", #api_name); \
  43. } \
  44. (api_table.member_name) = fptr; \
  45. } while (0)
  46. #define INIT_API_TABLE_ENTRY(api_name, required) INIT_API_TABLE_ENTRY2(api_name, api_name, required)
  47. #define INIT_API_TABLE_ENTRY_PTR(api_name, required) do { \
  48. INIT_API_TABLE_ENTRY(api_name, required); \
  49. (api_table.api_name) = *(void **)(api_table.api_name); \
  50. } while (0)
  51. void
  52. pycall_init_libpython_api_table(VALUE libpython_handle)
  53. {
  54. VALUE eLibPythonFunctionNotFound = rb_const_get_at(pycall_mPyCall, rb_intern("LibPythonFunctionNotFound"));
  55. INIT_API_TABLE_ENTRY(_Py_NoneStruct, required);
  56. INIT_API_TABLE_ENTRY(PyBool_Type, required);
  57. INIT_API_TABLE_ENTRY(PyClass_Type, optional);
  58. INIT_API_TABLE_ENTRY(PyComplex_Type, required);
  59. INIT_API_TABLE_ENTRY(PyDict_Type, required);
  60. INIT_API_TABLE_ENTRY(PyFloat_Type, required);
  61. INIT_API_TABLE_ENTRY(PyList_Type, required);
  62. INIT_API_TABLE_ENTRY(PyInstance_Type, optional);
  63. INIT_API_TABLE_ENTRY(PyInt_Type, optional);
  64. INIT_API_TABLE_ENTRY(PyLong_Type, required);
  65. INIT_API_TABLE_ENTRY(PyModule_Type, required);
  66. python_string_as_bytes = !CHECK_API_ENTRY(PyString_Type);
  67. if (python_string_as_bytes) {
  68. INIT_API_TABLE_ENTRY2(PyString_Type, PyBytes_Type, required);
  69. }
  70. else {
  71. INIT_API_TABLE_ENTRY(PyString_Type, required);
  72. }
  73. INIT_API_TABLE_ENTRY(PyTuple_Type, required);
  74. INIT_API_TABLE_ENTRY(PyType_Type, required);
  75. INIT_API_TABLE_ENTRY(PyUnicode_Type, required);
  76. INIT_API_TABLE_ENTRY(Py_InitializeEx, required);
  77. INIT_API_TABLE_ENTRY(Py_IsInitialized, required);
  78. INIT_API_TABLE_ENTRY(Py_GetVersion, required);
  79. INIT_API_TABLE_ENTRY(PySys_SetArgvEx, required);
  80. INIT_API_TABLE_ENTRY(Py_IncRef, required);
  81. INIT_API_TABLE_ENTRY(Py_DecRef, required);
  82. INIT_API_TABLE_ENTRY(_PyObject_New, required);
  83. INIT_API_TABLE_ENTRY(PyCallable_Check, required);
  84. INIT_API_TABLE_ENTRY(PyObject_IsInstance, required);
  85. INIT_API_TABLE_ENTRY(PyObject_IsSubclass, required);
  86. INIT_API_TABLE_ENTRY2(PyObject_Hash._hash_t, PyObject_Hash, required);
  87. INIT_API_TABLE_ENTRY(PyObject_RichCompare, required);
  88. INIT_API_TABLE_ENTRY(PyObject_Call, required);
  89. INIT_API_TABLE_ENTRY(PyObject_CallMethod, required);
  90. INIT_API_TABLE_ENTRY(PyObject_Dir, required);
  91. INIT_API_TABLE_ENTRY(PyObject_GenericGetAttr, required);
  92. INIT_API_TABLE_ENTRY(PyObject_GetAttrString, required);
  93. INIT_API_TABLE_ENTRY(PyObject_SetAttrString, required);
  94. INIT_API_TABLE_ENTRY(PyObject_HasAttrString, required);
  95. INIT_API_TABLE_ENTRY(PyObject_GetItem, required);
  96. INIT_API_TABLE_ENTRY(PyObject_SetItem, required);
  97. INIT_API_TABLE_ENTRY(PyObject_DelItem, required);
  98. INIT_API_TABLE_ENTRY(PyObject_GetIter, required);
  99. INIT_API_TABLE_ENTRY(PyObject_Str, required);
  100. INIT_API_TABLE_ENTRY(PyObject_Repr, required);
  101. INIT_API_TABLE_ENTRY(PyType_Ready, required);
  102. INIT_API_TABLE_ENTRY(PyType_GenericNew, required);
  103. INIT_API_TABLE_ENTRY(PyCFunction_NewEx, required);
  104. INIT_API_TABLE_ENTRY(PyWeakref_NewRef, required);
  105. INIT_API_TABLE_ENTRY(PyBool_FromLong, required);
  106. INIT_API_TABLE_ENTRY(PyComplex_RealAsDouble, required);
  107. INIT_API_TABLE_ENTRY(PyComplex_ImagAsDouble, required);
  108. INIT_API_TABLE_ENTRY(PyComplex_FromDoubles, required);
  109. INIT_API_TABLE_ENTRY(PyFloat_AsDouble, required);
  110. INIT_API_TABLE_ENTRY(PyFloat_FromDouble, required);
  111. INIT_API_TABLE_ENTRY(PyList_New, required);
  112. INIT_API_TABLE_ENTRY(PyList_Size, required);
  113. INIT_API_TABLE_ENTRY(PyList_GetItem, required);
  114. INIT_API_TABLE_ENTRY(PyList_SetItem, required);
  115. INIT_API_TABLE_ENTRY(PyList_Insert, required);
  116. INIT_API_TABLE_ENTRY(PyList_Append, required);
  117. INIT_API_TABLE_ENTRY(PyInt_FromLong, optional);
  118. INIT_API_TABLE_ENTRY(PyInt_FromSsize_t, optional);
  119. INIT_API_TABLE_ENTRY(PyInt_AsSsize_t, optional);
  120. INIT_API_TABLE_ENTRY(PyLong_AsLongAndOverflow, required);
  121. INIT_API_TABLE_ENTRY(PyLong_FromLong, required);
  122. #ifdef HAVE_LONG_LONG
  123. INIT_API_TABLE_ENTRY(PyLong_AsLongLongAndOverflow, required);
  124. INIT_API_TABLE_ENTRY(PyLong_FromLongLong, required);
  125. #endif
  126. INIT_API_TABLE_ENTRY(PyLong_AsSsize_t, required);
  127. INIT_API_TABLE_ENTRY(PyTuple_New, required);
  128. INIT_API_TABLE_ENTRY(PyTuple_Size, required);
  129. INIT_API_TABLE_ENTRY(PyTuple_GetItem, required);
  130. INIT_API_TABLE_ENTRY(PyTuple_SetItem, required);
  131. INIT_API_TABLE_ENTRY(PySlice_New, required);
  132. INIT_API_TABLE_ENTRY(PyIter_Next, required);
  133. INIT_API_TABLE_ENTRY(PyEval_ThreadsInitialized, required);
  134. INIT_API_TABLE_ENTRY(PyEval_InitThreads, required);
  135. INIT_API_TABLE_ENTRY(PyErr_Occurred, required);
  136. INIT_API_TABLE_ENTRY(PyErr_Fetch, required);
  137. INIT_API_TABLE_ENTRY(PyErr_Restore, required);
  138. INIT_API_TABLE_ENTRY(PyErr_Clear, required);
  139. INIT_API_TABLE_ENTRY(PyErr_SetString, required);
  140. INIT_API_TABLE_ENTRY(PyErr_Format, required);
  141. INIT_API_TABLE_ENTRY(PyErr_SetInterrupt, required);
  142. INIT_API_TABLE_ENTRY(PyImport_ImportModule, required);
  143. INIT_API_TABLE_ENTRY(PyImport_ImportModuleLevel, required);
  144. INIT_API_TABLE_ENTRY(PyOS_AfterFork, required);
  145. INIT_API_TABLE_ENTRY(PyList_Size, required);
  146. INIT_API_TABLE_ENTRY(PyList_GetItem, required);
  147. INIT_API_TABLE_ENTRY(PyDict_New, required);
  148. INIT_API_TABLE_ENTRY(PyDict_Contains, required);
  149. INIT_API_TABLE_ENTRY(PyDict_SetItemString, required);
  150. INIT_API_TABLE_ENTRY(PyDict_Next, required);
  151. INIT_API_TABLE_ENTRY(PySequence_Check, required);
  152. INIT_API_TABLE_ENTRY(PySequence_Size, required);
  153. INIT_API_TABLE_ENTRY(PySequence_Contains, required);
  154. INIT_API_TABLE_ENTRY(PySequence_GetItem, required);
  155. if (python_string_as_bytes) {
  156. INIT_API_TABLE_ENTRY2(PyString_AsStringAndSize, PyBytes_AsStringAndSize, required);
  157. INIT_API_TABLE_ENTRY2(PyString_FromStringAndSize, PyBytes_FromStringAndSize, required);
  158. INIT_API_TABLE_ENTRY2(PyString_FromFormatV, PyBytes_FromFormat, required);
  159. }
  160. else {
  161. INIT_API_TABLE_ENTRY(PyString_AsStringAndSize, required);
  162. INIT_API_TABLE_ENTRY(PyString_FromStringAndSize, required);
  163. INIT_API_TABLE_ENTRY(PyString_FromFormatV, required);
  164. }
  165. if (CHECK_API_ENTRY(PyUnicode_DecodeUTF8)) {
  166. INIT_API_TABLE_ENTRY(PyUnicode_AsUTF8String, required);
  167. INIT_API_TABLE_ENTRY(PyUnicode_DecodeUTF8, required);
  168. INIT_API_TABLE_ENTRY(PyUnicode_FromFormatV, required);
  169. }
  170. else if (CHECK_API_ENTRY(PyUnicodeUCS4_DecodeUTF8)) {
  171. INIT_API_TABLE_ENTRY2(PyUnicode_AsUTF8String, PyUnicodeUCS4_AsUTF8String, required);
  172. INIT_API_TABLE_ENTRY2(PyUnicode_DecodeUTF8, PyUnicodeUCS4_DecodeUTF8, required);
  173. INIT_API_TABLE_ENTRY2(PyUnicode_FromFormatV, PyUnicodeUCS4_FromFormatV, required);
  174. }
  175. else if (CHECK_API_ENTRY(PyUnicodeUCS2_DecodeUTF8)) {
  176. INIT_API_TABLE_ENTRY2(PyUnicode_AsUTF8String, PyUnicodeUCS2_AsUTF8String, required);
  177. INIT_API_TABLE_ENTRY2(PyUnicode_DecodeUTF8, PyUnicodeUCS2_DecodeUTF8, required);
  178. INIT_API_TABLE_ENTRY2(PyUnicode_FromFormatV, PyUnicodeUCS2_FromFormatV, required);
  179. }
  180. }
  181. void
  182. pycall_init_exceptions(VALUE libpython_handle)
  183. {
  184. VALUE eLibPythonFunctionNotFound = rb_const_get_at(pycall_mPyCall, rb_intern("LibPythonFunctionNotFound"));
  185. INIT_API_TABLE_ENTRY_PTR(PyExc_RuntimeError, required);
  186. INIT_API_TABLE_ENTRY_PTR(PyExc_TypeError, required);
  187. }