mirror of
https://github.com/python/cpython.git
synced 2026-10-28 10:25:05 +08:00
If Py_MIN/MAX/ABS() macros are called on an existing "_x" or "_y" variable name, the external variable is used instead of macro local variable. So use more unique names in the macros to avoid the issue. Add test_min_max_abs_macros() to _testlimitedcapi.
390 lines
9.8 KiB
C
390 lines
9.8 KiB
C
#include "pyconfig.h" // Py_GIL_DISABLED
|
|
#ifdef Py_GIL_DISABLED
|
|
# define Py_TARGET_ABI3T 0x030f0000
|
|
#else
|
|
// Need limited C API version 3.13 for Py_GetConstant()
|
|
# define Py_LIMITED_API 0x030d0000
|
|
#endif
|
|
|
|
#include "parts.h"
|
|
#include "util.h"
|
|
|
|
|
|
/* Test Py_GetConstant() */
|
|
static PyObject *
|
|
get_constant(PyObject *Py_UNUSED(module), PyObject *args)
|
|
{
|
|
int constant_id;
|
|
if (!PyArg_ParseTuple(args, "i", &constant_id)) {
|
|
return NULL;
|
|
}
|
|
|
|
PyObject *obj = Py_GetConstant(constant_id);
|
|
if (obj == NULL) {
|
|
assert(PyErr_Occurred());
|
|
return NULL;
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
|
|
/* Test Py_GetConstantBorrowed() */
|
|
static PyObject *
|
|
get_constant_borrowed(PyObject *Py_UNUSED(module), PyObject *args)
|
|
{
|
|
int constant_id;
|
|
if (!PyArg_ParseTuple(args, "i", &constant_id)) {
|
|
return NULL;
|
|
}
|
|
|
|
PyObject *obj = Py_GetConstantBorrowed(constant_id);
|
|
if (obj == NULL) {
|
|
assert(PyErr_Occurred());
|
|
return NULL;
|
|
}
|
|
return Py_NewRef(obj);
|
|
}
|
|
|
|
|
|
/* Test constants */
|
|
static PyObject *
|
|
test_constants(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
|
|
{
|
|
// Test that implementation of constants in the limited C API:
|
|
// check that the C code compiles.
|
|
//
|
|
// Test also that constants and Py_GetConstant() return the same
|
|
// objects.
|
|
assert(Py_None == Py_GetConstant(Py_CONSTANT_NONE));
|
|
assert(Py_False == Py_GetConstant(Py_CONSTANT_FALSE));
|
|
assert(Py_True == Py_GetConstant(Py_CONSTANT_TRUE));
|
|
assert(Py_Ellipsis == Py_GetConstant(Py_CONSTANT_ELLIPSIS));
|
|
assert(Py_NotImplemented == Py_GetConstant(Py_CONSTANT_NOT_IMPLEMENTED));
|
|
// Other constants are tested in test_capi.test_object
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
|
|
static PyObject *
|
|
_test_incref(PyObject *ob)
|
|
{
|
|
return Py_NewRef(ob);
|
|
}
|
|
|
|
static PyObject *
|
|
test_xincref_doesnt_leak(PyObject *ob, PyObject *Py_UNUSED(ignored))
|
|
{
|
|
PyObject *obj = PyLong_FromLong(0);
|
|
Py_XINCREF(_test_incref(obj));
|
|
Py_DECREF(obj);
|
|
Py_DECREF(obj);
|
|
Py_DECREF(obj);
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
|
|
static PyObject *
|
|
test_incref_doesnt_leak(PyObject *ob, PyObject *Py_UNUSED(ignored))
|
|
{
|
|
PyObject *obj = PyLong_FromLong(0);
|
|
Py_INCREF(_test_incref(obj));
|
|
Py_DECREF(obj);
|
|
Py_DECREF(obj);
|
|
Py_DECREF(obj);
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
|
|
static PyObject *
|
|
test_xdecref_doesnt_leak(PyObject *ob, PyObject *Py_UNUSED(ignored))
|
|
{
|
|
Py_XDECREF(PyLong_FromLong(0));
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
|
|
static PyObject *
|
|
test_decref_doesnt_leak(PyObject *ob, PyObject *Py_UNUSED(ignored))
|
|
{
|
|
Py_DECREF(PyLong_FromLong(0));
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
|
|
static PyObject *
|
|
test_incref_decref_API(PyObject *ob, PyObject *Py_UNUSED(ignored))
|
|
{
|
|
PyObject *obj = PyLong_FromLong(0);
|
|
Py_IncRef(obj);
|
|
Py_DecRef(obj);
|
|
Py_DecRef(obj);
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
|
|
// Test Py_CLEAR() macro
|
|
static PyObject*
|
|
test_py_clear(PyObject *self, PyObject *Py_UNUSED(ignored))
|
|
{
|
|
// simple case with a variable
|
|
PyObject *obj = PyList_New(0);
|
|
if (obj == NULL) {
|
|
return NULL;
|
|
}
|
|
Py_CLEAR(obj);
|
|
assert(obj == NULL);
|
|
|
|
// gh-98724: complex case, Py_CLEAR() argument has a side effect
|
|
PyObject* array[1];
|
|
array[0] = PyList_New(0);
|
|
if (array[0] == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
PyObject **p = array;
|
|
Py_CLEAR(*p++);
|
|
assert(array[0] == NULL);
|
|
assert(p == array + 1);
|
|
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
|
|
// Test Py_SETREF() and Py_XSETREF() macros, similar to test_py_clear()
|
|
static PyObject*
|
|
test_py_setref(PyObject *self, PyObject *Py_UNUSED(ignored))
|
|
{
|
|
// Py_SETREF() simple case with a variable
|
|
PyObject *obj = PyList_New(0);
|
|
if (obj == NULL) {
|
|
return NULL;
|
|
}
|
|
Py_SETREF(obj, NULL);
|
|
assert(obj == NULL);
|
|
|
|
// Py_XSETREF() simple case with a variable
|
|
PyObject *obj2 = PyList_New(0);
|
|
if (obj2 == NULL) {
|
|
return NULL;
|
|
}
|
|
Py_XSETREF(obj2, NULL);
|
|
assert(obj2 == NULL);
|
|
// test Py_XSETREF() when the argument is NULL
|
|
Py_XSETREF(obj2, NULL);
|
|
assert(obj2 == NULL);
|
|
|
|
// gh-98724: complex case, Py_SETREF() argument has a side effect
|
|
PyObject* array[1];
|
|
array[0] = PyList_New(0);
|
|
if (array[0] == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
PyObject **p = array;
|
|
Py_SETREF(*p++, NULL);
|
|
assert(array[0] == NULL);
|
|
assert(p == array + 1);
|
|
|
|
// gh-98724: complex case, Py_XSETREF() argument has a side effect
|
|
PyObject* array2[1];
|
|
array2[0] = PyList_New(0);
|
|
if (array2[0] == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
PyObject **p2 = array2;
|
|
Py_XSETREF(*p2++, NULL);
|
|
assert(array2[0] == NULL);
|
|
assert(p2 == array2 + 1);
|
|
|
|
// test Py_XSETREF() when the argument is NULL
|
|
p2 = array2;
|
|
Py_XSETREF(*p2++, NULL);
|
|
assert(array2[0] == NULL);
|
|
assert(p2 == array2 + 1);
|
|
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
|
|
#define TEST_REFCOUNT() \
|
|
do { \
|
|
PyObject *obj = PyList_New(0); \
|
|
if (obj == NULL) { \
|
|
return NULL; \
|
|
} \
|
|
assert(Py_REFCNT(obj) == 1); \
|
|
\
|
|
/* test Py_NewRef() */ \
|
|
PyObject *ref = Py_NewRef(obj); \
|
|
assert(ref == obj); \
|
|
assert(Py_REFCNT(obj) == 2); \
|
|
Py_DECREF(ref); \
|
|
\
|
|
/* test Py_XNewRef() */ \
|
|
PyObject *xref = Py_XNewRef(obj); \
|
|
assert(xref == obj); \
|
|
assert(Py_REFCNT(obj) == 2); \
|
|
Py_DECREF(xref); \
|
|
\
|
|
assert(Py_XNewRef(NULL) == NULL); \
|
|
\
|
|
Py_DECREF(obj); \
|
|
Py_RETURN_NONE; \
|
|
} while (0)
|
|
|
|
|
|
// Test Py_NewRef() and Py_XNewRef() macros
|
|
static PyObject*
|
|
test_refcount_macros(PyObject *self, PyObject *Py_UNUSED(ignored))
|
|
{
|
|
TEST_REFCOUNT();
|
|
}
|
|
|
|
|
|
// Test Py_MIN(), Py_MAX() and Py_ABS() macros.
|
|
// On GCC/clang, they are implemented as a statement expression to only
|
|
// evaluate each argument only once.
|
|
static PyObject*
|
|
test_min_max_abs_macros(PyObject *self, PyObject *Py_UNUSED(ignored))
|
|
{
|
|
int x = 5, y = 7, z = -11;
|
|
|
|
// Simple usage
|
|
assert(Py_MIN(x, y) == 5);
|
|
assert(Py_MAX(x, y) == 7);
|
|
assert(Py_ABS(z) == 11);
|
|
|
|
// Combined macros
|
|
assert(Py_MIN(x, Py_MIN(y, z)) == -11);
|
|
assert(Py_MAX(x, Py_MAX(y, z)) == 7);
|
|
assert(Py_MIN(x, Py_MAX(y, z)) == 5);
|
|
assert(Py_MAX(x, Py_MIN(y, z)) == 5);
|
|
assert(Py_ABS(Py_ABS(z)) == 11);
|
|
|
|
// A few more tests
|
|
assert(Py_MIN(2, Py_MIN(6, 12)) == 2);
|
|
assert(Py_MAX(2, Py_MAX(6, 12)) == 12);
|
|
assert(Py_MIN(2, Py_MAX(6, 12)) == 2);
|
|
assert(Py_MAX(2, Py_MIN(6, 12)) == 6);
|
|
assert(Py_MIN(Py_MIN(2, 6), 12) == 2);
|
|
assert(Py_MAX(Py_MAX(2, 6), 12) == 12);
|
|
assert(Py_MIN(Py_MAX(2, 6), 12) == 6);
|
|
assert(Py_MAX(Py_MIN(2, 6), 12) == 12);
|
|
assert(Py_ABS(Py_ABS(12)) == 12);
|
|
assert(Py_ABS(Py_ABS(12)) == 12);
|
|
|
|
// Integer limits
|
|
assert(Py_MIN(123, INT_MIN) == INT_MIN);
|
|
assert(Py_MAX(123, INT_MAX) == INT_MAX);
|
|
assert(Py_ABS(INT_MAX) == INT_MAX);
|
|
// Do not tests Py_ABS(INT_MIN), since the behavior is documented.
|
|
// This limitation is documented in Py_ABS() documentation.
|
|
|
|
#if ((defined(__GNUC__) || defined(__clang__)) \
|
|
&& defined(_Py_TYPEOF) && !defined(__cplusplus))
|
|
// Check that arguments are only evaluated once
|
|
int a = 5, b = 7, c = -11;
|
|
assert(Py_MIN(++a, ++b) == 6);
|
|
assert(a == 6);
|
|
assert(b == 8);
|
|
assert(Py_MAX(++a, ++b) == 9);
|
|
assert(a == 7);
|
|
assert(b == 9);
|
|
assert(Py_ABS(--c) == 12);
|
|
assert(c == -12);
|
|
#endif
|
|
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
#undef Py_NewRef
|
|
#undef Py_XNewRef
|
|
|
|
// Test Py_NewRef() and Py_XNewRef() functions, after undefining macros.
|
|
static PyObject*
|
|
test_refcount_funcs(PyObject *self, PyObject *Py_UNUSED(ignored))
|
|
{
|
|
TEST_REFCOUNT();
|
|
}
|
|
|
|
|
|
// Test Py_Is() function
|
|
#define TEST_PY_IS() \
|
|
do { \
|
|
PyObject *o_none = Py_None; \
|
|
PyObject *o_true = Py_True; \
|
|
PyObject *o_false = Py_False; \
|
|
PyObject *obj = PyList_New(0); \
|
|
if (obj == NULL) { \
|
|
return NULL; \
|
|
} \
|
|
\
|
|
/* test Py_Is() */ \
|
|
assert(Py_Is(obj, obj)); \
|
|
assert(!Py_Is(obj, o_none)); \
|
|
\
|
|
/* test Py_None */ \
|
|
assert(Py_Is(o_none, o_none)); \
|
|
assert(!Py_Is(obj, o_none)); \
|
|
\
|
|
/* test Py_True */ \
|
|
assert(Py_Is(o_true, o_true)); \
|
|
assert(!Py_Is(o_false, o_true)); \
|
|
assert(!Py_Is(obj, o_true)); \
|
|
\
|
|
/* test Py_False */ \
|
|
assert(Py_Is(o_false, o_false)); \
|
|
assert(!Py_Is(o_true, o_false)); \
|
|
assert(!Py_Is(obj, o_false)); \
|
|
\
|
|
Py_DECREF(obj); \
|
|
Py_RETURN_NONE; \
|
|
} while (0)
|
|
|
|
// Test Py_Is() macro
|
|
static PyObject*
|
|
test_py_is_macros(PyObject *self, PyObject *Py_UNUSED(ignored))
|
|
{
|
|
TEST_PY_IS();
|
|
}
|
|
|
|
#undef Py_Is
|
|
|
|
// Test Py_Is() function, after undefining its macro.
|
|
static PyObject*
|
|
test_py_is_funcs(PyObject *self, PyObject *Py_UNUSED(ignored))
|
|
{
|
|
TEST_PY_IS();
|
|
}
|
|
|
|
|
|
static PyMethodDef test_methods[] = {
|
|
{"get_constant", get_constant, METH_VARARGS},
|
|
{"get_constant_borrowed", get_constant_borrowed, METH_VARARGS},
|
|
{"test_constants", test_constants, METH_NOARGS},
|
|
{"test_xincref_doesnt_leak",test_xincref_doesnt_leak, METH_NOARGS},
|
|
{"test_incref_doesnt_leak", test_incref_doesnt_leak, METH_NOARGS},
|
|
{"test_xdecref_doesnt_leak",test_xdecref_doesnt_leak, METH_NOARGS},
|
|
{"test_decref_doesnt_leak", test_decref_doesnt_leak, METH_NOARGS},
|
|
{"test_incref_decref_API", test_incref_decref_API, METH_NOARGS},
|
|
{"test_py_clear", test_py_clear, METH_NOARGS},
|
|
{"test_py_setref", test_py_setref, METH_NOARGS},
|
|
{"test_refcount_macros", test_refcount_macros, METH_NOARGS},
|
|
{"test_refcount_funcs", test_refcount_funcs, METH_NOARGS},
|
|
{"test_min_max_abs_macros", test_min_max_abs_macros, METH_NOARGS},
|
|
{"test_py_is_macros", test_py_is_macros, METH_NOARGS},
|
|
{"test_py_is_funcs", test_py_is_funcs, METH_NOARGS},
|
|
{NULL},
|
|
};
|
|
|
|
int
|
|
_PyTestLimitedCAPI_Init_Object(PyObject *m)
|
|
{
|
|
if (PyModule_AddFunctions(m, test_methods) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|