Nuitka
The Python compiler
Loading...
Searching...
No Matches
HelpersAttributes.c
1// Copyright 2025, Kay Hayen, mailto:kay.hayen@gmail.com find license text at end of file
2
3// This file is included from another C file, help IDEs to still parse it on
4// its own.
5#ifdef __IDE_ONLY__
6#include "nuitka/prelude.h"
7#endif
8
9#if PYTHON_VERSION < 0x300
10PyObject *FIND_ATTRIBUTE_IN_CLASS(PyClassObject *class_object, PyObject *attr_name) {
11 CHECK_OBJECT(class_object);
12 CHECK_OBJECT(attr_name);
13
14 assert(PyClass_Check(class_object));
15 assert(PyString_CheckExact(attr_name));
16
17 PyObject *result = GET_STRING_DICT_VALUE((PyDictObject *)class_object->cl_dict, (PyStringObject *)attr_name);
18
19 if (result == NULL) {
20 assert(PyTuple_Check(class_object->cl_bases));
21
22 Py_ssize_t base_count = PyTuple_GET_SIZE(class_object->cl_bases);
23
24 for (Py_ssize_t i = 0; i < base_count; i++) {
25 result = FIND_ATTRIBUTE_IN_CLASS((PyClassObject *)PyTuple_GET_ITEM(class_object->cl_bases, i), attr_name);
26
27 if (result != NULL) {
28 break;
29 }
30 }
31 }
32
33 return result;
34}
35#endif
36
37#if PYTHON_VERSION < 0x300
38static PyObject *LOOKUP_INSTANCE(PyThreadState *tstate, PyObject *source, PyObject *attr_name) {
39 CHECK_OBJECT(source);
40 CHECK_OBJECT(attr_name);
41
42 assert(PyInstance_Check(source));
43 assert(PyString_CheckExact(attr_name));
44
45 PyInstanceObject *source_instance = (PyInstanceObject *)source;
46
47 // The special cases have their own variant on the code generation level
48 // as we are called with constants only.
49 assert(attr_name != const_str_plain___dict__);
50 assert(attr_name != const_str_plain___class__);
51
52 // Try the instance dict first.
53 PyObject *result = GET_STRING_DICT_VALUE((PyDictObject *)source_instance->in_dict, (PyStringObject *)attr_name);
54
55 if (result) {
56 Py_INCREF(result);
57 return result;
58 }
59
60 // Next see if a class has it
61 result = FIND_ATTRIBUTE_IN_CLASS(source_instance->in_class, attr_name);
62
63 if (result != NULL) {
64 descrgetfunc func = Py_TYPE(result)->tp_descr_get;
65
66 if (func) {
67 result = func(result, source, (PyObject *)source_instance->in_class);
68
69 if (unlikely(result == NULL)) {
70 return NULL;
71 }
72
73 CHECK_OBJECT(result);
74
75 return result;
76 } else {
77 Py_INCREF(result);
78 return result;
79 }
80 }
81
82 // Finally allow a __getattr__ to handle it or else it's an error.
83 if (unlikely(source_instance->in_class->cl_getattr == NULL)) {
84 PyErr_Format(PyExc_AttributeError, "%s instance has no attribute '%s'",
85 PyString_AS_STRING(source_instance->in_class->cl_name), PyString_AS_STRING(attr_name));
86
87 return NULL;
88 } else {
89 PyObject *args[] = {source, attr_name};
90 return CALL_FUNCTION_WITH_ARGS2(tstate, source_instance->in_class->cl_getattr, args);
91 }
92}
93#endif
94
95PyObject *LOOKUP_ATTRIBUTE(PyThreadState *tstate, PyObject *source, PyObject *attr_name) {
96 /* Note: There are 2 specializations of this function, that need to be
97 * updated in line with this: LOOKUP_ATTRIBUTE_[DICT|CLASS]_SLOT
98 */
99
100#if _NUITKA_EXPERIMENTAL_DISABLE_ATTR_OPT
101 return PyObject_GetAttr(source, attr_name);
102#else
103 CHECK_OBJECT(source);
104 CHECK_OBJECT(attr_name);
105
106 PyTypeObject *type = Py_TYPE(source);
107
108 if (hasTypeGenericGetAttr(type)) {
109 // Unfortunately this is required, although of cause rarely necessary.
110 if (unlikely(type->tp_dict == NULL)) {
111 if (unlikely(PyType_Ready(type) < 0)) {
112 return NULL;
113 }
114 }
115
116 PyObject *descr = Nuitka_TypeLookup(type, attr_name);
117 descrgetfunc func = NULL;
118
119 if (descr != NULL) {
120 Py_INCREF(descr);
121
122 if (NuitkaType_HasFeatureClass(Py_TYPE(descr))) {
123 func = Py_TYPE(descr)->tp_descr_get;
124
125 if (func != NULL && Nuitka_Descr_IsData(descr)) {
126 PyObject *result = func(descr, source, (PyObject *)type);
127 Py_DECREF(descr);
128
129 return result;
130 }
131 }
132 }
133
134 PyObject *dict = NULL;
135
136// TODO: This is what Python 3.11 requires us to add.
137#if PYTHON_VERSION >= 0x3b0 && 0
138 if ((type->tp_flags & Py_TPFLAGS_MANAGED_DICT) && *_PyObject_ValuesPointer(source)) {
139 PyObject **dict_pointer;
140
141 PyDictValues **values_ptr = _PyObject_ValuesPointer(source);
142
143 // TODO: Seems we would benefit by knowing this on the outside.
144 if (PyUnicode_CheckExact(attr_name)) {
145 assert(*_PyObject_DictPointer(source) == NULL);
146 PyObject *result = _PyObject_GetInstanceAttribute(source, *values_ptr, attr_name);
147 if (result != NULL) {
148 return result;
149 }
150 } else {
151 dict_pointer = _PyObject_DictPointer(source);
152 assert(dict_pointer != NULL && *dict_pointer == NULL);
153
154 *dict_pointer = dict = _PyObject_MakeDictFromInstanceAttributes(source, *values_ptr);
155 if (unlikely(dict == NULL)) {
156 return NULL;
157 }
158 *values_ptr = NULL;
159 }
160 }
161#endif
162
163 if (dict == NULL) {
164 Py_ssize_t dict_offset = type->tp_dictoffset;
165
166 if (dict_offset != 0) {
167
168 // Negative dictionary offsets have special meaning.
169 if (dict_offset < 0) {
170 Py_ssize_t tsize;
171 size_t size;
172
173 tsize = ((PyVarObject *)source)->ob_size;
174 if (tsize < 0) {
175 tsize = -tsize;
176 }
177 size = _PyObject_VAR_SIZE(type, tsize);
178
179 dict_offset += (long)size;
180 }
181
182 PyObject **dict_pointer = (PyObject **)((char *)source + dict_offset);
183 dict = *dict_pointer;
184 }
185 }
186
187 if (dict != NULL) {
188 CHECK_OBJECT(dict);
189
190 // TODO: If this is an exact dict, we don't have to hold a reference, is it?
191 Py_INCREF(dict);
192
193 PyObject *result = DICT_GET_ITEM1(tstate, dict, attr_name);
194
195 Py_DECREF(dict);
196
197 if (result != NULL) {
198 Py_XDECREF(descr);
199
200 CHECK_OBJECT(result);
201 return result;
202 }
203 }
204
205 if (func != NULL) {
206 PyObject *result = func(descr, source, (PyObject *)type);
207 Py_DECREF(descr);
208
209 CHECK_OBJECT_X(result);
210 return result;
211 }
212
213 if (descr != NULL) {
214 CHECK_OBJECT(descr);
215 return descr;
216 }
217
218#if PYTHON_VERSION < 0x300
219 PyErr_Format(PyExc_AttributeError, "'%s' object has no attribute '%s'", type->tp_name,
220 PyString_AS_STRING(attr_name));
221#else
222 PyErr_Format(PyExc_AttributeError, "'%s' object has no attribute '%U'", type->tp_name, attr_name);
223#endif
224 return NULL;
225 }
226#if PYTHON_VERSION < 0x300
227 else if (type->tp_getattro == PyInstance_Type.tp_getattro && Nuitka_String_CheckExact(attr_name)) {
228 PyObject *result = LOOKUP_INSTANCE(tstate, source, attr_name);
229 return result;
230 }
231#endif
232 else if (type->tp_getattro != NULL) {
233 PyObject *result = (*type->tp_getattro)(source, attr_name);
234
235 if (unlikely(result == NULL)) {
236 return NULL;
237 }
238
239 CHECK_OBJECT(result);
240 return result;
241 } else if (type->tp_getattr != NULL) {
242 PyObject *result = (*type->tp_getattr)(source, (char *)Nuitka_String_AsString_Unchecked(attr_name));
243 return result;
244 } else {
245 PyErr_Format(PyExc_AttributeError, "'%s' object has no attribute '%s'", type->tp_name,
246 Nuitka_String_AsString_Unchecked(attr_name));
247
248 return NULL;
249 }
250#endif
251}
252
253PyObject *LOOKUP_ATTRIBUTE_DICT_SLOT(PyThreadState *tstate, PyObject *source) {
254 CHECK_OBJECT(source);
255
256#if _NUITKA_EXPERIMENTAL_DISABLE_ATTR_OPT
257 return PyObject_GetAttr(source, const_str_plain___dict__);
258#else
259 PyTypeObject *type = Py_TYPE(source);
260
261 if (hasTypeGenericGetAttr(type)) {
262 if (unlikely(type->tp_dict == NULL)) {
263 if (unlikely(PyType_Ready(type) < 0)) {
264 return NULL;
265 }
266 }
267
268 PyObject *descr = Nuitka_TypeLookup(type, const_str_plain___dict__);
269 descrgetfunc func = NULL;
270
271 if (descr != NULL) {
272 Py_INCREF(descr);
273
274 if (NuitkaType_HasFeatureClass(Py_TYPE(descr))) {
275 func = Py_TYPE(descr)->tp_descr_get;
276
277 if (func != NULL && Nuitka_Descr_IsData(descr)) {
278 PyObject *result = func(descr, source, (PyObject *)type);
279 Py_DECREF(descr);
280
281 return result;
282 }
283 }
284 }
285
286 Py_ssize_t dict_offset = type->tp_dictoffset;
287 PyObject *dict = NULL;
288
289 if (dict_offset != 0) {
290 // Negative dictionary offsets have special meaning.
291 if (dict_offset < 0) {
292 Py_ssize_t tsize;
293 size_t size;
294
295 tsize = ((PyVarObject *)source)->ob_size;
296 if (tsize < 0)
297 tsize = -tsize;
298 size = _PyObject_VAR_SIZE(type, tsize);
299
300 dict_offset += (long)size;
301 }
302
303 PyObject **dict_pointer = (PyObject **)((char *)source + dict_offset);
304 dict = *dict_pointer;
305 }
306
307 if (dict != NULL) {
308 CHECK_OBJECT(dict);
309
310 Py_INCREF(dict);
311
312 PyObject *result = DICT_GET_ITEM1(tstate, dict, const_str_plain___dict__);
313
314 if (result != NULL) {
315 Py_XDECREF(descr);
316 Py_DECREF(dict);
317
318 CHECK_OBJECT(result);
319 return result;
320 }
321
322 Py_DECREF(dict);
323 }
324
325 if (func != NULL) {
326 PyObject *result = func(descr, source, (PyObject *)type);
327 Py_DECREF(descr);
328
329 CHECK_OBJECT_X(result);
330 return result;
331 }
332
333 if (descr != NULL) {
334 CHECK_OBJECT(descr);
335 return descr;
336 }
337
338 PyErr_Format(PyExc_AttributeError, "'%s' object has no attribute '__dict__'", type->tp_name);
339 return NULL;
340 }
341#if PYTHON_VERSION < 0x300
342 else if (type->tp_getattro == PyInstance_Type.tp_getattro) {
343 PyInstanceObject *source_instance = (PyInstanceObject *)source;
344 PyObject *result = source_instance->in_dict;
345
346 Py_INCREF(result);
347 return result;
348 }
349#endif
350 else if (type->tp_getattro != NULL) {
351 PyObject *result = (*type->tp_getattro)(source, const_str_plain___dict__);
352
353 if (unlikely(result == NULL)) {
354 return NULL;
355 }
356
357 CHECK_OBJECT(result);
358 return result;
359 } else if (type->tp_getattr != NULL) {
360 PyObject *result = (*type->tp_getattr)(source, (char *)"__dict__");
361 return result;
362 } else {
363 PyErr_Format(PyExc_AttributeError, "'%s' object has no attribute '__dict__'", type->tp_name);
364
365 return NULL;
366 }
367#endif
368}
369
370PyObject *LOOKUP_ATTRIBUTE_CLASS_SLOT(PyThreadState *tstate, PyObject *source) {
371 CHECK_OBJECT(source);
372
373#if _NUITKA_EXPERIMENTAL_DISABLE_ATTR_OPT
374 return PyObject_GetAttr(source, const_str_plain___class__);
375#else
376 PyTypeObject *type = Py_TYPE(source);
377
378 if (hasTypeGenericGetAttr(type)) {
379 if (unlikely(type->tp_dict == NULL)) {
380 if (unlikely(PyType_Ready(type) < 0)) {
381 return NULL;
382 }
383 }
384
385 PyObject *descr = Nuitka_TypeLookup(type, const_str_plain___class__);
386 descrgetfunc func = NULL;
387
388 if (descr != NULL) {
389 Py_INCREF(descr);
390
391 if (NuitkaType_HasFeatureClass(Py_TYPE(descr))) {
392 func = Py_TYPE(descr)->tp_descr_get;
393
394 if (func != NULL && Nuitka_Descr_IsData(descr)) {
395 PyObject *result = func(descr, source, (PyObject *)type);
396 Py_DECREF(descr);
397
398 return result;
399 }
400 }
401 }
402
403 Py_ssize_t dict_offset = type->tp_dictoffset;
404 PyObject *dict = NULL;
405
406 if (dict_offset != 0) {
407 // Negative dictionary offsets have special meaning.
408 if (dict_offset < 0) {
409 Py_ssize_t tsize;
410 size_t size;
411
412 tsize = ((PyVarObject *)source)->ob_size;
413 if (tsize < 0) {
414 tsize = -tsize;
415 }
416 size = _PyObject_VAR_SIZE(type, tsize);
417
418 dict_offset += (long)size;
419 }
420
421 PyObject **dict_pointer = (PyObject **)((char *)source + dict_offset);
422 dict = *dict_pointer;
423 }
424
425 if (dict != NULL) {
426 CHECK_OBJECT(dict);
427
428 Py_INCREF(dict);
429
430 PyObject *result = DICT_GET_ITEM1(tstate, dict, const_str_plain___class__);
431
432 if (result != NULL) {
433 Py_XDECREF(descr);
434 Py_DECREF(dict);
435
436 CHECK_OBJECT(result);
437 return result;
438 }
439
440 Py_DECREF(dict);
441 }
442
443 if (func != NULL) {
444 PyObject *result = func(descr, source, (PyObject *)type);
445 Py_DECREF(descr);
446
447 CHECK_OBJECT_X(result);
448 return result;
449 }
450
451 if (descr != NULL) {
452 CHECK_OBJECT(descr);
453 return descr;
454 }
455
456 PyErr_Format(PyExc_AttributeError, "'%s' object has no attribute '__class__'", type->tp_name);
457 return NULL;
458 }
459#if PYTHON_VERSION < 0x300
460 else if (type->tp_getattro == PyInstance_Type.tp_getattro) {
461 PyInstanceObject *source_instance = (PyInstanceObject *)source;
462 PyObject *result = (PyObject *)source_instance->in_class;
463
464 Py_INCREF(result);
465 return result;
466 }
467#endif
468 else if (type->tp_getattro != NULL) {
469 PyObject *result = (*type->tp_getattro)(source, const_str_plain___class__);
470
471 if (unlikely(result == NULL)) {
472 return NULL;
473 }
474
475 CHECK_OBJECT(result);
476 return result;
477 } else if (type->tp_getattr != NULL) {
478 PyObject *result = (*type->tp_getattr)(source, (char *)"__class__");
479 return result;
480 } else {
481 PyErr_Format(PyExc_AttributeError, "'%s' object has no attribute '__class__'", type->tp_name);
482
483 return NULL;
484 }
485#endif
486}
487
488int BUILTIN_HASATTR_BOOL(PyThreadState *tstate, PyObject *source, PyObject *attr_name) {
489 CHECK_OBJECT(source);
490 CHECK_OBJECT(attr_name);
491
492#if PYTHON_VERSION < 0x300
493 if (PyUnicode_Check(attr_name)) {
494 attr_name = _PyUnicode_AsDefaultEncodedString(attr_name, NULL);
495
496 if (unlikely(attr_name == NULL)) {
497 return -1;
498 }
499 }
500
501 if (unlikely(!PyString_Check(attr_name))) {
502 SET_CURRENT_EXCEPTION_TYPE0_STR(tstate, PyExc_TypeError, "hasattr(): attribute name must be string");
503
504 return -1;
505 }
506#else
507 if (unlikely(!PyUnicode_Check(attr_name))) {
508 SET_CURRENT_EXCEPTION_TYPE0_STR(tstate, PyExc_TypeError, "hasattr(): attribute name must be string");
509
510 return -1;
511 }
512#endif
513
514 // TODO: This should use what LOOKUP_ATTRIBUTE does and know that the result value is going to
515 // be unused, having an easier time generally, e.g. not having to create the error in the first
516 // place.
517 PyObject *value = PyObject_GetAttr(source, attr_name);
518
519 if (value == NULL) {
520 if (CHECK_AND_CLEAR_ATTRIBUTE_ERROR_OCCURRED(tstate) == false) {
521 return -1;
522 }
523 return 0;
524 }
525
526 Py_DECREF(value);
527
528 return 1;
529}
530
531bool HAS_ATTR_BOOL(PyThreadState *tstate, PyObject *source, PyObject *attr_name) {
532 CHECK_OBJECT(source);
533 CHECK_OBJECT(attr_name);
534
535#if _NUITKA_EXPERIMENTAL_DISABLE_ATTR_OPT
536 return PyObject_HasAttr(source, attr_name);
537#else
538 PyTypeObject *type = Py_TYPE(source);
539
540 if (hasTypeGenericGetAttr(type)) {
541 // Unfortunately this is required, although of cause rarely necessary.
542 if (unlikely(type->tp_dict == NULL)) {
543 if (unlikely(PyType_Ready(type) < 0)) {
544 CLEAR_ERROR_OCCURRED(tstate);
545
546 return false;
547 }
548 }
549
550 PyObject *descr = Nuitka_TypeLookup(type, attr_name);
551 descrgetfunc func = NULL;
552
553 if (descr != NULL) {
554 Py_INCREF(descr);
555
556 if (NuitkaType_HasFeatureClass(Py_TYPE(descr))) {
557 func = Py_TYPE(descr)->tp_descr_get;
558
559 if (func != NULL && Nuitka_Descr_IsData(descr)) {
560 PyObject *result = func(descr, source, (PyObject *)type);
561 Py_DECREF(descr);
562
563 if (result) {
564 CHECK_OBJECT(result);
565
566 Py_DECREF(result);
567 return true;
568 }
569
570 DROP_ERROR_OCCURRED(tstate);
571 return false;
572 }
573 }
574 }
575
576 Py_ssize_t dict_offset = type->tp_dictoffset;
577 PyObject *dict = NULL;
578
579 if (dict_offset != 0) {
580 // Negative dictionary offsets have special meaning.
581 if (dict_offset < 0) {
582 Py_ssize_t tsize;
583 size_t size;
584
585 tsize = ((PyVarObject *)source)->ob_size;
586 if (tsize < 0) {
587 tsize = -tsize;
588 }
589 size = _PyObject_VAR_SIZE(type, tsize);
590
591 dict_offset += (long)size;
592 }
593
594 PyObject **dict_pointer = (PyObject **)((char *)source + dict_offset);
595 dict = *dict_pointer;
596 }
597
598 if (dict != NULL) {
599 CHECK_OBJECT(dict);
600
601 // TODO: If this is an exact dict, we don't have to hold a reference, is it?
602 Py_INCREF(dict);
603
604 PyObject *result = DICT_GET_ITEM1(tstate, dict, attr_name);
605 DROP_ERROR_OCCURRED(tstate);
606
607 Py_DECREF(dict);
608
609 if (result != NULL) {
610 Py_XDECREF(descr);
611
612 CHECK_OBJECT(result);
613
614 Py_DECREF(result);
615 return true;
616 }
617 }
618
619 if (func != NULL) {
620 PyObject *result = func(descr, source, (PyObject *)type);
621 Py_DECREF(descr);
622
623 if (result != NULL) {
624 CHECK_OBJECT(result);
625
626 Py_DECREF(result);
627 return true;
628 }
629
630 if (CHECK_AND_CLEAR_ATTRIBUTE_ERROR_OCCURRED(tstate) == false) {
631 return false;
632 }
633 return true;
634 }
635
636 if (descr != NULL) {
637 CHECK_OBJECT(descr);
638
639 Py_DECREF(descr);
640 return true;
641 }
642
643 return false;
644 }
645#if PYTHON_VERSION < 0x300
646 else if (type->tp_getattro == PyInstance_Type.tp_getattro && Nuitka_String_CheckExact(attr_name)) {
647 PyObject *result = LOOKUP_INSTANCE(tstate, source, attr_name);
648
649 if (result == NULL) {
650 CLEAR_ERROR_OCCURRED(tstate);
651
652 return false;
653 }
654
655 CHECK_OBJECT(result);
656
657 Py_DECREF(result);
658 return true;
659 }
660#endif
661 else if (type->tp_getattro != NULL) {
662 PyObject *result = (*type->tp_getattro)(source, attr_name);
663
664 if (result == NULL) {
665 DROP_ERROR_OCCURRED(tstate);
666
667 return false;
668 }
669
670 CHECK_OBJECT(result);
671 Py_DECREF(result);
672 return true;
673 } else if (type->tp_getattr != NULL) {
674 PyObject *result = (*type->tp_getattr)(source, (char *)Nuitka_String_AsString_Unchecked(attr_name));
675
676 if (result == NULL) {
677 CLEAR_ERROR_OCCURRED(tstate);
678
679 return false;
680 }
681
682 CHECK_OBJECT(result);
683 Py_DECREF(result);
684 return true;
685 } else {
686 return false;
687 }
688#endif
689}
690
691int HAS_ATTR_BOOL2(PyThreadState *tstate, PyObject *source, PyObject *attr_name) {
692 CHECK_OBJECT(source);
693 CHECK_OBJECT(attr_name);
694
695#if _NUITKA_EXPERIMENTAL_DISABLE_ATTR_OPT
696 PyObject *result = PyObject_GetAttr(source, attr_name);
697
698 if (CHECK_AND_CLEAR_ATTRIBUTE_ERROR_OCCURRED(tstate) == false) {
699 return -1;
700 }
701
702 if (result == NULL) {
703 return 0;
704 }
705
706 return CHECK_IF_TRUE(result) ? 1 : 0;
707#else
708 PyTypeObject *type = Py_TYPE(source);
709
710 if (hasTypeGenericGetAttr(type)) {
711 // Unfortunately this is required, although of cause rarely necessary.
712 if (unlikely(type->tp_dict == NULL)) {
713 if (unlikely(PyType_Ready(type) < 0)) {
714 return -1;
715 }
716 }
717
718 PyObject *descr = Nuitka_TypeLookup(type, attr_name);
719 descrgetfunc func = NULL;
720
721 if (descr != NULL) {
722 // Hold a refcount, Nuitka_TypeLookup does not give any.
723 Py_INCREF(descr);
724
725 if (NuitkaType_HasFeatureClass(Py_TYPE(descr))) {
726 func = Py_TYPE(descr)->tp_descr_get;
727
728 if (func != NULL && Nuitka_Descr_IsData(descr)) {
729 PyObject *result = func(descr, source, (PyObject *)type);
730 Py_DECREF(descr);
731
732 if (result) {
733 CHECK_OBJECT(result);
734
735 Py_DECREF(result);
736 return 1;
737 }
738
739 DROP_ERROR_OCCURRED(tstate);
740 return 0;
741 }
742 }
743 }
744
745 Py_ssize_t dict_offset = type->tp_dictoffset;
746 PyObject *dict = NULL;
747
748 if (dict_offset != 0) {
749 // Negative dictionary offsets have special meaning.
750 if (dict_offset < 0) {
751 Py_ssize_t tsize;
752 size_t size;
753
754 tsize = ((PyVarObject *)source)->ob_size;
755 if (tsize < 0) {
756 tsize = -tsize;
757 }
758 size = _PyObject_VAR_SIZE(type, tsize);
759
760 dict_offset += (long)size;
761 }
762
763 PyObject **dict_pointer = (PyObject **)((char *)source + dict_offset);
764 dict = *dict_pointer;
765 }
766
767 if (dict != NULL) {
768 CHECK_OBJECT(dict);
769
770 // TODO: If this is an exact dict, we don't have to hold a reference, is it?
771 Py_INCREF(dict);
772
773 PyObject *result = DICT_GET_ITEM1(tstate, dict, attr_name);
774
775 if (CHECK_AND_CLEAR_ATTRIBUTE_ERROR_OCCURRED(tstate) == false) {
776 return -1;
777 }
778
779 Py_DECREF(dict);
780
781 if (result != NULL) {
782 Py_XDECREF(descr);
783
784 CHECK_OBJECT(result);
785
786 Py_DECREF(result);
787 return 1;
788 }
789 }
790
791 if (func != NULL) {
792 PyObject *result = func(descr, source, (PyObject *)type);
793 Py_DECREF(descr);
794
795 if (result != NULL) {
796 CHECK_OBJECT(result);
797
798 Py_DECREF(result);
799 return 1;
800 }
801
802 if (CHECK_AND_CLEAR_ATTRIBUTE_ERROR_OCCURRED(tstate) == false) {
803 return -1;
804 }
805 return 0;
806 }
807
808 if (descr != NULL) {
809 CHECK_OBJECT(descr);
810
811 Py_DECREF(descr);
812 return 1;
813 }
814
815 return 0;
816 }
817#if PYTHON_VERSION < 0x300
818 else if (type->tp_getattro == PyInstance_Type.tp_getattro && Nuitka_String_CheckExact(attr_name)) {
819 PyObject *result = LOOKUP_INSTANCE(tstate, source, attr_name);
820
821 if (result == NULL) {
822 return -1;
823 }
824
825 CHECK_OBJECT(result);
826
827 Py_DECREF(result);
828 return true;
829 }
830#endif
831 else if (type->tp_getattro != NULL) {
832 PyObject *result = (*type->tp_getattro)(source, attr_name);
833
834 if (result == NULL) {
835 if (CHECK_AND_CLEAR_ATTRIBUTE_ERROR_OCCURRED(tstate) == false) {
836 return -1;
837 }
838
839 return 0;
840 }
841
842 CHECK_OBJECT(result);
843 Py_DECREF(result);
844 return true;
845 } else if (type->tp_getattr != NULL) {
846 PyObject *result = (*type->tp_getattr)(source, (char *)Nuitka_String_AsString_Unchecked(attr_name));
847
848 if (result == NULL) {
849 if (CHECK_AND_CLEAR_ATTRIBUTE_ERROR_OCCURRED(tstate) == false) {
850 return -1;
851 }
852
853 return 0;
854 }
855
856 CHECK_OBJECT(result);
857 Py_DECREF(result);
858
859 return 1;
860 } else {
861 return 0;
862 }
863#endif
864}
865
866#if PYTHON_VERSION < 0x300
867extern PyObject *CALL_FUNCTION_WITH_ARGS3(PyThreadState *tstate, PyObject *called, PyObject *const *args);
868
869static bool SET_INSTANCE(PyThreadState *tstate, PyObject *target, PyObject *attr_name, PyObject *value) {
870 CHECK_OBJECT(target);
871 CHECK_OBJECT(attr_name);
872 CHECK_OBJECT(value);
873
874 assert(PyInstance_Check(target));
875 assert(PyString_Check(attr_name));
876
877 PyInstanceObject *target_instance = (PyInstanceObject *)target;
878
879 // The special cases should get their own SET_ATTRIBUTE_xxxx_SLOT variants
880 // on the code generation level as SET_ATTRIBUTE is called with constants
881 // only.
882 assert(attr_name != const_str_plain___dict__);
883 assert(attr_name != const_str_plain___class__);
884
885 if (target_instance->in_class->cl_setattr != NULL) {
886 PyObject *args[] = {target, attr_name, value};
887 PyObject *result = CALL_FUNCTION_WITH_ARGS3(tstate, target_instance->in_class->cl_setattr, args);
888
889 if (unlikely(result == NULL)) {
890 return false;
891 }
892
893 Py_DECREF(result);
894
895 return true;
896 } else {
897 int status = PyDict_SetItem(target_instance->in_dict, attr_name, value);
898
899 if (unlikely(status != 0)) {
900 return false;
901 }
902
903 return true;
904 }
905}
906#endif
907
908#if (PYTHON_VERSION < 0x300 || defined(_NUITKA_USE_UNEXPOSED_API)) && !defined(_NUITKA_EXPERIMENTAL_DISABLE_ATTR_OPT)
909
910// Classes in Python3 might share keys.
911#define CACHED_KEYS(type) (((PyHeapTypeObject *)type)->ht_cached_keys)
912
913static bool SET_ATTRIBUTE_GENERIC(PyThreadState *tstate, PyTypeObject *type, PyObject *target, PyObject *attr_name,
914 PyObject *value) {
915 // Unfortunately this is required, although of cause rarely necessary.
916 if (unlikely(type->tp_dict == NULL)) {
917 if (unlikely(PyType_Ready(type) < 0)) {
918 return false;
919 }
920 }
921
922 PyObject *descr = Nuitka_TypeLookup(type, attr_name);
923
924 if (descr != NULL) {
925 Py_INCREF(descr);
926
927 if (NuitkaType_HasFeatureClass(Py_TYPE(descr))) {
928 descrsetfunc func = Py_TYPE(descr)->tp_descr_set;
929
930 if (func != NULL && Nuitka_Descr_IsData(descr)) {
931 int res = func(descr, target, value);
932 Py_DECREF(descr);
933
934 return res == 0;
935 }
936 }
937 }
938
939 Py_ssize_t dict_offset = type->tp_dictoffset;
940 PyObject *dict = NULL;
941
942 if (dict_offset != 0) {
943 // Negative dictionary offsets have special meaning.
944 if (dict_offset < 0) {
945 Py_ssize_t tsize;
946 size_t size;
947
948 tsize = ((PyVarObject *)target)->ob_size;
949 if (tsize < 0) {
950 tsize = -tsize;
951 }
952 size = _PyObject_VAR_SIZE(type, tsize);
953
954 dict_offset += (long)size;
955 }
956
957 PyObject **dict_pointer = (PyObject **)((char *)target + dict_offset);
958
959#if PYTHON_VERSION >= 0x300
960 if ((type->tp_flags & Py_TPFLAGS_HEAPTYPE) && (CACHED_KEYS(type) != NULL)) {
961#if PYTHON_VERSION >= 0x3d0
962 int res = _PyObjectDict_SetItem(type, target, dict_pointer, attr_name, value);
963#else
964 int res = _PyObjectDict_SetItem(type, dict_pointer, attr_name, value);
965#endif
966 // TODO: Not possible for set, is it?
967 if (res < 0 && PyErr_ExceptionMatches(PyExc_KeyError)) {
968 SET_CURRENT_EXCEPTION_TYPE0_VALUE0(tstate, PyExc_AttributeError, attr_name);
969 return false;
970 }
971
972 return res >= 0;
973 } else
974#endif
975 {
976 dict = *dict_pointer;
977
978 if (dict == NULL) {
979 dict = MAKE_DICT_EMPTY(tstate);
980 *dict_pointer = dict;
981 }
982 }
983 }
984
985 if (dict != NULL) {
986 CHECK_OBJECT(dict);
987
988 // TODO: If this is an exact dict, we don't have to hold a reference, is it?
989 Py_INCREF(dict);
990
991 int res = PyDict_SetItem(dict, attr_name, value);
992
993 Py_DECREF(dict);
994 Py_XDECREF(descr);
995
996 return res == 0;
997 }
998
999#if PYTHON_VERSION < 0x300
1000 PyErr_Format(PyExc_AttributeError, "'%s' object has no attribute '%s'", type->tp_name,
1001 PyString_AS_STRING(attr_name));
1002#else
1003 PyErr_Format(PyExc_AttributeError, "'%s' object has no attribute '%U'", type->tp_name, attr_name);
1004#endif
1005 return false;
1006}
1007
1008#endif
1009
1010bool SET_ATTRIBUTE(PyThreadState *tstate, PyObject *target, PyObject *attr_name, PyObject *value) {
1011 CHECK_OBJECT(target);
1012 CHECK_OBJECT(attr_name);
1013 CHECK_OBJECT(value);
1014
1015#if _NUITKA_EXPERIMENTAL_DISABLE_ATTR_OPT
1016 int res = PyObject_SetAttr(target, attr_name, value);
1017 return res == 0;
1018#else
1019 PyTypeObject *type = Py_TYPE(target);
1020
1021#if PYTHON_VERSION < 0x300 || defined(_NUITKA_USE_UNEXPOSED_API)
1022 if (hasTypeGenericSetAttr(type)) {
1023 return SET_ATTRIBUTE_GENERIC(tstate, type, target, attr_name, value);
1024 }
1025#endif
1026#if PYTHON_VERSION < 0x300
1027 if (type->tp_setattro == PyInstance_Type.tp_setattro) {
1028 return SET_INSTANCE(tstate, target, attr_name, value);
1029 }
1030#endif
1031 if (type->tp_setattro != NULL) {
1032 int status = (*type->tp_setattro)(target, attr_name, value);
1033
1034 if (unlikely(status == -1)) {
1035 return false;
1036 }
1037
1038 return true;
1039 }
1040
1041 if (type->tp_setattr != NULL) {
1042 int status = (*type->tp_setattr)(target, (char *)Nuitka_String_AsString_Unchecked(attr_name), value);
1043
1044 if (unlikely(status == -1)) {
1045 return false;
1046 }
1047
1048 return true;
1049 }
1050
1051 if (type->tp_getattr == NULL && type->tp_getattro == NULL) {
1052 PyErr_Format(PyExc_TypeError, "'%s' object has no attributes (assign to %s)", type->tp_name,
1053 Nuitka_String_AsString_Unchecked(attr_name));
1054
1055 return false;
1056 } else {
1057 PyErr_Format(PyExc_TypeError, "'%s' object has only read-only attributes (assign to %s)", type->tp_name,
1058 Nuitka_String_AsString_Unchecked(attr_name));
1059
1060 return false;
1061 }
1062#endif
1063}
1064
1065bool SET_ATTRIBUTE_DICT_SLOT(PyThreadState *tstate, PyObject *target, PyObject *value) {
1066 CHECK_OBJECT(target);
1067 CHECK_OBJECT(value);
1068
1069#if PYTHON_VERSION < 0x300
1070 if (likely(PyInstance_Check(target))) {
1071 PyInstanceObject *target_instance = (PyInstanceObject *)target;
1072
1073 /* Note seems this doesn't have to be an exact dictionary. */
1074 if (unlikely(!PyDict_Check(value))) {
1075 SET_CURRENT_EXCEPTION_TYPE0_STR(tstate, PyExc_TypeError, "__dict__ must be set to a dictionary");
1076 return false;
1077 }
1078
1079 PyObject *old = target_instance->in_dict;
1080
1081 Py_INCREF(value);
1082 target_instance->in_dict = value;
1083
1084 Py_DECREF(old);
1085 } else
1086#endif
1087 {
1088 PyTypeObject *type = Py_TYPE(target);
1089
1090 if (type->tp_setattro != NULL) {
1091 int status = (*type->tp_setattro)(target, const_str_plain___dict__, value);
1092
1093 if (unlikely(status == -1)) {
1094 return false;
1095 }
1096 } else if (type->tp_setattr != NULL) {
1097 int status = (*type->tp_setattr)(target, (char *)"__dict__", value);
1098
1099 if (unlikely(status == -1)) {
1100 return false;
1101 }
1102 } else if (type->tp_getattr == NULL && type->tp_getattro == NULL) {
1103 PyErr_Format(PyExc_TypeError, "'%s' object has no attributes (assign to __dict__)", type->tp_name);
1104
1105 return false;
1106 } else {
1107 PyErr_Format(PyExc_TypeError, "'%s' object has only read-only attributes (assign to __dict__)",
1108 type->tp_name);
1109
1110 return false;
1111 }
1112 }
1113
1114 return true;
1115}
1116
1117bool SET_ATTRIBUTE_CLASS_SLOT(PyThreadState *tstate, PyObject *target, PyObject *value) {
1118 CHECK_OBJECT(target);
1119 CHECK_OBJECT(value);
1120
1121#if PYTHON_VERSION < 0x300
1122 if (likely(PyInstance_Check(target))) {
1123 PyInstanceObject *target_instance = (PyInstanceObject *)target;
1124
1125 if (unlikely(!PyClass_Check(value))) {
1126 SET_CURRENT_EXCEPTION_TYPE0_STR(tstate, PyExc_TypeError, "__class__ must be set to a class");
1127 return false;
1128 }
1129
1130 PyObject *old = (PyObject *)(target_instance->in_class);
1131 Py_INCREF(value);
1132 target_instance->in_class = (PyClassObject *)value;
1133 Py_DECREF(old);
1134 } else
1135#endif
1136 {
1137 PyTypeObject *type = Py_TYPE(target);
1138
1139 if (type->tp_setattro != NULL) {
1140 int status = (*type->tp_setattro)(target, const_str_plain___class__, value);
1141
1142 if (unlikely(status == -1)) {
1143 return false;
1144 }
1145 } else if (type->tp_setattr != NULL) {
1146 int status = (*type->tp_setattr)(target, (char *)"__class__", value);
1147
1148 if (unlikely(status == -1)) {
1149 return false;
1150 }
1151 } else if (type->tp_getattr == NULL && type->tp_getattro == NULL) {
1152 PyErr_Format(PyExc_TypeError, "'%s' object has no attributes (assign to __class__)", type->tp_name);
1153
1154 return false;
1155 } else {
1156 PyErr_Format(PyExc_TypeError, "'%s' object has only read-only attributes (assign to __class__)",
1157 type->tp_name);
1158
1159 return false;
1160 }
1161 }
1162
1163 return true;
1164}
1165
1166PyObject *LOOKUP_SPECIAL(PyThreadState *tstate, PyObject *source, PyObject *attr_name) {
1167#if PYTHON_VERSION < 0x300
1168 if (PyInstance_Check(source)) {
1169 return LOOKUP_INSTANCE(tstate, source, attr_name);
1170 }
1171#endif
1172
1173 // TODO: There is heavy optimization in CPython to avoid it. Potentially
1174 // that's worth it to imitate that.
1175
1176 PyObject *result = Nuitka_TypeLookup(Py_TYPE(source), attr_name);
1177
1178 if (likely(result)) {
1179 descrgetfunc func = Py_TYPE(result)->tp_descr_get;
1180
1181 if (func == NULL) {
1182 Py_INCREF(result);
1183 return result;
1184 } else {
1185 PyObject *func_result = func(result, source, (PyObject *)(Py_TYPE(source)));
1186
1187 if (unlikely(func_result == NULL)) {
1188 return NULL;
1189 }
1190
1191 CHECK_OBJECT(func_result);
1192 return func_result;
1193 }
1194 }
1195
1196#if PYTHON_VERSION < 0x3b0
1197 SET_CURRENT_EXCEPTION_TYPE0_VALUE0(tstate, PyExc_AttributeError, attr_name);
1198#else
1199 // TODO: Maybe we should have dedicated variations with the 4 hard coded
1200 // attribute names, might save a bit of complexity to large programs not
1201 // having to pass constant values in what is a frequent construct.
1202 if (attr_name == const_str_plain___exit__) {
1203 SET_CURRENT_EXCEPTION_TYPE_COMPLAINT(
1204 "'%s' object does not support the context manager protocol (missed __exit__ method)", source);
1205 } else if (attr_name == const_str_plain___aexit__) {
1206 SET_CURRENT_EXCEPTION_TYPE_COMPLAINT(
1207 "'%s' object does not support the asynchronous context manager protocol (missed __aexit__ method)", source);
1208 } else if (attr_name == const_str_plain___aenter__) {
1209 SET_CURRENT_EXCEPTION_TYPE_COMPLAINT("'%s' object does not support the asynchronous context manager protocol",
1210 source);
1211 } else {
1212 SET_CURRENT_EXCEPTION_TYPE_COMPLAINT("'%s' object does not support the context manager protocol", source);
1213 }
1214#endif
1215 return NULL;
1216}
1217
1218PyObject *LOOKUP_MODULE_VALUE(PyDictObject *module_dict, PyObject *var_name) {
1219 PyObject *result = GET_STRING_DICT_VALUE(module_dict, (Nuitka_StringObject *)var_name);
1220
1221 if (unlikely(result == NULL)) {
1222 result = GET_STRING_DICT_VALUE(dict_builtin, (Nuitka_StringObject *)var_name);
1223 }
1224
1225 return result;
1226}
1227
1228// Part of "Nuitka", an optimizing Python compiler that is compatible and
1229// integrates with CPython, but also works on its own.
1230//
1231// Licensed under the Apache License, Version 2.0 (the "License");
1232// you may not use this file except in compliance with the License.
1233// You may obtain a copy of the License at
1234//
1235// http://www.apache.org/licenses/LICENSE-2.0
1236//
1237// Unless required by applicable law or agreed to in writing, software
1238// distributed under the License is distributed on an "AS IS" BASIS,
1239// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1240// See the License for the specific language governing permissions and
1241// limitations under the License.