1 /*
2 * C Extension module to test Python internal C APIs (Include/internal).
3 */
4
5 #if !defined(Py_BUILD_CORE_BUILTIN) && !defined(Py_BUILD_CORE_MODULE)
6 # error "Py_BUILD_CORE_BUILTIN or Py_BUILD_CORE_MODULE must be defined"
7 #endif
8
9 /* Always enable assertions */
10 #undef NDEBUG
11
12 #define PY_SSIZE_T_CLEAN
13
14 #include "Python.h"
15 #include "pycore_byteswap.h" // _Py_bswap32()
16 #include "pycore_initconfig.h" // _Py_GetConfigsAsDict()
17 #include "pycore_hashtable.h" // _Py_hashtable_new()
18 #include "pycore_gc.h" // PyGC_Head
19
20
21 #ifdef MS_WINDOWS
22 #include <windows.h>
23
24 static int
_add_windows_config(PyObject * configs)25 _add_windows_config(PyObject *configs)
26 {
27 HMODULE hPython3;
28 wchar_t py3path[MAX_PATH];
29 PyObject *dict = PyDict_New();
30 PyObject *obj = NULL;
31 if (!dict) {
32 return -1;
33 }
34
35 hPython3 = GetModuleHandleW(PY3_DLLNAME);
36 if (hPython3 && GetModuleFileNameW(hPython3, py3path, MAX_PATH)) {
37 obj = PyUnicode_FromWideChar(py3path, -1);
38 } else {
39 obj = Py_None;
40 Py_INCREF(obj);
41 }
42 if (obj &&
43 !PyDict_SetItemString(dict, "python3_dll", obj) &&
44 !PyDict_SetItemString(configs, "windows", dict)) {
45 Py_DECREF(obj);
46 Py_DECREF(dict);
47 return 0;
48 }
49 Py_DECREF(obj);
50 Py_DECREF(dict);
51 return -1;
52 }
53 #endif
54
55
56 static PyObject *
get_configs(PyObject * self,PyObject * Py_UNUSED (args))57 get_configs(PyObject *self, PyObject *Py_UNUSED(args))
58 {
59 PyObject *dict = _Py_GetConfigsAsDict();
60 #ifdef MS_WINDOWS
61 if (dict) {
62 if (_add_windows_config(dict) < 0) {
63 Py_CLEAR(dict);
64 }
65 }
66 #endif
67 return dict;
68 }
69
70
71 static PyObject*
get_recursion_depth(PyObject * self,PyObject * Py_UNUSED (args))72 get_recursion_depth(PyObject *self, PyObject *Py_UNUSED(args))
73 {
74 PyThreadState *tstate = PyThreadState_Get();
75
76 /* subtract one to ignore the frame of the get_recursion_depth() call */
77 return PyLong_FromLong(tstate->recursion_depth - 1);
78 }
79
80
81 static PyObject*
test_bswap(PyObject * self,PyObject * Py_UNUSED (args))82 test_bswap(PyObject *self, PyObject *Py_UNUSED(args))
83 {
84 uint16_t u16 = _Py_bswap16(UINT16_C(0x3412));
85 if (u16 != UINT16_C(0x1234)) {
86 PyErr_Format(PyExc_AssertionError,
87 "_Py_bswap16(0x3412) returns %u", u16);
88 return NULL;
89 }
90
91 uint32_t u32 = _Py_bswap32(UINT32_C(0x78563412));
92 if (u32 != UINT32_C(0x12345678)) {
93 PyErr_Format(PyExc_AssertionError,
94 "_Py_bswap32(0x78563412) returns %lu", u32);
95 return NULL;
96 }
97
98 uint64_t u64 = _Py_bswap64(UINT64_C(0xEFCDAB9078563412));
99 if (u64 != UINT64_C(0x1234567890ABCDEF)) {
100 PyErr_Format(PyExc_AssertionError,
101 "_Py_bswap64(0xEFCDAB9078563412) returns %llu", u64);
102 return NULL;
103 }
104
105 Py_RETURN_NONE;
106 }
107
108
109 #define TO_PTR(ch) ((void*)(uintptr_t)ch)
110 #define FROM_PTR(ptr) ((uintptr_t)ptr)
111 #define VALUE(key) (1 + ((int)(key) - 'a'))
112
113 static Py_uhash_t
hash_char(const void * key)114 hash_char(const void *key)
115 {
116 char ch = (char)FROM_PTR(key);
117 return ch;
118 }
119
120
121 static int
hashtable_cb(_Py_hashtable_t * table,const void * key_ptr,const void * value_ptr,void * user_data)122 hashtable_cb(_Py_hashtable_t *table,
123 const void *key_ptr, const void *value_ptr,
124 void *user_data)
125 {
126 int *count = (int *)user_data;
127 char key = (char)FROM_PTR(key_ptr);
128 int value = (int)FROM_PTR(value_ptr);
129 assert(value == VALUE(key));
130 *count += 1;
131 return 0;
132 }
133
134
135 static PyObject*
test_hashtable(PyObject * self,PyObject * Py_UNUSED (args))136 test_hashtable(PyObject *self, PyObject *Py_UNUSED(args))
137 {
138 _Py_hashtable_t *table = _Py_hashtable_new(hash_char,
139 _Py_hashtable_compare_direct);
140 if (table == NULL) {
141 return PyErr_NoMemory();
142 }
143
144 // Using an newly allocated table must not crash
145 assert(table->nentries == 0);
146 assert(table->nbuckets > 0);
147 assert(_Py_hashtable_get(table, TO_PTR('x')) == NULL);
148
149 // Test _Py_hashtable_set()
150 char key;
151 for (key='a'; key <= 'z'; key++) {
152 int value = VALUE(key);
153 if (_Py_hashtable_set(table, TO_PTR(key), TO_PTR(value)) < 0) {
154 _Py_hashtable_destroy(table);
155 return PyErr_NoMemory();
156 }
157 }
158 assert(table->nentries == 26);
159 assert(table->nbuckets > table->nentries);
160
161 // Test _Py_hashtable_get_entry()
162 for (key='a'; key <= 'z'; key++) {
163 _Py_hashtable_entry_t *entry = _Py_hashtable_get_entry(table, TO_PTR(key));
164 assert(entry != NULL);
165 assert(entry->key == TO_PTR(key));
166 assert(entry->value == TO_PTR(VALUE(key)));
167 }
168
169 // Test _Py_hashtable_get()
170 for (key='a'; key <= 'z'; key++) {
171 void *value_ptr = _Py_hashtable_get(table, TO_PTR(key));
172 assert((int)FROM_PTR(value_ptr) == VALUE(key));
173 }
174
175 // Test _Py_hashtable_steal()
176 key = 'p';
177 void *value_ptr = _Py_hashtable_steal(table, TO_PTR(key));
178 assert((int)FROM_PTR(value_ptr) == VALUE(key));
179 assert(table->nentries == 25);
180 assert(_Py_hashtable_get_entry(table, TO_PTR(key)) == NULL);
181
182 // Test _Py_hashtable_foreach()
183 int count = 0;
184 int res = _Py_hashtable_foreach(table, hashtable_cb, &count);
185 assert(res == 0);
186 assert(count == 25);
187
188 // Test _Py_hashtable_clear()
189 _Py_hashtable_clear(table);
190 assert(table->nentries == 0);
191 assert(table->nbuckets > 0);
192 assert(_Py_hashtable_get(table, TO_PTR('x')) == NULL);
193
194 _Py_hashtable_destroy(table);
195 Py_RETURN_NONE;
196 }
197
198
199 static PyMethodDef TestMethods[] = {
200 {"get_configs", get_configs, METH_NOARGS},
201 {"get_recursion_depth", get_recursion_depth, METH_NOARGS},
202 {"test_bswap", test_bswap, METH_NOARGS},
203 {"test_hashtable", test_hashtable, METH_NOARGS},
204 {NULL, NULL} /* sentinel */
205 };
206
207
208 static struct PyModuleDef _testcapimodule = {
209 PyModuleDef_HEAD_INIT,
210 "_testinternalcapi",
211 NULL,
212 -1,
213 TestMethods,
214 NULL,
215 NULL,
216 NULL,
217 NULL
218 };
219
220
221 PyMODINIT_FUNC
PyInit__testinternalcapi(void)222 PyInit__testinternalcapi(void)
223 {
224 PyObject *module = PyModule_Create(&_testcapimodule);
225 if (module == NULL) {
226 return NULL;
227 }
228
229 if (PyModule_AddObject(module, "SIZEOF_PYGC_HEAD",
230 PyLong_FromSsize_t(sizeof(PyGC_Head))) < 0) {
231 goto error;
232 }
233
234 return module;
235
236 error:
237 Py_DECREF(module);
238 return NULL;
239 }
240