• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Test Vectorcall in the limited API */
2 
3 // Need limited C API version 3.12 for PyObject_Vectorcall()
4 #include "pyconfig.h"   // Py_GIL_DISABLED
5 #if !defined(Py_GIL_DISABLED) && !defined(Py_LIMITED_API)
6 #  define Py_LIMITED_API 0x030c0000
7 #endif
8 
9 #include "parts.h"
10 #include "clinic/vectorcall_limited.c.h"
11 
12 /*[clinic input]
13 module _testlimitedcapi
14 [clinic start generated code]*/
15 /*[clinic end generated code: output=da39a3ee5e6b4b0d input=2700057f9c1135ba]*/
16 
17 static PyObject *
LimitedVectorCallClass_tpcall(PyObject * self,PyObject * args,PyObject * kwargs)18 LimitedVectorCallClass_tpcall(PyObject *self, PyObject *args, PyObject *kwargs) {
19     return PyUnicode_FromString("tp_call called");
20 }
21 
22 static PyObject *
LimitedVectorCallClass_vectorcall(PyObject * callable,PyObject * const * args,size_t nargsf,PyObject * kwnames)23 LimitedVectorCallClass_vectorcall(PyObject *callable,
24                             PyObject *const *args,
25                             size_t nargsf,
26                             PyObject *kwnames) {
27     return PyUnicode_FromString("vectorcall called");
28 }
29 
30 static PyObject *
LimitedVectorCallClass_new(PyTypeObject * tp,PyTypeObject * a,PyTypeObject * kw)31 LimitedVectorCallClass_new(PyTypeObject *tp, PyTypeObject *a, PyTypeObject *kw)
32 {
33     PyObject *self = ((allocfunc)PyType_GetSlot(tp, Py_tp_alloc))(tp, 0);
34     if (!self) {
35         return NULL;
36     }
37     *(vectorcallfunc*)((char*)self + sizeof(PyObject)) = (
38         LimitedVectorCallClass_vectorcall);
39     return self;
40 }
41 
42 /*[clinic input]
43 _testlimitedcapi.call_vectorcall
44 
45     callable: object
46     /
47 [clinic start generated code]*/
48 
49 static PyObject *
_testlimitedcapi_call_vectorcall(PyObject * module,PyObject * callable)50 _testlimitedcapi_call_vectorcall(PyObject *module, PyObject *callable)
51 /*[clinic end generated code: output=9cbb7832263a8eef input=0743636c12dccb28]*/
52 {
53     PyObject *args[3] = { NULL, NULL, NULL };
54     PyObject *kwname = NULL, *kwnames = NULL, *result = NULL;
55 
56     args[1] = PyUnicode_FromString("foo");
57     if (!args[1]) {
58         goto leave;
59     }
60 
61     args[2] = PyUnicode_FromString("bar");
62     if (!args[2]) {
63         goto leave;
64     }
65 
66     kwname = PyUnicode_InternFromString("baz");
67     if (!kwname) {
68         goto leave;
69     }
70 
71     kwnames = PyTuple_New(1);
72     if (!kwnames) {
73         goto leave;
74     }
75 
76     if (PyTuple_SetItem(kwnames, 0, kwname)) {
77         goto leave;
78     }
79 
80     result = PyObject_Vectorcall(
81         callable,
82         args + 1,
83         1 | PY_VECTORCALL_ARGUMENTS_OFFSET,
84         kwnames
85     );
86 
87 leave:
88     Py_XDECREF(args[1]);
89     Py_XDECREF(args[2]);
90     Py_XDECREF(kwnames);
91 
92     return result;
93 }
94 
95 /*[clinic input]
96 _testlimitedcapi.call_vectorcall_method
97 
98     callable: object
99     /
100 [clinic start generated code]*/
101 
102 static PyObject *
_testlimitedcapi_call_vectorcall_method(PyObject * module,PyObject * callable)103 _testlimitedcapi_call_vectorcall_method(PyObject *module, PyObject *callable)
104 /*[clinic end generated code: output=4558323a46cc09eb input=a736f7dbf15f1be5]*/
105 {
106     PyObject *args[3] = { NULL, NULL, NULL };
107     PyObject *name = NULL, *kwname = NULL,
108              *kwnames = NULL, *result = NULL;
109 
110     name = PyUnicode_FromString("f");
111     if (!name) {
112         goto leave;
113     }
114 
115     args[0] = callable;
116     args[1] = PyUnicode_FromString("foo");
117     if (!args[1]) {
118         goto leave;
119     }
120 
121     args[2] = PyUnicode_FromString("bar");
122     if (!args[2]) {
123         goto leave;
124     }
125 
126     kwname = PyUnicode_InternFromString("baz");
127     if (!kwname) {
128         goto leave;
129     }
130 
131     kwnames = PyTuple_New(1);
132     if (!kwnames) {
133         goto leave;
134     }
135 
136     if (PyTuple_SetItem(kwnames, 0, kwname)) {
137         goto leave;
138     }
139 
140 
141     result = PyObject_VectorcallMethod(
142         name,
143         args,
144         2 | PY_VECTORCALL_ARGUMENTS_OFFSET,
145         kwnames
146     );
147 
148 leave:
149     Py_XDECREF(name);
150     Py_XDECREF(args[1]);
151     Py_XDECREF(args[2]);
152     Py_XDECREF(kwnames);
153 
154     return result;
155 }
156 
157 static PyMemberDef LimitedVectorCallClass_members[] = {
158     {"__vectorcalloffset__", Py_T_PYSSIZET, sizeof(PyObject), Py_READONLY},
159     {NULL}
160 };
161 
162 static PyType_Slot LimitedVectorallClass_slots[] = {
163     {Py_tp_new, LimitedVectorCallClass_new},
164     {Py_tp_call, LimitedVectorCallClass_tpcall},
165     {Py_tp_members, LimitedVectorCallClass_members},
166     {0},
167 };
168 
169 static PyType_Spec LimitedVectorCallClass_spec = {
170     .name = "_testlimitedcapi.LimitedVectorCallClass",
171     .basicsize = (int)(sizeof(PyObject) + sizeof(vectorcallfunc)),
172     .flags = Py_TPFLAGS_DEFAULT
173         | Py_TPFLAGS_HAVE_VECTORCALL
174         | Py_TPFLAGS_BASETYPE,
175     .slots = LimitedVectorallClass_slots,
176 };
177 
178 static PyMethodDef TestMethods[] = {
179     _TESTLIMITEDCAPI_CALL_VECTORCALL_METHODDEF
180     _TESTLIMITEDCAPI_CALL_VECTORCALL_METHOD_METHODDEF
181     {NULL},
182 };
183 
184 int
_PyTestLimitedCAPI_Init_VectorcallLimited(PyObject * m)185 _PyTestLimitedCAPI_Init_VectorcallLimited(PyObject *m)
186 {
187     if (PyModule_AddFunctions(m, TestMethods) < 0) {
188         return -1;
189     }
190 
191     PyObject *LimitedVectorCallClass = PyType_FromModuleAndSpec(
192         m, &LimitedVectorCallClass_spec, NULL);
193     if (!LimitedVectorCallClass) {
194         return -1;
195     }
196     if (PyModule_AddType(m, (PyTypeObject *)LimitedVectorCallClass) < 0) {
197         return -1;
198     }
199     Py_DECREF(LimitedVectorCallClass);
200     return 0;
201 }
202