Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-93649: Split vectorcall testing from _testcapimodule.c #94549

Merged
merged 5 commits into from
Jul 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile.pre.in
Original file line number Diff line number Diff line change
Expand Up @@ -2563,7 +2563,7 @@ MODULE__SHA3_DEPS=$(srcdir)/Modules/_sha3/sha3.c $(srcdir)/Modules/_sha3/sha3.h
MODULE__SHA512_DEPS=$(srcdir)/Modules/hashlib.h
MODULE__SOCKET_DEPS=$(srcdir)/Modules/socketmodule.h
MODULE__SSL_DEPS=$(srcdir)/Modules/_ssl.h $(srcdir)/Modules/_ssl/cert.c $(srcdir)/Modules/_ssl/debughelpers.c $(srcdir)/Modules/_ssl/misc.c $(srcdir)/Modules/_ssl_data.h $(srcdir)/Modules/_ssl_data_111.h $(srcdir)/Modules/_ssl_data_300.h $(srcdir)/Modules/socketmodule.h
MODULE__TESTCAPI_DEPS=$(srcdir)/Modules/testcapi_long.h
MODULE__TESTCAPI_DEPS=$(srcdir)/Modules/testcapi_long.h Modules/_testcapi/parts.h $(srcdir)/Modules/_testcapi/vectorcall.c
MODULE__SQLITE3_DEPS=$(srcdir)/Modules/_sqlite/connection.h $(srcdir)/Modules/_sqlite/cursor.h $(srcdir)/Modules/_sqlite/microprotocols.h $(srcdir)/Modules/_sqlite/module.h $(srcdir)/Modules/_sqlite/prepare_protocol.h $(srcdir)/Modules/_sqlite/row.h $(srcdir)/Modules/_sqlite/util.h

# IF YOU PUT ANYTHING HERE IT WILL GO AWAY
Expand Down
2 changes: 1 addition & 1 deletion Modules/Setup.stdlib.in
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@
@MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c

# Some testing modules MUST be built as shared libraries.
*shared*
Expand Down
3 changes: 3 additions & 0 deletions Modules/_testcapi/README.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Tests in this directory are compiled into the _testcapi extension.
The main file for the extension is Modules/_testcapimodule.c, which
calls `_PyTestCapi_Init_*` from these functions.
3 changes: 3 additions & 0 deletions Modules/_testcapi/parts.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#include "Python.h"

PyAPI_FUNC(int) _PyTestCapi_Init_Vectorcall(PyObject *module);
270 changes: 270 additions & 0 deletions Modules/_testcapi/vectorcall.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
#include "parts.h"
#include <stddef.h> // offsetof


/* Test PEP 590 - Vectorcall */

static int
fastcall_args(PyObject *args, PyObject ***stack, Py_ssize_t *nargs)
{
if (args == Py_None) {
*stack = NULL;
*nargs = 0;
}
else if (PyTuple_Check(args)) {
*stack = ((PyTupleObject *)args)->ob_item;
*nargs = PyTuple_GET_SIZE(args);
}
else {
PyErr_SetString(PyExc_TypeError, "args must be None or a tuple");
return -1;
}
return 0;
}


static PyObject *
test_pyobject_fastcall(PyObject *self, PyObject *args)
{
PyObject *func, *func_args;
PyObject **stack;
Py_ssize_t nargs;

if (!PyArg_ParseTuple(args, "OO", &func, &func_args)) {
return NULL;
}

if (fastcall_args(func_args, &stack, &nargs) < 0) {
return NULL;
}
return _PyObject_FastCall(func, stack, nargs);
}

static PyObject *
test_pyobject_fastcalldict(PyObject *self, PyObject *args)
{
PyObject *func, *func_args, *kwargs;
PyObject **stack;
Py_ssize_t nargs;

if (!PyArg_ParseTuple(args, "OOO", &func, &func_args, &kwargs)) {
return NULL;
}

if (fastcall_args(func_args, &stack, &nargs) < 0) {
return NULL;
}

if (kwargs == Py_None) {
kwargs = NULL;
}
else if (!PyDict_Check(kwargs)) {
PyErr_SetString(PyExc_TypeError, "kwnames must be None or a dict");
return NULL;
}

return PyObject_VectorcallDict(func, stack, nargs, kwargs);
}

static PyObject *
test_pyobject_vectorcall(PyObject *self, PyObject *args)
{
PyObject *func, *func_args, *kwnames = NULL;
PyObject **stack;
Py_ssize_t nargs, nkw;

if (!PyArg_ParseTuple(args, "OOO", &func, &func_args, &kwnames)) {
return NULL;
}

if (fastcall_args(func_args, &stack, &nargs) < 0) {
return NULL;
}

if (kwnames == Py_None) {
kwnames = NULL;
}
else if (PyTuple_Check(kwnames)) {
nkw = PyTuple_GET_SIZE(kwnames);
if (nargs < nkw) {
PyErr_SetString(PyExc_ValueError, "kwnames longer than args");
return NULL;
}
nargs -= nkw;
}
else {
PyErr_SetString(PyExc_TypeError, "kwnames must be None or a tuple");
return NULL;
}
return PyObject_Vectorcall(func, stack, nargs, kwnames);
}

static PyObject *
test_pyvectorcall_call(PyObject *self, PyObject *args)
{
PyObject *func;
PyObject *argstuple;
PyObject *kwargs = NULL;

if (!PyArg_ParseTuple(args, "OO|O", &func, &argstuple, &kwargs)) {
return NULL;
}

if (!PyTuple_Check(argstuple)) {
PyErr_SetString(PyExc_TypeError, "args must be a tuple");
return NULL;
}
if (kwargs != NULL && !PyDict_Check(kwargs)) {
PyErr_SetString(PyExc_TypeError, "kwargs must be a dict");
return NULL;
}

return PyVectorcall_Call(func, argstuple, kwargs);
}

static PyMethodDef TestMethods[] = {
{"pyobject_fastcall", test_pyobject_fastcall, METH_VARARGS},
{"pyobject_fastcalldict", test_pyobject_fastcalldict, METH_VARARGS},
{"pyobject_vectorcall", test_pyobject_vectorcall, METH_VARARGS},
{"pyvectorcall_call", test_pyvectorcall_call, METH_VARARGS},
{NULL},
};


typedef struct {
PyObject_HEAD
vectorcallfunc vectorcall;
} MethodDescriptorObject;

static PyObject *
MethodDescriptor_vectorcall(PyObject *callable, PyObject *const *args,
size_t nargsf, PyObject *kwnames)
{
/* True if using the vectorcall function in MethodDescriptorObject
* but False for MethodDescriptor2Object */
MethodDescriptorObject *md = (MethodDescriptorObject *)callable;
return PyBool_FromLong(md->vectorcall != NULL);
}

static PyObject *
MethodDescriptor_new(PyTypeObject* type, PyObject* args, PyObject *kw)
{
MethodDescriptorObject *op = (MethodDescriptorObject *)type->tp_alloc(type, 0);
op->vectorcall = MethodDescriptor_vectorcall;
return (PyObject *)op;
}

static PyObject *
func_descr_get(PyObject *func, PyObject *obj, PyObject *type)
{
if (obj == Py_None || obj == NULL) {
Py_INCREF(func);
return func;
}
return PyMethod_New(func, obj);
}

static PyObject *
nop_descr_get(PyObject *func, PyObject *obj, PyObject *type)
{
Py_INCREF(func);
return func;
}

static PyObject *
call_return_args(PyObject *self, PyObject *args, PyObject *kwargs)
{
Py_INCREF(args);
return args;
}

static PyTypeObject MethodDescriptorBase_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"MethodDescriptorBase",
sizeof(MethodDescriptorObject),
.tp_new = MethodDescriptor_new,
.tp_call = PyVectorcall_Call,
.tp_vectorcall_offset = offsetof(MethodDescriptorObject, vectorcall),
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
Py_TPFLAGS_METHOD_DESCRIPTOR | Py_TPFLAGS_HAVE_VECTORCALL,
.tp_descr_get = func_descr_get,
};

static PyTypeObject MethodDescriptorDerived_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"MethodDescriptorDerived",
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
};

static PyTypeObject MethodDescriptorNopGet_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"MethodDescriptorNopGet",
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
.tp_call = call_return_args,
.tp_descr_get = nop_descr_get,
};

typedef struct {
MethodDescriptorObject base;
vectorcallfunc vectorcall;
} MethodDescriptor2Object;

static PyObject *
MethodDescriptor2_new(PyTypeObject* type, PyObject* args, PyObject *kw)
{
MethodDescriptor2Object *op = PyObject_New(MethodDescriptor2Object, type);
op->base.vectorcall = NULL;
op->vectorcall = MethodDescriptor_vectorcall;
return (PyObject *)op;
}

static PyTypeObject MethodDescriptor2_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"MethodDescriptor2",
sizeof(MethodDescriptor2Object),
.tp_new = MethodDescriptor2_new,
.tp_call = PyVectorcall_Call,
.tp_vectorcall_offset = offsetof(MethodDescriptor2Object, vectorcall),
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_VECTORCALL,
};


int
_PyTestCapi_Init_Vectorcall(PyObject *m) {
if (PyModule_AddFunctions(m, TestMethods) < 0) {
return -1;
}

if (PyType_Ready(&MethodDescriptorBase_Type) < 0) {
return -1;
}
if (PyModule_AddType(m, &MethodDescriptorBase_Type) < 0) {
return -1;
}

MethodDescriptorDerived_Type.tp_base = &MethodDescriptorBase_Type;
if (PyType_Ready(&MethodDescriptorDerived_Type) < 0) {
return -1;
}
if (PyModule_AddType(m, &MethodDescriptorDerived_Type) < 0) {
return -1;
}

MethodDescriptorNopGet_Type.tp_base = &MethodDescriptorBase_Type;
if (PyType_Ready(&MethodDescriptorNopGet_Type) < 0) {
return -1;
}
if (PyModule_AddType(m, &MethodDescriptorNopGet_Type) < 0) {
return -1;
}

MethodDescriptor2_Type.tp_base = &MethodDescriptorBase_Type;
if (PyType_Ready(&MethodDescriptor2_Type) < 0) {
return -1;
}
if (PyModule_AddType(m, &MethodDescriptor2_Type) < 0) {
return -1;
}

return 0;
}
Loading