| #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_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_func = PyImport_ImportModuleAttrString("test.test_capi.test_misc", | |
| "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_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; | |
| } |