this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2// dictobject.c implementation
3
4#include "cpython-func.h"
5
6#include "api-handle.h"
7#include "dict-builtins.h"
8#include "handles.h"
9#include "int-builtins.h"
10#include "object-builtins.h"
11#include "objects.h"
12#include "runtime.h"
13#include "str-builtins.h"
14
15namespace py {
16
17PY_EXPORT PyTypeObject* PyDictItems_Type_Ptr() {
18 Runtime* runtime = Thread::current()->runtime();
19 return reinterpret_cast<PyTypeObject*>(ApiHandle::borrowedReference(
20 runtime, runtime->typeAt(LayoutId::kDictItems)));
21}
22
23PY_EXPORT PyTypeObject* PyDictIterItem_Type_Ptr() {
24 Runtime* runtime = Thread::current()->runtime();
25 return reinterpret_cast<PyTypeObject*>(ApiHandle::borrowedReference(
26 runtime, runtime->typeAt(LayoutId::kDictItemIterator)));
27}
28
29PY_EXPORT PyTypeObject* PyDictIterKey_Type_Ptr() {
30 Runtime* runtime = Thread::current()->runtime();
31 return reinterpret_cast<PyTypeObject*>(ApiHandle::borrowedReference(
32 runtime, runtime->typeAt(LayoutId::kDictKeyIterator)));
33}
34
35PY_EXPORT PyTypeObject* PyDictIterValue_Type_Ptr() {
36 Runtime* runtime = Thread::current()->runtime();
37 return reinterpret_cast<PyTypeObject*>(ApiHandle::borrowedReference(
38 runtime, runtime->typeAt(LayoutId::kDictValueIterator)));
39}
40
41PY_EXPORT PyTypeObject* PyDictKeys_Type_Ptr() {
42 Runtime* runtime = Thread::current()->runtime();
43 return reinterpret_cast<PyTypeObject*>(ApiHandle::borrowedReference(
44 runtime, runtime->typeAt(LayoutId::kDictKeys)));
45}
46
47PY_EXPORT PyTypeObject* PyDictValues_Type_Ptr() {
48 Runtime* runtime = Thread::current()->runtime();
49 return reinterpret_cast<PyTypeObject*>(ApiHandle::borrowedReference(
50 runtime, runtime->typeAt(LayoutId::kDictValues)));
51}
52
53PY_EXPORT int PyDict_CheckExact_Func(PyObject* obj) {
54 return ApiHandle::asObject(ApiHandle::fromPyObject(obj)).isDict();
55}
56
57PY_EXPORT int PyDict_Check_Func(PyObject* obj) {
58 return Thread::current()->runtime()->isInstanceOfDict(
59 ApiHandle::asObject(ApiHandle::fromPyObject(obj)));
60}
61
62PY_EXPORT Py_ssize_t PyDict_GET_SIZE_Func(PyObject* dict) {
63 HandleScope scope(Thread::current());
64 Dict dict_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(dict)));
65 return dict_obj.numItems();
66}
67
68PY_EXPORT int _PyDict_SetItem_KnownHash(PyObject* pydict, PyObject* key,
69 PyObject* value, Py_hash_t pyhash) {
70 Thread* thread = Thread::current();
71 HandleScope scope(thread);
72 Object dict_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(pydict)));
73 Runtime* runtime = thread->runtime();
74 if (!runtime->isInstanceOfDict(*dict_obj)) {
75 thread->raiseBadInternalCall();
76 return -1;
77 }
78 Dict dict(&scope, *dict_obj);
79 Object key_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(key)));
80 Object value_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(value)));
81 word hash = SmallInt::truncate(pyhash);
82 if (dictAtPut(thread, dict, key_obj, hash, value_obj).isErrorException()) {
83 return -1;
84 }
85 return 0;
86}
87
88PY_EXPORT int PyDict_SetItem(PyObject* pydict, PyObject* key, PyObject* value) {
89 Thread* thread = Thread::current();
90 HandleScope scope(thread);
91 Object dict_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(pydict)));
92 if (!thread->runtime()->isInstanceOfDict(*dict_obj)) {
93 thread->raiseBadInternalCall();
94 return -1;
95 }
96 Dict dict(&scope, *dict_obj);
97 Object key_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(key)));
98 Object value_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(value)));
99 Object hash_obj(&scope, Interpreter::hash(thread, key_obj));
100 if (hash_obj.isError()) return -1;
101 word hash = SmallInt::cast(*hash_obj).value();
102 if (dictAtPut(thread, dict, key_obj, hash, value_obj).isErrorException()) {
103 return -1;
104 }
105 return 0;
106}
107
108PY_EXPORT int PyDict_SetItemString(PyObject* pydict, const char* key,
109 PyObject* value) {
110 Thread* thread = Thread::current();
111 HandleScope scope(thread);
112 Object dict_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(pydict)));
113 Runtime* runtime = thread->runtime();
114 if (!runtime->isInstanceOfDict(*dict_obj)) {
115 thread->raiseBadInternalCall();
116 return -1;
117 }
118 Dict dict(&scope, *dict_obj);
119 Str key_obj(&scope, Runtime::internStrFromCStr(thread, key));
120 Object value_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(value)));
121 word hash = strHash(thread, *key_obj);
122 if (dictAtPut(thread, dict, key_obj, hash, value_obj).isErrorException()) {
123 return -1;
124 }
125 return 0;
126}
127
128PY_EXPORT PyTypeObject* PyDict_Type_Ptr() {
129 Runtime* runtime = Thread::current()->runtime();
130 return reinterpret_cast<PyTypeObject*>(
131 ApiHandle::borrowedReference(runtime, runtime->typeAt(LayoutId::kDict)));
132}
133
134PY_EXPORT PyObject* PyDict_New() {
135 Runtime* runtime = Thread::current()->runtime();
136 return ApiHandle::newReferenceWithManaged(runtime, runtime->newDict());
137}
138
139static PyObject* getItem(Thread* thread, const Object& dict_obj,
140 const Object& key) {
141 HandleScope scope(thread);
142 // For historical reasons, PyDict_GetItem supresses all errors that may occur.
143 Runtime* runtime = thread->runtime();
144 if (!runtime->isInstanceOfDict(*dict_obj)) {
145 return nullptr;
146 }
147 Dict dict(&scope, *dict_obj);
148 Object hash_obj(&scope, Interpreter::hash(thread, key));
149 if (hash_obj.isError()) {
150 thread->clearPendingException();
151 return nullptr;
152 }
153 word hash = SmallInt::cast(*hash_obj).value();
154 Object result(&scope, dictAt(thread, dict, key, hash));
155 if (result.isErrorException()) {
156 thread->clearPendingException();
157 return nullptr;
158 }
159 if (result.isErrorNotFound()) {
160 return nullptr;
161 }
162 return ApiHandle::borrowedReference(runtime, *result);
163}
164
165PY_EXPORT PyObject* _PyDict_GetItem_KnownHash(PyObject* pydict, PyObject* key,
166 Py_hash_t pyhash) {
167 Thread* thread = Thread::current();
168 HandleScope scope(thread);
169 Object dictobj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(pydict)));
170 Runtime* runtime = thread->runtime();
171 if (!runtime->isInstanceOfDict(*dictobj)) {
172 thread->raiseBadInternalCall();
173 return nullptr;
174 }
175 Dict dict(&scope, *dictobj);
176 Object key_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(key)));
177 word hash = SmallInt::truncate(pyhash);
178 Object value(&scope, dictAt(thread, dict, key_obj, hash));
179 if (value.isError()) return nullptr;
180 return ApiHandle::borrowedReference(runtime, *value);
181}
182
183PY_EXPORT PyObject* PyDict_GetItem(PyObject* pydict, PyObject* key) {
184 Thread* thread = Thread::current();
185 HandleScope scope(thread);
186 Object dict(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(pydict)));
187 Object key_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(key)));
188 return getItem(thread, dict, key_obj);
189}
190
191PY_EXPORT PyObject* PyDict_GetItemString(PyObject* pydict, const char* key) {
192 Thread* thread = Thread::current();
193 HandleScope scope(thread);
194 Object dict(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(pydict)));
195 Object key_obj(&scope, thread->runtime()->newStrFromCStr(key));
196 return getItem(thread, dict, key_obj);
197}
198
199PY_EXPORT void PyDict_Clear(PyObject* pydict) {
200 Thread* thread = Thread::current();
201 HandleScope scope(thread);
202 Runtime* runtime = thread->runtime();
203 Object dict_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(pydict)));
204 if (!runtime->isInstanceOfDict(*dict_obj)) {
205 return;
206 }
207 Dict dict(&scope, *dict_obj);
208 dict.setNumItems(0);
209 dict.setData(runtime->emptyTuple());
210}
211
212PY_EXPORT int PyDict_Contains(PyObject* pydict, PyObject* key) {
213 Thread* thread = Thread::current();
214 HandleScope scope(thread);
215 Dict dict(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(pydict)));
216 Object key_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(key)));
217 Object hash_obj(&scope, Interpreter::hash(thread, key_obj));
218 if (hash_obj.isErrorException()) return -1;
219 word hash = SmallInt::cast(*hash_obj).value();
220 Object result(&scope, dictIncludes(thread, dict, key_obj, hash));
221 if (result.isErrorException()) return -1;
222 return Bool::cast(*result).value();
223}
224
225PY_EXPORT PyObject* PyDict_Copy(PyObject* pydict) {
226 Thread* thread = Thread::current();
227 if (pydict == nullptr) {
228 thread->raiseBadInternalCall();
229 return nullptr;
230 }
231 HandleScope scope(thread);
232 Object dict_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(pydict)));
233 Runtime* runtime = thread->runtime();
234 if (!runtime->isInstanceOfDict(*dict_obj)) {
235 thread->raiseBadInternalCall();
236 return nullptr;
237 }
238 Dict dict(&scope, *dict_obj);
239 return ApiHandle::newReferenceWithManaged(runtime, dictCopy(thread, dict));
240}
241
242PY_EXPORT int PyDict_DelItem(PyObject* pydict, PyObject* key) {
243 Thread* thread = Thread::current();
244 HandleScope scope(thread);
245 Object dict_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(pydict)));
246 Runtime* runtime = thread->runtime();
247 if (!runtime->isInstanceOfDict(*dict_obj)) {
248 thread->raiseBadInternalCall();
249 return -1;
250 }
251 Dict dict(&scope, *dict_obj);
252 Object key_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(key)));
253 Object hash_obj(&scope, Interpreter::hash(thread, key_obj));
254 if (hash_obj.isErrorException()) return -1;
255 word hash = SmallInt::cast(*hash_obj).value();
256 if (dictRemove(thread, dict, key_obj, hash).isError()) {
257 thread->raise(LayoutId::kKeyError, *key_obj);
258 return -1;
259 }
260 return 0;
261}
262
263PY_EXPORT int PyDict_DelItemString(PyObject* pydict, const char* key) {
264 PyObject* str = PyUnicode_FromString(key);
265 if (str == nullptr) return -1;
266 int result = PyDict_DelItem(pydict, str);
267 Py_DECREF(str);
268 return result;
269}
270
271PY_EXPORT PyObject* PyDict_GetItemWithError(PyObject* pydict, PyObject* key) {
272 Thread* thread = Thread::current();
273 HandleScope scope(thread);
274 Object dict_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(pydict)));
275 Runtime* runtime = thread->runtime();
276 if (!runtime->isInstanceOfDict(*dict_obj)) {
277 thread->raiseBadInternalCall();
278 return nullptr;
279 }
280
281 Object key_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(key)));
282 Object hash_obj(&scope, Interpreter::hash(thread, key_obj));
283 if (hash_obj.isErrorException()) return nullptr;
284 word hash = SmallInt::cast(*hash_obj).value();
285 Dict dict(&scope, *dict_obj);
286 Object value(&scope, dictAt(thread, dict, key_obj, hash));
287 if (value.isError()) {
288 return nullptr;
289 }
290 return ApiHandle::borrowedReference(runtime, *value);
291}
292
293PY_EXPORT PyObject* PyDict_Items(PyObject* pydict) {
294 Thread* thread = Thread::current();
295 HandleScope scope(thread);
296 Object dict_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(pydict)));
297 Runtime* runtime = thread->runtime();
298 if (!runtime->isInstanceOfDict(*dict_obj)) {
299 thread->raiseBadInternalCall();
300 return nullptr;
301 }
302 Dict dict(&scope, *dict_obj);
303 word len = dict.numItems();
304 if (len == 0) {
305 return ApiHandle::newReferenceWithManaged(runtime, runtime->newList());
306 }
307
308 List result(&scope, runtime->newList());
309 MutableTuple items(&scope, runtime->newMutableTuple(len));
310 Object key(&scope, NoneType::object());
311 Object value(&scope, NoneType::object());
312 for (word i = 0, j = 0; dictNextItem(dict, &i, &key, &value);) {
313 items.atPut(j++, runtime->newTupleWith2(key, value));
314 }
315 result.setItems(*items);
316 result.setNumItems(len);
317 return ApiHandle::newReferenceWithManaged(runtime, *result);
318}
319
320PY_EXPORT PyObject* PyDict_Keys(PyObject* pydict) {
321 Thread* thread = Thread::current();
322 HandleScope scope(thread);
323 Object dict_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(pydict)));
324 Runtime* runtime = thread->runtime();
325 if (!runtime->isInstanceOfDict(*dict_obj)) {
326 thread->raiseBadInternalCall();
327 return nullptr;
328 }
329 Dict dict(&scope, *dict_obj);
330 return ApiHandle::newReferenceWithManaged(runtime, dictKeys(thread, dict));
331}
332
333PY_EXPORT int PyDict_Merge(PyObject* left, PyObject* right,
334 int override_matching) {
335 CHECK_BOUND(override_matching, 2);
336 Thread* thread = Thread::current();
337 if (left == nullptr || right == nullptr) {
338 thread->raiseBadInternalCall();
339 return -1;
340 }
341 HandleScope scope(thread);
342 Object left_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(left)));
343 if (!thread->runtime()->isInstanceOfDict(*left_obj)) {
344 thread->raiseBadInternalCall();
345 return -1;
346 }
347 Dict left_dict(&scope, *left_obj);
348 Object right_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(right)));
349 auto merge_func = override_matching ? dictMergeOverride : dictMergeIgnore;
350 if ((*merge_func)(thread, left_dict, right_obj).isError()) {
351 return -1;
352 }
353 return 0;
354}
355
356PY_EXPORT int PyDict_MergeFromSeq2(PyObject* /* d */, PyObject* /* 2 */,
357 int /* e */) {
358 UNIMPLEMENTED("PyDict_MergeFromSeq2");
359}
360
361PY_EXPORT int _PyDict_Next(PyObject* dict, Py_ssize_t* ppos, PyObject** pkey,
362 PyObject** pvalue, Py_hash_t* phash) {
363 Thread* thread = Thread::current();
364 HandleScope scope(thread);
365 Object dict_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(dict)));
366 Runtime* runtime = thread->runtime();
367 if (!runtime->isInstanceOfDict(*dict_obj)) {
368 return 0;
369 }
370 Dict dict_dict(&scope, *dict_obj);
371 // Below are all the possible statuses of ppos and what to do in each case.
372 // * If an index is out of bounds, we should not advance.
373 // * If an index does not point to a valid bucket, we should try and find the
374 // next bucket, or fail.
375 // * Read the contents of that bucket.
376 // * Advance the index.
377 Object key(&scope, NoneType::object());
378 Object value(&scope, NoneType::object());
379 word hash;
380 if (!dictNextItemHash(dict_dict, ppos, &key, &value, &hash)) {
381 return 0;
382 }
383 // At this point, we will always have a valid bucket index.
384 if (pkey != nullptr) *pkey = ApiHandle::borrowedReference(runtime, *key);
385 if (pvalue != nullptr) {
386 *pvalue = ApiHandle::borrowedReference(runtime, *value);
387 }
388 if (phash != nullptr) *phash = hash;
389 return true;
390}
391
392PY_EXPORT int PyDict_Next(PyObject* dict, Py_ssize_t* ppos, PyObject** pkey,
393 PyObject** pvalue) {
394 return _PyDict_Next(dict, ppos, pkey, pvalue, nullptr);
395}
396
397PY_EXPORT Py_ssize_t PyDict_Size(PyObject* p) {
398 Thread* thread = Thread::current();
399 Runtime* runtime = thread->runtime();
400 HandleScope scope(thread);
401
402 Object dict_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(p)));
403 if (!runtime->isInstanceOfDict(*dict_obj)) {
404 thread->raiseBadInternalCall();
405 return -1;
406 }
407
408 Dict dict(&scope, *dict_obj);
409 return dict.numItems();
410}
411
412PY_EXPORT int PyDict_Update(PyObject* left, PyObject* right) {
413 return PyDict_Merge(left, right, 1);
414}
415
416PY_EXPORT PyObject* PyDict_Values(PyObject* pydict) {
417 Thread* thread = Thread::current();
418 HandleScope scope(thread);
419 Object dict_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(pydict)));
420 Runtime* runtime = thread->runtime();
421 if (!runtime->isInstanceOfDict(*dict_obj)) {
422 thread->raiseBadInternalCall();
423 return nullptr;
424 }
425 Dict dict(&scope, *dict_obj);
426 word len = dict.numItems();
427 if (len == 0) {
428 return ApiHandle::newReferenceWithManaged(runtime, runtime->newList());
429 }
430
431 List result(&scope, runtime->newList());
432 MutableTuple values(&scope, runtime->newMutableTuple(len));
433 Object value(&scope, NoneType::object());
434 for (word i = 0, j = 0; dictNextValue(dict, &i, &value);) {
435 values.atPut(j++, *value);
436 }
437 result.setItems(*values);
438 result.setNumItems(len);
439 return ApiHandle::newReferenceWithManaged(runtime, *result);
440}
441
442PY_EXPORT PyObject* PyObject_GenericGetDict(PyObject* obj, void*) {
443 Thread* thread = Thread::current();
444 HandleScope scope(thread);
445 Object object(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(obj)));
446 Runtime* runtime = thread->runtime();
447 Object name(&scope, runtime->symbols()->at(ID(__dict__)));
448 Object dict(&scope, objectGetAttribute(thread, object, name));
449 if (dict.isError()) {
450 thread->raiseWithFmt(LayoutId::kAttributeError,
451 "This object has no __dict__");
452 return nullptr;
453 }
454 return ApiHandle::newReference(runtime, *dict);
455}
456
457} // namespace py