Nuitka
The Python compiler
Loading...
Searching...
No Matches
HelpersComparisonEqUtils.c
1// Copyright 2025, Kay Hayen, mailto:kay.hayen@gmail.com find license text at end of file
2
10// This file is included from another C file, help IDEs to still parse it on
11// its own.
12#ifdef __IDE_ONLY__
13#include "nuitka/prelude.h"
14#endif
15
16python_init_proc default_tp_init_wrapper;
17
18#if PYTHON_VERSION < 0x300
19static cmpfunc default_tp_compare;
20#endif
21
22void _initSlotCompare(void) {
23 // Create a class with "__cmp__" attribute, to get a hand at the default
24 // implementation of tp_compare. It's not part of the API and with shared
25 // libraries it's not accessible. The name does not matter, nor does the
26 // actual value used for "__cmp__".
27
28 PyThreadState *tstate = PyThreadState_GET();
29
30 // Use "int" as the base class.
31 PyObject *pos_args = MAKE_TUPLE1(tstate, (PyObject *)&PyLong_Type);
32
33 // Use "__cmp__" with true value, won't matter.
34 // Note: Not using MAKE_DICT_EMPTY on purpose, this is called early on.
35 PyObject *kw_args = PyDict_New();
36#if PYTHON_VERSION < 0x0300
37 PyDict_SetItem(kw_args, const_str_plain___cmp__, Py_True);
38#endif
39 PyDict_SetItem(kw_args, const_str_plain___init__, (PyObject *)Py_TYPE(Py_None));
40
41 // Create the type.
42 CHECK_OBJECT(const_str_plain___cmp__);
43 PyTypeObject *c = (PyTypeObject *)PyObject_CallFunctionObjArgs((PyObject *)&PyType_Type, const_str_plain___cmp__,
44 pos_args, kw_args, NULL);
45 Py_DECREF(pos_args);
46 Py_DECREF(kw_args);
47
48 CHECK_OBJECT(c);
49
50#if PYTHON_VERSION < 0x0300
51 assert(c->tp_compare);
52 default_tp_compare = c->tp_compare;
53#endif
54
55 assert(c->tp_init);
56 default_tp_init_wrapper = c->tp_init;
57
58 Py_DECREF(c);
59}
60
61#if PYTHON_VERSION < 0x300
62
63static inline int adjust_tp_compare(int c) {
64 PyThreadState *tstate = PyThreadState_GET();
65
66 if (HAS_ERROR_OCCURRED(tstate)) {
67 return -2;
68 } else if (c < -1 || c > 1) {
69 return c < -1 ? -1 : 1;
70 } else {
71 return c;
72 }
73}
74
75static inline int coerce_objects(PyObject **pa, PyObject **pb) {
76 PyObject *a = *pa;
77 PyObject *b = *pb;
78
79 // Shortcut only for old-style types
80 if (a->ob_type == b->ob_type && !PyType_HasFeature(a->ob_type, Py_TPFLAGS_CHECKTYPES)) {
81 Py_INCREF(a);
82 Py_INCREF(b);
83
84 return 0;
85 }
86 if (a->ob_type->tp_as_number && a->ob_type->tp_as_number->nb_coerce) {
87 int res = (*a->ob_type->tp_as_number->nb_coerce)(pa, pb);
88
89 if (res <= 0) {
90 return res;
91 }
92 }
93 if (b->ob_type->tp_as_number && b->ob_type->tp_as_number->nb_coerce) {
94 int res = (*b->ob_type->tp_as_number->nb_coerce)(pb, pa);
95
96 if (res <= 0) {
97 return res;
98 }
99 }
100
101 return 1;
102}
103
104static int try_3way_compare(PyObject *a, PyObject *b) {
105 cmpfunc f1 = a->ob_type->tp_compare;
106 cmpfunc f2 = b->ob_type->tp_compare;
107 int c;
108
109 // Same compares, just use it.
110 if (f1 != NULL && f1 == f2) {
111 c = (*f1)(a, b);
112 return adjust_tp_compare(c);
113 }
114
115 // If one slot is _PyObject_SlotCompare (which we got our hands on under a
116 // different name in case it's a shared library), prefer it.
117 if (f1 == default_tp_compare || f2 == default_tp_compare) {
118 return default_tp_compare(a, b);
119 }
120
121 // Try coercion.
122 c = coerce_objects(&a, &b);
123
124 if (c < 0) {
125 return -2;
126 }
127 if (c > 0) {
128 return 2;
129 }
130
131 f1 = a->ob_type->tp_compare;
132 if (f1 != NULL && f1 == b->ob_type->tp_compare) {
133 c = (*f1)(a, b);
134 Py_DECREF(a);
135 Py_DECREF(b);
136
137 return adjust_tp_compare(c);
138 }
139
140 // No comparison defined.
141 Py_DECREF(a);
142 Py_DECREF(b);
143 return 2;
144}
145
146#endif
147
148static inline bool IS_SANE_TYPE(PyTypeObject *type) {
149 return
150#if PYTHON_VERSION < 0x300
151 type == &PyString_Type || type == &PyInt_Type ||
152#endif
153 type == &PyLong_Type || type == &PyList_Type || type == &PyTuple_Type;
154}
155
156// Part of "Nuitka", an optimizing Python compiler that is compatible and
157// integrates with CPython, but also works on its own.
158//
159// Licensed under the Apache License, Version 2.0 (the "License");
160// you may not use this file except in compliance with the License.
161// You may obtain a copy of the License at
162//
163// http://www.apache.org/licenses/LICENSE-2.0
164//
165// Unless required by applicable law or agreed to in writing, software
166// distributed under the License is distributed on an "AS IS" BASIS,
167// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
168// See the License for the specific language governing permissions and
169// limitations under the License.