Nuitka
The Python compiler
Loading...
Searching...
No Matches
subscripts.h
1// Copyright 2025, Kay Hayen, mailto:kay.hayen@gmail.com find license text at end of file
2
3#ifndef __NUITKA_HELPER_SUBSCRIPTS_H__
4#define __NUITKA_HELPER_SUBSCRIPTS_H__
5
6extern PyObject *STRING_FROM_CHAR(unsigned char c);
7
8#if PYTHON_VERSION >= 0x3b0
9static void formatNotSubscriptableTypeError(PyObject *type) {
10 SET_CURRENT_EXCEPTION_TYPE0_FORMAT1(PyExc_TypeError, "type '%s' is not subscriptable",
11 ((PyTypeObject *)type)->tp_name);
12}
13#endif
14
15#if !defined(_NUITKA_EXPERIMENTAL_DISABLE_SUBSCRIPT_OPT)
16static void formatNotSubscriptableError(PyObject *source) {
17 SET_CURRENT_EXCEPTION_TYPE_COMPLAINT(
18#if PYTHON_VERSION < 0x270
19 "'%s' object is unsubscriptable",
20#elif PYTHON_VERSION <= 0x272
21 "'%s' object is not subscriptable",
22#elif PYTHON_VERSION < 0x300
23 "'%s' object has no attribute '__getitem__'",
24#else
25 "'%s' object is not subscriptable",
26#endif
27 source);
28}
29#endif
30
31#if PYTHON_VERSION < 0x370
32#define HAS_SEQUENCE_ITEM_SLOT(type) (type->tp_as_sequence != NULL)
33#else
34#define HAS_SEQUENCE_ITEM_SLOT(type) (type->tp_as_sequence != NULL && type->tp_as_sequence->sq_item)
35#endif
36
37#if !defined(_NUITKA_EXPERIMENTAL_DISABLE_SUBSCRIPT_OPT)
38static PyObject *SEQUENCE_GET_ITEM_CONST(PyObject *sequence, Py_ssize_t int_subscript) {
39 PySequenceMethods *tp_as_sequence = Py_TYPE(sequence)->tp_as_sequence;
40 assert(tp_as_sequence != NULL);
41
42#if PYTHON_VERSION < 0x370
43 if (unlikely(tp_as_sequence->sq_item == NULL)) {
44 PyErr_Format(PyExc_TypeError, "'%s' object does not support indexing", Py_TYPE(sequence)->tp_name);
45 return NULL;
46 }
47#endif
48
49 if (int_subscript < 0) {
50 if (tp_as_sequence->sq_length) {
51 Py_ssize_t length = (*tp_as_sequence->sq_length)(sequence);
52 if (length < 0) {
53 return NULL;
54 }
55
56 int_subscript += length;
57 }
58 }
59
60 PyObject *res = tp_as_sequence->sq_item(sequence, int_subscript);
61 return res;
62}
63#endif
64
65NUITKA_MAY_BE_UNUSED static PyObject *LOOKUP_SUBSCRIPT_CONST(PyThreadState *tstate, PyObject *source,
66 PyObject *const_subscript, Py_ssize_t int_subscript) {
67 CHECK_OBJECT(source);
68 CHECK_OBJECT(const_subscript);
69
70#if _NUITKA_EXPERIMENTAL_DISABLE_SUBSCRIPT_OPT
71 return PyObject_GetItem(source, const_subscript);
72#else
73 PyTypeObject *type = Py_TYPE(source);
74 PyMappingMethods *tp_as_mapping = type->tp_as_mapping;
75
76 PyObject *result;
77
78 if (tp_as_mapping && tp_as_mapping->mp_subscript) {
79 if (PyList_CheckExact(source)) {
80 Py_ssize_t list_size = PyList_GET_SIZE(source);
81
82 if (int_subscript < 0) {
83 if (-int_subscript > list_size) {
84 SET_CURRENT_EXCEPTION_TYPE0_STR(tstate, PyExc_IndexError, "list index out of range");
85 return NULL;
86 }
87
88 int_subscript += list_size;
89 } else {
90 if (int_subscript >= list_size) {
91 SET_CURRENT_EXCEPTION_TYPE0_STR(tstate, PyExc_IndexError, "list index out of range");
92 return NULL;
93 }
94 }
95
96 result = ((PyListObject *)source)->ob_item[int_subscript];
97
98 Py_INCREF(result);
99 return result;
100 }
101#if PYTHON_VERSION < 0x300
102 else if (PyString_CheckExact(source)) {
103 Py_ssize_t string_size = PyString_GET_SIZE(source);
104
105 if (int_subscript < 0) {
106 if (-int_subscript > string_size) {
107 SET_CURRENT_EXCEPTION_TYPE0_STR(tstate, PyExc_IndexError, "string index out of range");
108 return NULL;
109 }
110
111 int_subscript += string_size;
112 } else {
113 if (int_subscript >= string_size) {
114 SET_CURRENT_EXCEPTION_TYPE0_STR(tstate, PyExc_IndexError, "string index out of range");
115 return NULL;
116 }
117 }
118
119 unsigned char c = ((PyStringObject *)source)->ob_sval[int_subscript];
120 return STRING_FROM_CHAR(c);
121 }
122#else
123 else if (PyUnicode_CheckExact(source)) {
124 if (int_subscript < 0) {
125 int_subscript += PyUnicode_GET_LENGTH(source);
126 }
127
128 result = type->tp_as_sequence->sq_item(source, int_subscript);
129 }
130#endif
131 else {
132 result = tp_as_mapping->mp_subscript(source, const_subscript);
133 }
134 } else if (HAS_SEQUENCE_ITEM_SLOT(type)) {
135 result = SEQUENCE_GET_ITEM_CONST(source, int_subscript);
136 } else {
137#if PYTHON_VERSION >= 0x370
138 if (PyType_Check(source)) {
139#if PYTHON_VERSION >= 0x390
140 if (source == (PyObject *)&PyType_Type) {
141 PyObject *subscript = PyLong_FromSsize_t(int_subscript);
142 result = Py_GenericAlias(source, subscript);
143 Py_DECREF(subscript);
144
145 return result;
146 }
147#endif
148
149 PyObject *meth = LOOKUP_ATTRIBUTE(tstate, source, const_str_plain___class_getitem__);
150
151 if (meth) {
152 PyObject *subscript = PyLong_FromSsize_t(int_subscript);
153 result = CALL_FUNCTION_WITH_SINGLE_ARG(tstate, meth, subscript);
154 Py_DECREF(meth);
155 Py_DECREF(subscript);
156
157 return result;
158 }
159
160 // Different error against types for Python3.11+
161#if PYTHON_VERSION >= 0x3b0
162 formatNotSubscriptableTypeError(source);
163 return NULL;
164#endif
165 }
166#endif
167
168 formatNotSubscriptableError(source);
169 return NULL;
170 }
171
172 if (unlikely(result == NULL)) {
173 return NULL;
174 }
175
176 return result;
177#endif
178}
179
180NUITKA_MAY_BE_UNUSED static PyObject *LOOKUP_SUBSCRIPT(PyThreadState *tstate, PyObject *source, PyObject *subscript) {
181 CHECK_OBJECT(source);
182 CHECK_OBJECT(subscript);
183
184#if _NUITKA_EXPERIMENTAL_DISABLE_SUBSCRIPT_OPT
185 return PyObject_GetItem(source, subscript);
186#else
187 PyTypeObject *type = Py_TYPE(source);
188 PyMappingMethods *tp_as_mapping = type->tp_as_mapping;
189
190 if (tp_as_mapping != NULL && tp_as_mapping->mp_subscript != NULL) {
191 return tp_as_mapping->mp_subscript(source, subscript);
192 } else if (HAS_SEQUENCE_ITEM_SLOT(type)) {
193 if (Nuitka_Index_Check(subscript)) {
194 Py_ssize_t index = PyNumber_AsSsize_t(subscript, NULL);
195
196 if (index == -1 && HAS_ERROR_OCCURRED(tstate)) {
197 return NULL;
198 }
199
200 return SEQUENCE_GET_ITEM_CONST(source, index);
201 } else if (type->tp_as_sequence->sq_item) {
202 PyErr_Format(PyExc_TypeError, "sequence index must be integer, not '%s'", Py_TYPE(subscript)->tp_name);
203 return NULL;
204#if PYTHON_VERSION < 0x370
205 } else {
206 formatNotSubscriptableError(source);
207 return NULL;
208#endif
209 }
210 }
211
212#if PYTHON_VERSION >= 0x370
213 if (PyType_Check(source)) {
214#if PYTHON_VERSION >= 0x390
215 if (source == (PyObject *)&PyType_Type) {
216 return Py_GenericAlias(source, subscript);
217 }
218#endif
219
220 PyObject *meth = LOOKUP_ATTRIBUTE(tstate, source, const_str_plain___class_getitem__);
221
222 if (meth) {
223 PyObject *result = CALL_FUNCTION_WITH_SINGLE_ARG(tstate, meth, subscript);
224 Py_DECREF(meth);
225 return result;
226 }
227
228 // Different error against types for Python3.11+
229#if PYTHON_VERSION >= 0x3b0
230 formatNotSubscriptableTypeError(source);
231 return NULL;
232#endif
233 }
234#endif
235
236 formatNotSubscriptableError(source);
237 return NULL;
238#endif
239}
240
241bool MATCH_MAPPING_KEY(PyThreadState *tstate, PyObject *map, PyObject *key);
242
243NUITKA_MAY_BE_UNUSED static bool SET_SUBSCRIPT_CONST(PyThreadState *tstate, PyObject *target, PyObject *subscript,
244 Py_ssize_t int_subscript, PyObject *value) {
245 CHECK_OBJECT(value);
246 CHECK_OBJECT(target);
247 CHECK_OBJECT(subscript);
248
249#if _NUITKA_EXPERIMENTAL_DISABLE_SUBSCRIPT_OPT
250 int res = PyObject_SetItem(target, subscript, value);
251 return res == 0;
252#else
253 PyMappingMethods *tp_as_mapping = Py_TYPE(target)->tp_as_mapping;
254
255 if (tp_as_mapping != NULL && tp_as_mapping->mp_ass_subscript) {
256 if (PyList_CheckExact(target)) {
257 Py_ssize_t list_size = PyList_GET_SIZE(target);
258
259 if (int_subscript < 0) {
260 if (-int_subscript > list_size) {
261 SET_CURRENT_EXCEPTION_TYPE0_STR(tstate, PyExc_IndexError, "list assignment index out of range");
262
263 return false;
264 }
265
266 int_subscript += list_size;
267 }
268
269 PyListObject *target_list = (PyListObject *)target;
270
271 PyObject *old_value = target_list->ob_item[int_subscript];
272 Py_INCREF(value);
273 target_list->ob_item[int_subscript] = value;
274 Py_DECREF(old_value);
275
276 return true;
277 } else {
278 int res = tp_as_mapping->mp_ass_subscript(target, subscript, value);
279
280 if (unlikely(res == -1)) {
281 return false;
282 }
283
284 return true;
285 }
286 } else if (Py_TYPE(target)->tp_as_sequence) {
287 if (Nuitka_Index_Check(subscript)) {
288 Py_ssize_t key_value = PyNumber_AsSsize_t(subscript, PyExc_IndexError);
289
290 if (key_value == -1) {
291 if (HAS_ERROR_OCCURRED(tstate)) {
292 return false;
293 }
294 }
295
296 return SEQUENCE_SET_ITEM(target, key_value, value);
297 } else if (Py_TYPE(target)->tp_as_sequence->sq_ass_item) {
298 PyErr_Format(PyExc_TypeError, "sequence index must be integer, not '%s'", Py_TYPE(subscript)->tp_name);
299
300 return false;
301 } else {
302 PyErr_Format(PyExc_TypeError, "'%s' object does not support item assignment", Py_TYPE(target)->tp_name);
303
304 return false;
305 }
306 } else {
307 PyErr_Format(PyExc_TypeError, "'%s' object does not support item assignment", Py_TYPE(target)->tp_name);
308
309 return false;
310 }
311#endif
312}
313
314NUITKA_MAY_BE_UNUSED static bool SET_SUBSCRIPT(PyThreadState *tstate, PyObject *target, PyObject *subscript,
315 PyObject *value) {
316 CHECK_OBJECT(value);
317 CHECK_OBJECT(target);
318 CHECK_OBJECT(subscript);
319
320#if _NUITKA_EXPERIMENTAL_DISABLE_SUBSCRIPT_OPT
321 int res = PyObject_SetItem(target, subscript, value);
322 return res == 0;
323#else
324 PyMappingMethods *tp_as_mapping = Py_TYPE(target)->tp_as_mapping;
325
326 if (tp_as_mapping != NULL && tp_as_mapping->mp_ass_subscript) {
327 int res = tp_as_mapping->mp_ass_subscript(target, subscript, value);
328
329 if (unlikely(res == -1)) {
330 return false;
331 }
332 } else if (Py_TYPE(target)->tp_as_sequence) {
333 if (Nuitka_Index_Check(subscript)) {
334 Py_ssize_t key_value = PyNumber_AsSsize_t(subscript, PyExc_IndexError);
335
336 if (key_value == -1) {
337 if (HAS_ERROR_OCCURRED(tstate)) {
338 return false;
339 }
340 }
341
342 return SEQUENCE_SET_ITEM(target, key_value, value);
343 } else if (Py_TYPE(target)->tp_as_sequence->sq_ass_item) {
344 PyErr_Format(PyExc_TypeError, "sequence index must be integer, not '%s'", Py_TYPE(subscript)->tp_name);
345
346 return false;
347 } else {
348 PyErr_Format(PyExc_TypeError, "'%s' object does not support item assignment", Py_TYPE(target)->tp_name);
349
350 return false;
351 }
352 } else {
353 PyErr_Format(PyExc_TypeError, "'%s' object does not support item assignment", Py_TYPE(target)->tp_name);
354
355 return false;
356 }
357
358 return true;
359#endif
360}
361
362NUITKA_MAY_BE_UNUSED static bool DEL_SUBSCRIPT(PyObject *target, PyObject *subscript) {
363 CHECK_OBJECT(target);
364 CHECK_OBJECT(subscript);
365
366 int status = PyObject_DelItem(target, subscript);
367
368 if (unlikely(status == -1)) {
369 return false;
370 }
371
372 return true;
373}
374
375#endif
376
377// Part of "Nuitka", an optimizing Python compiler that is compatible and
378// integrates with CPython, but also works on its own.
379//
380// Licensed under the Apache License, Version 2.0 (the "License");
381// you may not use this file except in compliance with the License.
382// You may obtain a copy of the License at
383//
384// http://www.apache.org/licenses/LICENSE-2.0
385//
386// Unless required by applicable law or agreed to in writing, software
387// distributed under the License is distributed on an "AS IS" BASIS,
388// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
389// See the License for the specific language governing permissions and
390// limitations under the License.