Files
typthon/Modules/_testcapi/object.c
copilot-swe-agent[bot] 71cf7bf14f Fix more missed Py_ patterns - opcode, thread, exception
Fixed additional patterns:
- _PyOpcode_* → _TyOpcode_* (all opcode metadata)
- _PyUOpName → _TyUOpName
- _PyFunction_* → _TyFunction_*
- _PyListIterObject → _TyListIterObject
- _Py_T_OBJECT → _Ty_T_OBJECT
- Py_BEGIN_ALLOW_THREADS, Py_END_ALLOW_THREADS → Ty_*
- PyDoc_STRVAR, PyDoc_STR → TyDoc_*
- PyInterpreterState, PyThread_*, PyTime_t → Ty*
- PyStructSequence_* → TyStructSequence_*
- PyLockStatus → TyLockStatus
- PyVarObject_HEAD_INIT → TyVarObject_HEAD_INIT
- PyBaseExceptionObject → TyBaseExceptionObject
- Fixed _PyExc_ → _TyExc_ in exception macros

Build is progressing further.

Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com>
2025-12-29 18:27:36 +00:00

522 lines
12 KiB
C

#include "parts.h"
#include "util.h"
static TyObject *
call_pyobject_print(TyObject *self, TyObject * args)
{
TyObject *object;
TyObject *filename;
TyObject *print_raw;
FILE *fp;
int flags = 0;
if (!TyArg_UnpackTuple(args, "call_pyobject_print", 3, 3,
&object, &filename, &print_raw)) {
return NULL;
}
fp = Ty_fopen(filename, "w+");
if (Ty_IsTrue(print_raw)) {
flags = Ty_PRINT_RAW;
}
if (PyObject_Print(object, fp, flags) < 0) {
fclose(fp);
return NULL;
}
fclose(fp);
Py_RETURN_NONE;
}
static TyObject *
pyobject_print_null(TyObject *self, TyObject *args)
{
TyObject *filename;
FILE *fp;
if (!TyArg_UnpackTuple(args, "call_pyobject_print", 1, 1, &filename)) {
return NULL;
}
fp = Ty_fopen(filename, "w+");
if (PyObject_Print(NULL, fp, 0) < 0) {
fclose(fp);
return NULL;
}
fclose(fp);
Py_RETURN_NONE;
}
static TyObject *
pyobject_print_noref_object(TyObject *self, TyObject *args)
{
TyObject *test_string;
TyObject *filename;
FILE *fp;
char correct_string[100];
test_string = TyUnicode_FromString("Spam spam spam");
Ty_SET_REFCNT(test_string, 0);
TyOS_snprintf(correct_string, 100, "<refcnt %zd at %p>",
Ty_REFCNT(test_string), (void *)test_string);
if (!TyArg_UnpackTuple(args, "call_pyobject_print", 1, 1, &filename)) {
return NULL;
}
fp = Ty_fopen(filename, "w+");
if (PyObject_Print(test_string, fp, 0) < 0){
fclose(fp);
Ty_SET_REFCNT(test_string, 1);
Ty_DECREF(test_string);
return NULL;
}
fclose(fp);
Ty_SET_REFCNT(test_string, 1);
Ty_DECREF(test_string);
return TyUnicode_FromString(correct_string);
}
static TyObject *
pyobject_print_os_error(TyObject *self, TyObject *args)
{
TyObject *test_string;
TyObject *filename;
FILE *fp;
test_string = TyUnicode_FromString("Spam spam spam");
if (!TyArg_UnpackTuple(args, "call_pyobject_print", 1, 1, &filename)) {
return NULL;
}
// open file in read mode to induce OSError
fp = Ty_fopen(filename, "r");
if (PyObject_Print(test_string, fp, 0) < 0) {
fclose(fp);
Ty_DECREF(test_string);
return NULL;
}
fclose(fp);
Ty_DECREF(test_string);
Py_RETURN_NONE;
}
static TyObject *
pyobject_clear_weakrefs_no_callbacks(TyObject *self, TyObject *obj)
{
PyUnstable_Object_ClearWeakRefsNoCallbacks(obj);
Py_RETURN_NONE;
}
static TyObject *
pyobject_enable_deferred_refcount(TyObject *self, TyObject *obj)
{
int result = PyUnstable_Object_EnableDeferredRefcount(obj);
return TyLong_FromLong(result);
}
static TyObject *
pyobject_is_unique_temporary(TyObject *self, TyObject *obj)
{
int result = PyUnstable_Object_IsUniqueReferencedTemporary(obj);
return TyLong_FromLong(result);
}
static int MyObject_dealloc_called = 0;
static void
MyObject_dealloc(TyObject *op)
{
// PyUnstable_TryIncRef should return 0 if object is being deallocated
assert(Ty_REFCNT(op) == 0);
assert(!PyUnstable_TryIncRef(op));
assert(Ty_REFCNT(op) == 0);
MyObject_dealloc_called++;
Ty_TYPE(op)->tp_free(op);
}
static TyTypeObject MyType = {
TyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "MyType",
.tp_basicsize = sizeof(TyObject),
.tp_dealloc = MyObject_dealloc,
};
static TyObject *
test_py_try_inc_ref(TyObject *self, TyObject *unused)
{
if (TyType_Ready(&MyType) < 0) {
return NULL;
}
MyObject_dealloc_called = 0;
TyObject *op = PyObject_New(TyObject, &MyType);
if (op == NULL) {
return NULL;
}
PyUnstable_EnableTryIncRef(op);
#ifdef Ty_GIL_DISABLED
// PyUnstable_EnableTryIncRef sets the shared flags to
// `_Ty_REF_MAYBE_WEAKREF` if the flags are currently zero to ensure that
// the shared reference count is merged on deallocation.
assert((op->ob_ref_shared & _Ty_REF_SHARED_FLAG_MASK) >= _Ty_REF_MAYBE_WEAKREF);
#endif
if (!PyUnstable_TryIncRef(op)) {
TyErr_SetString(TyExc_AssertionError, "PyUnstable_TryIncRef failed");
Ty_DECREF(op);
return NULL;
}
Ty_DECREF(op); // undo try-incref
Ty_DECREF(op); // dealloc
assert(MyObject_dealloc_called == 1);
Py_RETURN_NONE;
}
static TyObject *
_test_incref(TyObject *ob)
{
return Ty_NewRef(ob);
}
static TyObject *
test_xincref_doesnt_leak(TyObject *ob, TyObject *Py_UNUSED(ignored))
{
TyObject *obj = TyLong_FromLong(0);
Ty_XINCREF(_test_incref(obj));
Ty_DECREF(obj);
Ty_DECREF(obj);
Ty_DECREF(obj);
Py_RETURN_NONE;
}
static TyObject *
test_incref_doesnt_leak(TyObject *ob, TyObject *Py_UNUSED(ignored))
{
TyObject *obj = TyLong_FromLong(0);
Ty_INCREF(_test_incref(obj));
Ty_DECREF(obj);
Ty_DECREF(obj);
Ty_DECREF(obj);
Py_RETURN_NONE;
}
static TyObject *
test_xdecref_doesnt_leak(TyObject *ob, TyObject *Py_UNUSED(ignored))
{
Ty_XDECREF(TyLong_FromLong(0));
Py_RETURN_NONE;
}
static TyObject *
test_decref_doesnt_leak(TyObject *ob, TyObject *Py_UNUSED(ignored))
{
Ty_DECREF(TyLong_FromLong(0));
Py_RETURN_NONE;
}
static TyObject *
test_incref_decref_API(TyObject *ob, TyObject *Py_UNUSED(ignored))
{
TyObject *obj = TyLong_FromLong(0);
Ty_IncRef(obj);
Ty_DecRef(obj);
Ty_DecRef(obj);
Py_RETURN_NONE;
}
#ifdef Ty_REF_DEBUG
static TyObject *
negative_refcount(TyObject *self, TyObject *Py_UNUSED(args))
{
TyObject *obj = TyUnicode_FromString("negative_refcount");
if (obj == NULL) {
return NULL;
}
assert(Ty_REFCNT(obj) == 1);
Ty_SET_REFCNT(obj, 0);
/* Ty_DECREF() must call _Ty_NegativeRefcount() and abort Python */
Ty_DECREF(obj);
Py_RETURN_NONE;
}
static TyObject *
decref_freed_object(TyObject *self, TyObject *Py_UNUSED(args))
{
TyObject *obj = TyUnicode_FromString("decref_freed_object");
if (obj == NULL) {
return NULL;
}
assert(Ty_REFCNT(obj) == 1);
// Deallocate the memory
Ty_DECREF(obj);
// obj is a now a dangling pointer
// gh-109496: If Python is built in debug mode, Ty_DECREF() must call
// _Ty_NegativeRefcount() and abort Python.
Ty_DECREF(obj);
Py_RETURN_NONE;
}
#endif
// Test Ty_CLEAR() macro
static TyObject*
test_py_clear(TyObject *self, TyObject *Py_UNUSED(ignored))
{
// simple case with a variable
TyObject *obj = TyList_New(0);
if (obj == NULL) {
return NULL;
}
Ty_CLEAR(obj);
assert(obj == NULL);
// gh-98724: complex case, Ty_CLEAR() argument has a side effect
TyObject* array[1];
array[0] = TyList_New(0);
if (array[0] == NULL) {
return NULL;
}
TyObject **p = array;
Ty_CLEAR(*p++);
assert(array[0] == NULL);
assert(p == array + 1);
Py_RETURN_NONE;
}
// Test Ty_SETREF() and Ty_XSETREF() macros, similar to test_py_clear()
static TyObject*
test_py_setref(TyObject *self, TyObject *Py_UNUSED(ignored))
{
// Ty_SETREF() simple case with a variable
TyObject *obj = TyList_New(0);
if (obj == NULL) {
return NULL;
}
Ty_SETREF(obj, NULL);
assert(obj == NULL);
// Ty_XSETREF() simple case with a variable
TyObject *obj2 = TyList_New(0);
if (obj2 == NULL) {
return NULL;
}
Ty_XSETREF(obj2, NULL);
assert(obj2 == NULL);
// test Ty_XSETREF() when the argument is NULL
Ty_XSETREF(obj2, NULL);
assert(obj2 == NULL);
// gh-98724: complex case, Ty_SETREF() argument has a side effect
TyObject* array[1];
array[0] = TyList_New(0);
if (array[0] == NULL) {
return NULL;
}
TyObject **p = array;
Ty_SETREF(*p++, NULL);
assert(array[0] == NULL);
assert(p == array + 1);
// gh-98724: complex case, Ty_XSETREF() argument has a side effect
TyObject* array2[1];
array2[0] = TyList_New(0);
if (array2[0] == NULL) {
return NULL;
}
TyObject **p2 = array2;
Ty_XSETREF(*p2++, NULL);
assert(array2[0] == NULL);
assert(p2 == array2 + 1);
// test Ty_XSETREF() when the argument is NULL
p2 = array2;
Ty_XSETREF(*p2++, NULL);
assert(array2[0] == NULL);
assert(p2 == array2 + 1);
Py_RETURN_NONE;
}
#define TEST_REFCOUNT() \
do { \
TyObject *obj = TyList_New(0); \
if (obj == NULL) { \
return NULL; \
} \
assert(Ty_REFCNT(obj) == 1); \
\
/* test Ty_NewRef() */ \
TyObject *ref = Ty_NewRef(obj); \
assert(ref == obj); \
assert(Ty_REFCNT(obj) == 2); \
Ty_DECREF(ref); \
\
/* test Ty_XNewRef() */ \
TyObject *xref = Ty_XNewRef(obj); \
assert(xref == obj); \
assert(Ty_REFCNT(obj) == 2); \
Ty_DECREF(xref); \
\
assert(Ty_XNewRef(NULL) == NULL); \
\
Ty_DECREF(obj); \
Py_RETURN_NONE; \
} while (0)
// Test Ty_NewRef() and Ty_XNewRef() macros
static TyObject*
test_refcount_macros(TyObject *self, TyObject *Py_UNUSED(ignored))
{
TEST_REFCOUNT();
}
#undef Ty_NewRef
#undef Ty_XNewRef
// Test Ty_NewRef() and Ty_XNewRef() functions, after undefining macros.
static TyObject*
test_refcount_funcs(TyObject *self, TyObject *Py_UNUSED(ignored))
{
TEST_REFCOUNT();
}
// Test Ty_Is() function
#define TEST_PY_IS() \
do { \
TyObject *o_none = Ty_None; \
TyObject *o_true = Ty_True; \
TyObject *o_false = Ty_False; \
TyObject *obj = TyList_New(0); \
if (obj == NULL) { \
return NULL; \
} \
\
/* test Ty_Is() */ \
assert(Ty_Is(obj, obj)); \
assert(!Ty_Is(obj, o_none)); \
\
/* test Ty_None */ \
assert(Ty_Is(o_none, o_none)); \
assert(!Ty_Is(obj, o_none)); \
\
/* test Ty_True */ \
assert(Ty_Is(o_true, o_true)); \
assert(!Ty_Is(o_false, o_true)); \
assert(!Ty_Is(obj, o_true)); \
\
/* test Ty_False */ \
assert(Ty_Is(o_false, o_false)); \
assert(!Ty_Is(o_true, o_false)); \
assert(!Ty_Is(obj, o_false)); \
\
Ty_DECREF(obj); \
Py_RETURN_NONE; \
} while (0)
// Test Ty_Is() macro
static TyObject*
test_py_is_macros(TyObject *self, TyObject *Py_UNUSED(ignored))
{
TEST_PY_IS();
}
#undef Ty_Is
// Test Ty_Is() function, after undefining its macro.
static TyObject*
test_py_is_funcs(TyObject *self, TyObject *Py_UNUSED(ignored))
{
TEST_PY_IS();
}
static TyObject *
clear_managed_dict(TyObject *self, TyObject *obj)
{
PyObject_ClearManagedDict(obj);
Py_RETURN_NONE;
}
static TyObject *
is_uniquely_referenced(TyObject *self, TyObject *op)
{
return TyBool_FromLong(PyUnstable_Object_IsUniquelyReferenced(op));
}
static TyMethodDef test_methods[] = {
{"call_pyobject_print", call_pyobject_print, METH_VARARGS},
{"pyobject_print_null", pyobject_print_null, METH_VARARGS},
{"pyobject_print_noref_object", pyobject_print_noref_object, METH_VARARGS},
{"pyobject_print_os_error", pyobject_print_os_error, METH_VARARGS},
{"pyobject_clear_weakrefs_no_callbacks", pyobject_clear_weakrefs_no_callbacks, METH_O},
{"pyobject_enable_deferred_refcount", pyobject_enable_deferred_refcount, METH_O},
{"pyobject_is_unique_temporary", pyobject_is_unique_temporary, METH_O},
{"test_py_try_inc_ref", test_py_try_inc_ref, 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},
#ifdef Ty_REF_DEBUG
{"negative_refcount", negative_refcount, METH_NOARGS},
{"decref_freed_object", decref_freed_object, METH_NOARGS},
#endif
{"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_py_is_macros", test_py_is_macros, METH_NOARGS},
{"test_py_is_funcs", test_py_is_funcs, METH_NOARGS},
{"clear_managed_dict", clear_managed_dict, METH_O, NULL},
{"is_uniquely_referenced", is_uniquely_referenced, METH_O},
{NULL},
};
int
_PyTestCapi_Init_Object(TyObject *m)
{
return TyModule_AddFunctions(m, test_methods);
}