| #include "parts.h" |
| #include "util.h" |
| |
| static Py_ssize_t |
| get_code_extra_index(PyInterpreterState* interp) { |
| Py_ssize_t result = -1; |
| |
| static const char *key = "_testcapi.frame_evaluation.code_index"; |
| |
| PyObject *interp_dict = PyInterpreterState_GetDict(interp); // borrowed |
| assert(interp_dict); // real users would handle missing dict... somehow |
| |
| PyObject *index_obj; |
| if (PyDict_GetItemStringRef(interp_dict, key, &index_obj) < 0) { |
| goto finally; |
| } |
| Py_ssize_t index = 0; |
| if (!index_obj) { |
| index = PyUnstable_Eval_RequestCodeExtraIndex(NULL); |
| if (index < 0 || PyErr_Occurred()) { |
| goto finally; |
| } |
| index_obj = PyLong_FromSsize_t(index); // strong ref |
| if (!index_obj) { |
| goto finally; |
| } |
| int res = PyDict_SetItemString(interp_dict, key, index_obj); |
| Py_DECREF(index_obj); |
| if (res < 0) { |
| goto finally; |
| } |
| } |
| else { |
| index = PyLong_AsSsize_t(index_obj); |
| Py_DECREF(index_obj); |
| if (index == -1 && PyErr_Occurred()) { |
| goto finally; |
| } |
| } |
| |
| result = index; |
| finally: |
| return result; |
| } |
| |
| static PyObject * |
| test_code_extra(PyObject* self, PyObject *Py_UNUSED(callable)) |
| { |
| PyObject *result = NULL; |
| PyObject *test_module = NULL; |
| PyObject *test_func = NULL; |
| |
| // Get or initialize interpreter-specific code object storage index |
| PyInterpreterState *interp = PyInterpreterState_Get(); |
| if (!interp) { |
| return NULL; |
| } |
| Py_ssize_t code_extra_index = get_code_extra_index(interp); |
| if (PyErr_Occurred()) { |
| goto finally; |
| } |
| |
| // Get a function to test with |
| // This can be any Python function. Use `test.test_misc.testfunction`. |
| test_module = PyImport_ImportModule("test.test_capi.test_misc"); |
| if (!test_module) { |
| goto finally; |
| } |
| test_func = PyObject_GetAttrString(test_module, "testfunction"); |
| if (!test_func) { |
| goto finally; |
| } |
| PyObject *test_func_code = PyFunction_GetCode(test_func); // borrowed |
| if (!test_func_code) { |
| goto finally; |
| } |
| |
| // Check the value is initially NULL |
| void *extra = UNINITIALIZED_PTR; |
| int res = PyUnstable_Code_GetExtra(test_func_code, code_extra_index, &extra); |
| if (res < 0) { |
| goto finally; |
| } |
| assert (extra == NULL); |
| |
| // Set another code extra value |
| res = PyUnstable_Code_SetExtra(test_func_code, code_extra_index, (void*)(uintptr_t)77); |
| if (res < 0) { |
| goto finally; |
| } |
| // Assert it was set correctly |
| extra = UNINITIALIZED_PTR; |
| res = PyUnstable_Code_GetExtra(test_func_code, code_extra_index, &extra); |
| if (res < 0) { |
| goto finally; |
| } |
| assert ((uintptr_t)extra == 77); |
| // Revert to initial code extra value. |
| res = PyUnstable_Code_SetExtra(test_func_code, code_extra_index, NULL); |
| if (res < 0) { |
| goto finally; |
| } |
| result = Py_NewRef(Py_None); |
| finally: |
| Py_XDECREF(test_module); |
| Py_XDECREF(test_func); |
| return result; |
| } |
| |
| static PyMethodDef TestMethods[] = { |
| {"test_code_extra", test_code_extra, METH_NOARGS}, |
| {NULL}, |
| }; |
| |
| int |
| _PyTestCapi_Init_Code(PyObject *m) { |
| if (PyModule_AddFunctions(m, TestMethods) < 0) { |
| return -1; |
| } |
| |
| return 0; |
| } |