• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
24 static PyObject *
get_configs(PyObject * self,PyObject * Py_UNUSED (args))25 get_configs(PyObject *self, PyObject *Py_UNUSED(args))
26 {
27     return _Py_GetConfigsAsDict();
28 }
29 
30 
31 static PyObject*
get_recursion_depth(PyObject * self,PyObject * Py_UNUSED (args))32 get_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 
41 static PyObject*
test_bswap(PyObject * self,PyObject * Py_UNUSED (args))42 test_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 
69 static int
check_popcount(uint32_t x,int expected)70 check_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 
85 static PyObject*
test_popcount(PyObject * self,PyObject * Py_UNUSED (args))86 test_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 
108 static int
check_bit_length(unsigned long x,int expected)109 check_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 
124 static PyObject*
test_bit_length(PyObject * self,PyObject * Py_UNUSED (args))125 test_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 
151 static Py_uhash_t
hash_char(const void * key)152 hash_char(const void *key)
153 {
154     char ch = (char)FROM_PTR(key);
155     return ch;
156 }
157 
158 
159 static int
hashtable_cb(_Py_hashtable_t * table,const void * key_ptr,const void * value_ptr,void * user_data)160 hashtable_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 
173 static PyObject*
test_hashtable(PyObject * self,PyObject * Py_UNUSED (args))174 test_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 
237 static PyObject *
test_get_config(PyObject * Py_UNUSED (self),PyObject * Py_UNUSED (args))238 test_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 
252 static PyObject *
test_set_config(PyObject * Py_UNUSED (self),PyObject * dict)253 test_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 
266 error:
267     PyConfig_Clear(&config);
268     return NULL;
269 }
270 
271 
272 static PyObject*
test_atomic_funcs(PyObject * self,PyObject * Py_UNUSED (args))273 test_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 
283 static int
check_edit_cost(const char * a,const char * b,Py_ssize_t expected)284 check_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 (a_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;
328 exit:
329     Py_XDECREF(a_obj);
330     Py_XDECREF(b_obj);
331     return ret;
332 }
333 
334 static PyObject *
test_edit_cost(PyObject * self,PyObject * Py_UNUSED (args))335 test_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 
368 static 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 
383 static 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 
396 PyMODINIT_FUNC
PyInit__testinternalcapi(void)397 PyInit__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 
411 error:
412     Py_DECREF(module);
413     return NULL;
414 }
415