dict_ops.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. // Dict primitive operations
  2. //
  3. // These are registered in mypyc.primitives.dict_ops.
  4. #include <Python.h>
  5. #include "CPy.h"
  6. #ifndef Py_TPFLAGS_MAPPING
  7. #define Py_TPFLAGS_MAPPING (1 << 6)
  8. #endif
  9. // Dict subclasses like defaultdict override things in interesting
  10. // ways, so we don't want to just directly use the dict methods. Not
  11. // sure if it is actually worth doing all this stuff, but it saves
  12. // some indirections.
  13. PyObject *CPyDict_GetItem(PyObject *dict, PyObject *key) {
  14. if (PyDict_CheckExact(dict)) {
  15. PyObject *res = PyDict_GetItemWithError(dict, key);
  16. if (!res) {
  17. if (!PyErr_Occurred()) {
  18. PyErr_SetObject(PyExc_KeyError, key);
  19. }
  20. } else {
  21. Py_INCREF(res);
  22. }
  23. return res;
  24. } else {
  25. return PyObject_GetItem(dict, key);
  26. }
  27. }
  28. PyObject *CPyDict_Build(Py_ssize_t size, ...) {
  29. Py_ssize_t i;
  30. PyObject *res = _PyDict_NewPresized(size);
  31. if (res == NULL) {
  32. return NULL;
  33. }
  34. va_list args;
  35. va_start(args, size);
  36. for (i = 0; i < size; i++) {
  37. PyObject *key = va_arg(args, PyObject *);
  38. PyObject *value = va_arg(args, PyObject *);
  39. if (PyDict_SetItem(res, key, value)) {
  40. Py_DECREF(res);
  41. return NULL;
  42. }
  43. }
  44. va_end(args);
  45. return res;
  46. }
  47. PyObject *CPyDict_Get(PyObject *dict, PyObject *key, PyObject *fallback) {
  48. // We are dodgily assuming that get on a subclass doesn't have
  49. // different behavior.
  50. PyObject *res = PyDict_GetItemWithError(dict, key);
  51. if (!res) {
  52. if (PyErr_Occurred()) {
  53. return NULL;
  54. }
  55. res = fallback;
  56. }
  57. Py_INCREF(res);
  58. return res;
  59. }
  60. PyObject *CPyDict_GetWithNone(PyObject *dict, PyObject *key) {
  61. return CPyDict_Get(dict, key, Py_None);
  62. }
  63. PyObject *CPyDict_SetDefault(PyObject *dict, PyObject *key, PyObject *value) {
  64. if (PyDict_CheckExact(dict)) {
  65. PyObject* ret = PyDict_SetDefault(dict, key, value);
  66. Py_XINCREF(ret);
  67. return ret;
  68. }
  69. _Py_IDENTIFIER(setdefault);
  70. return _PyObject_CallMethodIdObjArgs(dict, &PyId_setdefault, key, value, NULL);
  71. }
  72. PyObject *CPyDict_SetDefaultWithNone(PyObject *dict, PyObject *key) {
  73. return CPyDict_SetDefault(dict, key, Py_None);
  74. }
  75. PyObject *CPyDict_SetDefaultWithEmptyDatatype(PyObject *dict, PyObject *key,
  76. int data_type) {
  77. PyObject *res = CPyDict_GetItem(dict, key);
  78. if (!res) {
  79. // CPyDict_GetItem() would generates a PyExc_KeyError
  80. // when key is not found.
  81. PyErr_Clear();
  82. PyObject *new_obj;
  83. if (data_type == 1) {
  84. new_obj = PyList_New(0);
  85. } else if (data_type == 2) {
  86. new_obj = PyDict_New();
  87. } else if (data_type == 3) {
  88. new_obj = PySet_New(NULL);
  89. } else {
  90. return NULL;
  91. }
  92. if (CPyDict_SetItem(dict, key, new_obj) == -1) {
  93. return NULL;
  94. } else {
  95. return new_obj;
  96. }
  97. } else {
  98. return res;
  99. }
  100. }
  101. int CPyDict_SetItem(PyObject *dict, PyObject *key, PyObject *value) {
  102. if (PyDict_CheckExact(dict)) {
  103. return PyDict_SetItem(dict, key, value);
  104. } else {
  105. return PyObject_SetItem(dict, key, value);
  106. }
  107. }
  108. static inline int CPy_ObjectToStatus(PyObject *obj) {
  109. if (obj) {
  110. Py_DECREF(obj);
  111. return 0;
  112. } else {
  113. return -1;
  114. }
  115. }
  116. static int CPyDict_UpdateGeneral(PyObject *dict, PyObject *stuff) {
  117. _Py_IDENTIFIER(update);
  118. PyObject *res = _PyObject_CallMethodIdOneArg(dict, &PyId_update, stuff);
  119. return CPy_ObjectToStatus(res);
  120. }
  121. int CPyDict_UpdateInDisplay(PyObject *dict, PyObject *stuff) {
  122. // from https://github.com/python/cpython/blob/55d035113dfb1bd90495c8571758f504ae8d4802/Python/ceval.c#L2710
  123. int ret = PyDict_Update(dict, stuff);
  124. if (ret < 0) {
  125. if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
  126. PyErr_Format(PyExc_TypeError,
  127. "'%.200s' object is not a mapping",
  128. Py_TYPE(stuff)->tp_name);
  129. }
  130. }
  131. return ret;
  132. }
  133. int CPyDict_Update(PyObject *dict, PyObject *stuff) {
  134. if (PyDict_CheckExact(dict)) {
  135. return PyDict_Update(dict, stuff);
  136. } else {
  137. return CPyDict_UpdateGeneral(dict, stuff);
  138. }
  139. }
  140. int CPyDict_UpdateFromAny(PyObject *dict, PyObject *stuff) {
  141. if (PyDict_CheckExact(dict)) {
  142. // Argh this sucks
  143. _Py_IDENTIFIER(keys);
  144. if (PyDict_Check(stuff) || _CPyObject_HasAttrId(stuff, &PyId_keys)) {
  145. return PyDict_Update(dict, stuff);
  146. } else {
  147. return PyDict_MergeFromSeq2(dict, stuff, 1);
  148. }
  149. } else {
  150. return CPyDict_UpdateGeneral(dict, stuff);
  151. }
  152. }
  153. PyObject *CPyDict_FromAny(PyObject *obj) {
  154. if (PyDict_Check(obj)) {
  155. return PyDict_Copy(obj);
  156. } else {
  157. int res;
  158. PyObject *dict = PyDict_New();
  159. if (!dict) {
  160. return NULL;
  161. }
  162. _Py_IDENTIFIER(keys);
  163. if (_CPyObject_HasAttrId(obj, &PyId_keys)) {
  164. res = PyDict_Update(dict, obj);
  165. } else {
  166. res = PyDict_MergeFromSeq2(dict, obj, 1);
  167. }
  168. if (res < 0) {
  169. Py_DECREF(dict);
  170. return NULL;
  171. }
  172. return dict;
  173. }
  174. }
  175. PyObject *CPyDict_KeysView(PyObject *dict) {
  176. if (PyDict_CheckExact(dict)){
  177. return _CPyDictView_New(dict, &PyDictKeys_Type);
  178. }
  179. _Py_IDENTIFIER(keys);
  180. return _PyObject_CallMethodIdNoArgs(dict, &PyId_keys);
  181. }
  182. PyObject *CPyDict_ValuesView(PyObject *dict) {
  183. if (PyDict_CheckExact(dict)){
  184. return _CPyDictView_New(dict, &PyDictValues_Type);
  185. }
  186. _Py_IDENTIFIER(values);
  187. return _PyObject_CallMethodIdNoArgs(dict, &PyId_values);
  188. }
  189. PyObject *CPyDict_ItemsView(PyObject *dict) {
  190. if (PyDict_CheckExact(dict)){
  191. return _CPyDictView_New(dict, &PyDictItems_Type);
  192. }
  193. _Py_IDENTIFIER(items);
  194. return _PyObject_CallMethodIdNoArgs(dict, &PyId_items);
  195. }
  196. PyObject *CPyDict_Keys(PyObject *dict) {
  197. if (PyDict_CheckExact(dict)) {
  198. return PyDict_Keys(dict);
  199. }
  200. // Inline generic fallback logic to also return a list.
  201. PyObject *list = PyList_New(0);
  202. _Py_IDENTIFIER(keys);
  203. PyObject *view = _PyObject_CallMethodIdNoArgs(dict, &PyId_keys);
  204. if (view == NULL) {
  205. return NULL;
  206. }
  207. PyObject *res = _PyList_Extend((PyListObject *)list, view);
  208. Py_DECREF(view);
  209. if (res == NULL) {
  210. return NULL;
  211. }
  212. Py_DECREF(res);
  213. return list;
  214. }
  215. PyObject *CPyDict_Values(PyObject *dict) {
  216. if (PyDict_CheckExact(dict)) {
  217. return PyDict_Values(dict);
  218. }
  219. // Inline generic fallback logic to also return a list.
  220. PyObject *list = PyList_New(0);
  221. _Py_IDENTIFIER(values);
  222. PyObject *view = _PyObject_CallMethodIdNoArgs(dict, &PyId_values);
  223. if (view == NULL) {
  224. return NULL;
  225. }
  226. PyObject *res = _PyList_Extend((PyListObject *)list, view);
  227. Py_DECREF(view);
  228. if (res == NULL) {
  229. return NULL;
  230. }
  231. Py_DECREF(res);
  232. return list;
  233. }
  234. PyObject *CPyDict_Items(PyObject *dict) {
  235. if (PyDict_CheckExact(dict)) {
  236. return PyDict_Items(dict);
  237. }
  238. // Inline generic fallback logic to also return a list.
  239. PyObject *list = PyList_New(0);
  240. _Py_IDENTIFIER(items);
  241. PyObject *view = _PyObject_CallMethodIdNoArgs(dict, &PyId_items);
  242. if (view == NULL) {
  243. return NULL;
  244. }
  245. PyObject *res = _PyList_Extend((PyListObject *)list, view);
  246. Py_DECREF(view);
  247. if (res == NULL) {
  248. return NULL;
  249. }
  250. Py_DECREF(res);
  251. return list;
  252. }
  253. char CPyDict_Clear(PyObject *dict) {
  254. if (PyDict_CheckExact(dict)) {
  255. PyDict_Clear(dict);
  256. } else {
  257. _Py_IDENTIFIER(clear);
  258. PyObject *res = _PyObject_CallMethodIdNoArgs(dict, &PyId_clear);
  259. if (res == NULL) {
  260. return 0;
  261. }
  262. }
  263. return 1;
  264. }
  265. PyObject *CPyDict_Copy(PyObject *dict) {
  266. if (PyDict_CheckExact(dict)) {
  267. return PyDict_Copy(dict);
  268. }
  269. _Py_IDENTIFIER(copy);
  270. return _PyObject_CallMethodIdNoArgs(dict, &PyId_copy);
  271. }
  272. PyObject *CPyDict_GetKeysIter(PyObject *dict) {
  273. if (PyDict_CheckExact(dict)) {
  274. // Return dict itself to indicate we can use fast path instead.
  275. Py_INCREF(dict);
  276. return dict;
  277. }
  278. return PyObject_GetIter(dict);
  279. }
  280. PyObject *CPyDict_GetItemsIter(PyObject *dict) {
  281. if (PyDict_CheckExact(dict)) {
  282. // Return dict itself to indicate we can use fast path instead.
  283. Py_INCREF(dict);
  284. return dict;
  285. }
  286. _Py_IDENTIFIER(items);
  287. PyObject *view = _PyObject_CallMethodIdNoArgs(dict, &PyId_items);
  288. if (view == NULL) {
  289. return NULL;
  290. }
  291. PyObject *iter = PyObject_GetIter(view);
  292. Py_DECREF(view);
  293. return iter;
  294. }
  295. PyObject *CPyDict_GetValuesIter(PyObject *dict) {
  296. if (PyDict_CheckExact(dict)) {
  297. // Return dict itself to indicate we can use fast path instead.
  298. Py_INCREF(dict);
  299. return dict;
  300. }
  301. _Py_IDENTIFIER(values);
  302. PyObject *view = _PyObject_CallMethodIdNoArgs(dict, &PyId_values);
  303. if (view == NULL) {
  304. return NULL;
  305. }
  306. PyObject *iter = PyObject_GetIter(view);
  307. Py_DECREF(view);
  308. return iter;
  309. }
  310. static void _CPyDict_FromNext(tuple_T3CIO *ret, PyObject *dict_iter) {
  311. // Get next item from iterator and set "should continue" flag.
  312. ret->f2 = PyIter_Next(dict_iter);
  313. if (ret->f2 == NULL) {
  314. ret->f0 = 0;
  315. Py_INCREF(Py_None);
  316. ret->f2 = Py_None;
  317. } else {
  318. ret->f0 = 1;
  319. }
  320. }
  321. // Helpers for fast dictionary iteration, return a single tuple
  322. // instead of writing to multiple registers, for exact dicts use
  323. // the fast path, and fall back to generic iterator logic for subclasses.
  324. tuple_T3CIO CPyDict_NextKey(PyObject *dict_or_iter, CPyTagged offset) {
  325. tuple_T3CIO ret;
  326. Py_ssize_t py_offset = CPyTagged_AsSsize_t(offset);
  327. PyObject *dummy;
  328. if (PyDict_CheckExact(dict_or_iter)) {
  329. ret.f0 = PyDict_Next(dict_or_iter, &py_offset, &ret.f2, &dummy);
  330. if (ret.f0) {
  331. ret.f1 = CPyTagged_FromSsize_t(py_offset);
  332. } else {
  333. // Set key to None, so mypyc can manage refcounts.
  334. ret.f1 = 0;
  335. ret.f2 = Py_None;
  336. }
  337. // PyDict_Next() returns borrowed references.
  338. Py_INCREF(ret.f2);
  339. } else {
  340. // offset is dummy in this case, just use the old value.
  341. ret.f1 = offset;
  342. _CPyDict_FromNext(&ret, dict_or_iter);
  343. }
  344. return ret;
  345. }
  346. tuple_T3CIO CPyDict_NextValue(PyObject *dict_or_iter, CPyTagged offset) {
  347. tuple_T3CIO ret;
  348. Py_ssize_t py_offset = CPyTagged_AsSsize_t(offset);
  349. PyObject *dummy;
  350. if (PyDict_CheckExact(dict_or_iter)) {
  351. ret.f0 = PyDict_Next(dict_or_iter, &py_offset, &dummy, &ret.f2);
  352. if (ret.f0) {
  353. ret.f1 = CPyTagged_FromSsize_t(py_offset);
  354. } else {
  355. // Set value to None, so mypyc can manage refcounts.
  356. ret.f1 = 0;
  357. ret.f2 = Py_None;
  358. }
  359. // PyDict_Next() returns borrowed references.
  360. Py_INCREF(ret.f2);
  361. } else {
  362. // offset is dummy in this case, just use the old value.
  363. ret.f1 = offset;
  364. _CPyDict_FromNext(&ret, dict_or_iter);
  365. }
  366. return ret;
  367. }
  368. tuple_T4CIOO CPyDict_NextItem(PyObject *dict_or_iter, CPyTagged offset) {
  369. tuple_T4CIOO ret;
  370. Py_ssize_t py_offset = CPyTagged_AsSsize_t(offset);
  371. if (PyDict_CheckExact(dict_or_iter)) {
  372. ret.f0 = PyDict_Next(dict_or_iter, &py_offset, &ret.f2, &ret.f3);
  373. if (ret.f0) {
  374. ret.f1 = CPyTagged_FromSsize_t(py_offset);
  375. } else {
  376. // Set key and value to None, so mypyc can manage refcounts.
  377. ret.f1 = 0;
  378. ret.f2 = Py_None;
  379. ret.f3 = Py_None;
  380. }
  381. } else {
  382. ret.f1 = offset;
  383. PyObject *item = PyIter_Next(dict_or_iter);
  384. if (item == NULL || !PyTuple_Check(item) || PyTuple_GET_SIZE(item) != 2) {
  385. if (item != NULL) {
  386. PyErr_SetString(PyExc_TypeError, "a tuple of length 2 expected");
  387. }
  388. ret.f0 = 0;
  389. ret.f2 = Py_None;
  390. ret.f3 = Py_None;
  391. } else {
  392. ret.f0 = 1;
  393. ret.f2 = PyTuple_GET_ITEM(item, 0);
  394. ret.f3 = PyTuple_GET_ITEM(item, 1);
  395. Py_DECREF(item);
  396. }
  397. }
  398. // PyDict_Next() returns borrowed references.
  399. Py_INCREF(ret.f2);
  400. Py_INCREF(ret.f3);
  401. return ret;
  402. }
  403. int CPyMapping_Check(PyObject *obj) {
  404. return Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MAPPING;
  405. }