mirror of
https://github.com/johndoe6345789/typthon.git
synced 2026-04-24 21:55:26 +00:00
Fixed several macros and constants that should not have been renamed: - _Py_CAST, _Py_NULL, _Py_RVALUE (internal utility macros) - Py_UNUSED (unused parameter macro) - Py_EQ, Py_NE, Py_LT, Py_LE, Py_GT, Py_GE (comparison constants) - Py_RETURN_* macros (NONE, TRUE, FALSE, NOTIMPLEMENTED, RICHCOMPARE) - Py_READONLY, Py_ULL, Py_CONTEXT_SWITCHED - TyGC_Head in generated clinic files Build is still in progress with some remaining issues to resolve. Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com>
522 lines
12 KiB
C
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 = {
|
|
PyVarObject_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);
|
|
}
|