• 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_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