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_atomic_funcs.h" // _Py_atomic_int_get()
16#include "pycore_bitutils.h" // _Py_bswap32()
17#include "pycore_gc.h" // PyGC_Head
18#include "pycore_hashtable.h" // _Py_hashtable_new()
19#include "pycore_initconfig.h" // _Py_GetConfigsAsDict()
20#include "pycore_interp.h" // _PyInterpreterState_GetConfigCopy()
21#include "pycore_pyerrors.h" // _Py_UTF8_Edit_Cost()
22
23
24static PyObject *
25get_configs(PyObject *self, PyObject *Py_UNUSED(args))
26{
27 return _Py_GetConfigsAsDict();
28}
29
30
31static PyObject*
32get_recursion_depth(PyObject *self, PyObject *Py_UNUSED(args))
33{
34 PyThreadState *tstate = PyThreadState_Get();
35
36 /* subtract one to ignore the frame of the get_recursion_depth() call */
37 return PyLong_FromLong(tstate->recursion_depth - 1);
38}
39
40
41static PyObject*
42test_bswap(PyObject *self, PyObject *Py_UNUSED(args))
43{
44 uint16_t u16 = _Py_bswap16(UINT16_C(0x3412));
45 if (u16 != UINT16_C(0x1234)) {
46 PyErr_Format(PyExc_AssertionError,
47 "_Py_bswap16(0x3412) returns %u", u16);
48 return NULL;
49 }
50
51 uint32_t u32 = _Py_bswap32(UINT32_C(0x78563412));
52 if (u32 != UINT32_C(0x12345678)) {
53 PyErr_Format(PyExc_AssertionError,
54 "_Py_bswap32(0x78563412) returns %lu", u32);
55 return NULL;
56 }
57
58 uint64_t u64 = _Py_bswap64(UINT64_C(0xEFCDAB9078563412));
59 if (u64 != UINT64_C(0x1234567890ABCDEF)) {
60 PyErr_Format(PyExc_AssertionError,
61 "_Py_bswap64(0xEFCDAB9078563412) returns %llu", u64);
62 return NULL;
63 }
64
65 Py_RETURN_NONE;
66}
67
68
69static int
70check_popcount(uint32_t x, int expected)
71{
72 // Use volatile to prevent the compiler to optimize out the whole test
73 volatile uint32_t u = x;
74 int bits = _Py_popcount32(u);
75 if (bits != expected) {
76 PyErr_Format(PyExc_AssertionError,
77 "_Py_popcount32(%lu) returns %i, expected %i",
78 (unsigned long)x, bits, expected);
79 return -1;
80 }
81 return 0;
82}
83
84
85static PyObject*
86test_popcount(PyObject *self, PyObject *Py_UNUSED(args))
87{
88#define CHECK(X, RESULT) \
89 do { \
90 if (check_popcount(X, RESULT) < 0) { \
91 return NULL; \
92 } \
93 } while (0)
94
95 CHECK(0, 0);
96 CHECK(1, 1);
97 CHECK(0x08080808, 4);
98 CHECK(0x10101010, 4);
99 CHECK(0x10204080, 4);
100 CHECK(0xDEADCAFE, 22);
101 CHECK(0xFFFFFFFF, 32);
102 Py_RETURN_NONE;
103
104#undef CHECK
105}
106
107
108static int
109check_bit_length(unsigned long x, int expected)
110{
111 // Use volatile to prevent the compiler to optimize out the whole test
112 volatile unsigned long u = x;
113 int len = _Py_bit_length(u);
114 if (len != expected) {
115 PyErr_Format(PyExc_AssertionError,
116 "_Py_bit_length(%lu) returns %i, expected %i",
117 x, len, expected);
118 return -1;
119 }
120 return 0;
121}
122
123
124static PyObject*
125test_bit_length(PyObject *self, PyObject *Py_UNUSED(args))
126{
127#define CHECK(X, RESULT) \
128 do { \
129 if (check_bit_length(X, RESULT) < 0) { \
130 return NULL; \
131 } \
132 } while (0)
133
134 CHECK(0, 0);
135 CHECK(1, 1);
136 CHECK(0x1000, 13);
137 CHECK(0x1234, 13);
138 CHECK(0x54321, 19);
139 CHECK(0x7FFFFFFF, 31);
140 CHECK(0xFFFFFFFF, 32);
141 Py_RETURN_NONE;
142
143#undef CHECK
144}
145
146
147#define TO_PTR(ch) ((void*)(uintptr_t)ch)
148#define FROM_PTR(ptr) ((uintptr_t)ptr)
149#define VALUE(key) (1 + ((int)(key) - 'a'))
150
151static Py_uhash_t
152hash_char(const void *key)
153{
154 char ch = (char)FROM_PTR(key);
155 return ch;
156}
157
158
159static int
160hashtable_cb(_Py_hashtable_t *table,
161 const void *key_ptr, const void *value_ptr,
162 void *user_data)
163{
164 int *count = (int *)user_data;
165 char key = (char)FROM_PTR(key_ptr);
166 int value = (int)FROM_PTR(value_ptr);
167 assert(value == VALUE(key));
168 *count += 1;
169 return 0;
170}
171
172
173static PyObject*
174test_hashtable(PyObject *self, PyObject *Py_UNUSED(args))
175{
176 _Py_hashtable_t *table = _Py_hashtable_new(hash_char,
177 _Py_hashtable_compare_direct);
178 if (table == NULL) {
179 return PyErr_NoMemory();
180 }
181
182 // Using an newly allocated table must not crash
183 assert(table->nentries == 0);
184 assert(table->nbuckets > 0);
185 assert(_Py_hashtable_get(table, TO_PTR('x')) == NULL);
186
187 // Test _Py_hashtable_set()
188 char key;
189 for (key='a'; key <= 'z'; key++) {
190 int value = VALUE(key);
191 if (_Py_hashtable_set(table, TO_PTR(key), TO_PTR(value)) < 0) {
192 _Py_hashtable_destroy(table);
193 return PyErr_NoMemory();
194 }
195 }
196 assert(table->nentries == 26);
197 assert(table->nbuckets > table->nentries);
198
199 // Test _Py_hashtable_get_entry()
200 for (key='a'; key <= 'z'; key++) {
201 _Py_hashtable_entry_t *entry = _Py_hashtable_get_entry(table, TO_PTR(key));
202 assert(entry != NULL);
203 assert(entry->key == TO_PTR(key));
204 assert(entry->value == TO_PTR(VALUE(key)));
205 }
206
207 // Test _Py_hashtable_get()
208 for (key='a'; key <= 'z'; key++) {
209 void *value_ptr = _Py_hashtable_get(table, TO_PTR(key));
210 assert((int)FROM_PTR(value_ptr) == VALUE(key));
211 }
212
213 // Test _Py_hashtable_steal()
214 key = 'p';
215 void *value_ptr = _Py_hashtable_steal(table, TO_PTR(key));
216 assert((int)FROM_PTR(value_ptr) == VALUE(key));
217 assert(table->nentries == 25);
218 assert(_Py_hashtable_get_entry(table, TO_PTR(key)) == NULL);
219
220 // Test _Py_hashtable_foreach()
221 int count = 0;
222 int res = _Py_hashtable_foreach(table, hashtable_cb, &count);
223 assert(res == 0);
224 assert(count == 25);
225
226 // Test _Py_hashtable_clear()
227 _Py_hashtable_clear(table);
228 assert(table->nentries == 0);
229 assert(table->nbuckets > 0);
230 assert(_Py_hashtable_get(table, TO_PTR('x')) == NULL);
231
232 _Py_hashtable_destroy(table);
233 Py_RETURN_NONE;
234}
235
236
237static PyObject *
238test_get_config(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args))
239{
240 PyConfig config;
241 PyConfig_InitIsolatedConfig(&config);
242 if (_PyInterpreterState_GetConfigCopy(&config) < 0) {
243 PyConfig_Clear(&config);
244 return NULL;
245 }
246 PyObject *dict = _PyConfig_AsDict(&config);
247 PyConfig_Clear(&config);
248 return dict;
249}
250
251
252static PyObject *
253test_set_config(PyObject *Py_UNUSED(self), PyObject *dict)
254{
255 PyConfig config;
256 PyConfig_InitIsolatedConfig(&config);
257 if (_PyConfig_FromDict(&config, dict) < 0) {
258 goto error;
259 }
260 if (_PyInterpreterState_SetConfig(&config) < 0) {
261 goto error;
262 }
263 PyConfig_Clear(&config);
264 Py_RETURN_NONE;
265
266error:
267 PyConfig_Clear(&config);
268 return NULL;
269}
270
271
272static PyObject*
273test_atomic_funcs(PyObject *self, PyObject *Py_UNUSED(args))
274{
275 // Test _Py_atomic_size_get() and _Py_atomic_size_set()
276 Py_ssize_t var = 1;
277 _Py_atomic_size_set(&var, 2);
278 assert(_Py_atomic_size_get(&var) == 2);
279 Py_RETURN_NONE;
280}
281
282
283static int
284check_edit_cost(const char *a, const char *b, Py_ssize_t expected)
285{
286 int ret = -1;
287 PyObject *a_obj = NULL;
288 PyObject *b_obj = NULL;
289
290 a_obj = PyUnicode_FromString(a);
291 if (a_obj == NULL) {
292 goto exit;
293 }
294 b_obj = PyUnicode_FromString(b);
295 if (b_obj == NULL) {
296 goto exit;
297 }
298 Py_ssize_t result = _Py_UTF8_Edit_Cost(a_obj, b_obj, -1);
299 if (result != expected) {
300 PyErr_Format(PyExc_AssertionError,
301 "Edit cost from '%s' to '%s' returns %zd, expected %zd",
302 a, b, result, expected);
303 goto exit;
304 }
305 // Check that smaller max_edits thresholds are exceeded.
306 Py_ssize_t max_edits = result;
307 while (max_edits > 0) {
308 max_edits /= 2;
309 Py_ssize_t result2 = _Py_UTF8_Edit_Cost(a_obj, b_obj, max_edits);
310 if (result2 <= max_edits) {
311 PyErr_Format(PyExc_AssertionError,
312 "Edit cost from '%s' to '%s' (threshold %zd) "
313 "returns %zd, expected greater than %zd",
314 a, b, max_edits, result2, max_edits);
315 goto exit;
316 }
317 }
318 // Check that bigger max_edits thresholds don't change anything
319 Py_ssize_t result3 = _Py_UTF8_Edit_Cost(a_obj, b_obj, result * 2 + 1);
320 if (result3 != result) {
321 PyErr_Format(PyExc_AssertionError,
322 "Edit cost from '%s' to '%s' (threshold %zd) "
323 "returns %zd, expected %zd",
324 a, b, result * 2, result3, result);
325 goto exit;
326 }
327 ret = 0;
328exit:
329 Py_XDECREF(a_obj);
330 Py_XDECREF(b_obj);
331 return ret;
332}
333
334static PyObject *
335test_edit_cost(PyObject *self, PyObject *Py_UNUSED(args))
336{
337 #define CHECK(a, b, n) do { \
338 if (check_edit_cost(a, b, n) < 0) { \
339 return NULL; \
340 } \
341 } while (0) \
342
343 CHECK("", "", 0);
344 CHECK("", "a", 2);
345 CHECK("a", "A", 1);
346 CHECK("Apple", "Aple", 2);
347 CHECK("Banana", "B@n@n@", 6);
348 CHECK("Cherry", "Cherry!", 2);
349 CHECK("---0---", "------", 2);
350 CHECK("abc", "y", 6);
351 CHECK("aa", "bb", 4);
352 CHECK("aaaaa", "AAAAA", 5);
353 CHECK("wxyz", "wXyZ", 2);
354 CHECK("wxyz", "wXyZ123", 8);
355 CHECK("Python", "Java", 12);
356 CHECK("Java", "C#", 8);
357 CHECK("AbstractFoobarManager", "abstract_foobar_manager", 3+2*2);
358 CHECK("CPython", "PyPy", 10);
359 CHECK("CPython", "pypy", 11);
360 CHECK("AttributeError", "AttributeErrop", 2);
361 CHECK("AttributeError", "AttributeErrorTests", 10);
362
363 #undef CHECK
364 Py_RETURN_NONE;
365}
366
367
368static PyMethodDef TestMethods[] = {
369 {"get_configs", get_configs, METH_NOARGS},
370 {"get_recursion_depth", get_recursion_depth, METH_NOARGS},
371 {"test_bswap", test_bswap, METH_NOARGS},
372 {"test_popcount", test_popcount, METH_NOARGS},
373 {"test_bit_length", test_bit_length, METH_NOARGS},
374 {"test_hashtable", test_hashtable, METH_NOARGS},
375 {"get_config", test_get_config, METH_NOARGS},
376 {"set_config", test_set_config, METH_O},
377 {"test_atomic_funcs", test_atomic_funcs, METH_NOARGS},
378 {"test_edit_cost", test_edit_cost, METH_NOARGS},
379 {NULL, NULL} /* sentinel */
380};
381
382
383static struct PyModuleDef _testcapimodule = {
384 PyModuleDef_HEAD_INIT,
385 "_testinternalcapi",
386 NULL,
387 -1,
388 TestMethods,
389 NULL,
390 NULL,
391 NULL,
392 NULL
393};
394
395
396PyMODINIT_FUNC
397PyInit__testinternalcapi(void)
398{
399 PyObject *module = PyModule_Create(&_testcapimodule);
400 if (module == NULL) {
401 return NULL;
402 }
403
404 if (PyModule_AddObject(module, "SIZEOF_PYGC_HEAD",
405 PyLong_FromSsize_t(sizeof(PyGC_Head))) < 0) {
406 goto error;
407 }
408
409 return module;
410
411error:
412 Py_DECREF(module);
413 return NULL;
414}
415