1 #define Py_LIMITED_API 0x030c0000 // 3.12
2 #include "parts.h"
3
4 #ifdef LIMITED_API_AVAILABLE
5
6 #include "structmember.h" // PyMemberDef
7
8 /* Test Vectorcall in the limited API */
9
10 static PyObject *
11 LimitedVectorCallClass_tpcall(PyObject *self, PyObject *args, PyObject *kwargs) {
12 return PyUnicode_FromString("tp_call called");
13 }
14
15 static PyObject *
16 LimitedVectorCallClass_vectorcall(PyObject *callable,
17 PyObject *const *args,
18 size_t nargsf,
19 PyObject *kwnames) {
20 return PyUnicode_FromString("vectorcall called");
21 }
22
23 static PyObject *
24 LimitedVectorCallClass_new(PyTypeObject *tp, PyTypeObject *a, PyTypeObject *kw)
25 {
26 PyObject *self = ((allocfunc)PyType_GetSlot(tp, Py_tp_alloc))(tp, 0);
27 if (!self) {
28 return NULL;
29 }
30 *(vectorcallfunc*)((char*)self + sizeof(PyObject)) = (
31 LimitedVectorCallClass_vectorcall);
32 return self;
33 }
34
35 static PyObject *
36 call_vectorcall(PyObject* self, PyObject *callable)
37 {
38 PyObject *args[3] = { NULL, NULL, NULL };
39 PyObject *kwname = NULL, *kwnames = NULL, *result = NULL;
40
41 args[1] = PyUnicode_FromString("foo");
42 if (!args[1]) {
43 goto leave;
44 }
45
46 args[2] = PyUnicode_FromString("bar");
47 if (!args[2]) {
48 goto leave;
49 }
50
51 kwname = PyUnicode_InternFromString("baz");
52 if (!kwname) {
53 goto leave;
54 }
55
56 kwnames = PyTuple_New(1);
57 if (!kwnames) {
58 goto leave;
59 }
60
61 if (PyTuple_SetItem(kwnames, 0, kwname)) {
62 goto leave;
63 }
64
65 result = PyObject_Vectorcall(
66 callable,
67 args + 1,
68 1 | PY_VECTORCALL_ARGUMENTS_OFFSET,
69 kwnames
70 );
71
72 leave:
73 Py_XDECREF(args[1]);
74 Py_XDECREF(args[2]);
75 Py_XDECREF(kwnames);
76
77 return result;
78 }
79
80 static PyObject *
81 call_vectorcall_method(PyObject* self, PyObject *callable)
82 {
83 PyObject *args[3] = { NULL, NULL, NULL };
84 PyObject *name = NULL, *kwname = NULL,
85 *kwnames = NULL, *result = NULL;
86
87 name = PyUnicode_FromString("f");
88 if (!name) {
89 goto leave;
90 }
91
92 args[0] = callable;
93 args[1] = PyUnicode_FromString("foo");
94 if (!args[1]) {
95 goto leave;
96 }
97
98 args[2] = PyUnicode_FromString("bar");
99 if (!args[2]) {
100 goto leave;
101 }
102
103 kwname = PyUnicode_InternFromString("baz");
104 if (!kwname) {
105 goto leave;
106 }
107
108 kwnames = PyTuple_New(1);
109 if (!kwnames) {
110 goto leave;
111 }
112
113 if (PyTuple_SetItem(kwnames, 0, kwname)) {
114 goto leave;
115 }
116
117
118 result = PyObject_VectorcallMethod(
119 name,
120 args,
121 2 | PY_VECTORCALL_ARGUMENTS_OFFSET,
122 kwnames
123 );
124
125 leave:
126 Py_XDECREF(name);
127 Py_XDECREF(args[1]);
128 Py_XDECREF(args[2]);
129 Py_XDECREF(kwnames);
130
131 return result;
132 }
133
134 static PyMemberDef LimitedVectorCallClass_members[] = {
135 {"__vectorcalloffset__", T_PYSSIZET, sizeof(PyObject), READONLY},
136 {NULL}
137 };
138
139 static PyType_Slot LimitedVectorallClass_slots[] = {
140 {Py_tp_new, LimitedVectorCallClass_new},
141 {Py_tp_call, LimitedVectorCallClass_tpcall},
142 {Py_tp_members, LimitedVectorCallClass_members},
143 {0},
144 };
145
146 static PyType_Spec LimitedVectorCallClass_spec = {
147 .name = "_testcapi.LimitedVectorCallClass",
148 .basicsize = (int)(sizeof(PyObject) + sizeof(vectorcallfunc)),
149 .flags = Py_TPFLAGS_DEFAULT
150 | Py_TPFLAGS_HAVE_VECTORCALL
151 | Py_TPFLAGS_BASETYPE,
152 .slots = LimitedVectorallClass_slots,
153 };
154
155 static PyMethodDef TestMethods[] = {
156 {"call_vectorcall", call_vectorcall, METH_O},
157 {"call_vectorcall_method", call_vectorcall_method, METH_O},
158 {NULL},
159 };
160
161 int
162 _PyTestCapi_Init_VectorcallLimited(PyObject *m) {
163 if (PyModule_AddFunctions(m, TestMethods) < 0) {
164 return -1;
165 }
166
167 PyObject *LimitedVectorCallClass = PyType_FromModuleAndSpec(
168 m, &LimitedVectorCallClass_spec, NULL);
169 if (!LimitedVectorCallClass) {
170 return -1;
171 }
172 if (PyModule_AddType(m, (PyTypeObject *)LimitedVectorCallClass) < 0) {
173 return -1;
174 }
175
176 return 0;
177 }
178
179 #endif // LIMITED_API_AVAILABLE