| 
									
										
										
										
											2024-12-20 16:52:20 +00:00
										 |  |  | #include "Python.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-10 14:06:56 +00:00
										 |  |  | #include "pycore_object.h"
 | 
					
						
							| 
									
										
										
										
											2024-12-20 16:52:20 +00:00
										 |  |  | #include "pycore_stackref.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if !defined(Py_GIL_DISABLED) && defined(Py_STACKREF_DEBUG)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if SIZEOF_VOID_P < 8
 | 
					
						
							|  |  |  | #error "Py_STACKREF_DEBUG requires 64 bit machine"
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "pycore_interp.h"
 | 
					
						
							|  |  |  | #include "pycore_hashtable.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef struct _table_entry { | 
					
						
							|  |  |  |     PyObject *obj; | 
					
						
							|  |  |  |     const char *classname; | 
					
						
							|  |  |  |     const char *filename; | 
					
						
							|  |  |  |     int linenumber; | 
					
						
							|  |  |  |     const char *filename_borrow; | 
					
						
							|  |  |  |     int linenumber_borrow; | 
					
						
							|  |  |  | } TableEntry; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | TableEntry * | 
					
						
							|  |  |  | make_table_entry(PyObject *obj, const char *filename, int linenumber) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     TableEntry *result = malloc(sizeof(TableEntry)); | 
					
						
							|  |  |  |     if (result == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     result->obj = obj; | 
					
						
							|  |  |  |     result->classname = Py_TYPE(obj)->tp_name; | 
					
						
							|  |  |  |     result->filename = filename; | 
					
						
							|  |  |  |     result->linenumber = linenumber; | 
					
						
							|  |  |  |     result->filename_borrow = NULL; | 
					
						
							| 
									
										
										
										
											2025-05-11 10:16:28 +02:00
										 |  |  |     result->linenumber_borrow = 0; | 
					
						
							| 
									
										
										
										
											2024-12-20 16:52:20 +00:00
										 |  |  |     return result; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PyObject * | 
					
						
							|  |  |  | _Py_stackref_get_object(_PyStackRef ref) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (ref.index == 0) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     PyInterpreterState *interp = PyInterpreterState_Get(); | 
					
						
							|  |  |  |     assert(interp != NULL); | 
					
						
							|  |  |  |     if (ref.index >= interp->next_stackref) { | 
					
						
							|  |  |  |         _Py_FatalErrorFormat(__func__, "Garbled stack ref with ID %" PRIu64 "\n", ref.index); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-03-05 14:00:42 +00:00
										 |  |  |     TableEntry *entry = _Py_hashtable_get(interp->open_stackrefs_table, (void *)ref.index); | 
					
						
							| 
									
										
										
										
											2024-12-20 16:52:20 +00:00
										 |  |  |     if (entry == NULL) { | 
					
						
							|  |  |  |         _Py_FatalErrorFormat(__func__, "Accessing closed stack ref with ID %" PRIu64 "\n", ref.index); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return entry->obj; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-03 09:40:37 +01:00
										 |  |  | int | 
					
						
							|  |  |  | PyStackRef_Is(_PyStackRef a, _PyStackRef b) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return _Py_stackref_get_object(a) == _Py_stackref_get_object(b); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-20 16:52:20 +00:00
										 |  |  | PyObject * | 
					
						
							| 
									
										
										
										
											2025-03-05 14:00:42 +00:00
										 |  |  | _Py_stackref_close(_PyStackRef ref, const char *filename, int linenumber) | 
					
						
							| 
									
										
										
										
											2024-12-20 16:52:20 +00:00
										 |  |  | { | 
					
						
							|  |  |  |     PyInterpreterState *interp = PyInterpreterState_Get(); | 
					
						
							|  |  |  |     if (ref.index >= interp->next_stackref) { | 
					
						
							| 
									
										
										
										
											2025-03-05 14:00:42 +00:00
										 |  |  |         _Py_FatalErrorFormat(__func__, "Invalid StackRef with ID %" PRIu64 " at %s:%d\n", (void *)ref.index, filename, linenumber); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-20 16:52:20 +00:00
										 |  |  |     } | 
					
						
							|  |  |  |     PyObject *obj; | 
					
						
							| 
									
										
										
										
											2025-04-29 18:00:35 +01:00
										 |  |  |     if (ref.index < INITIAL_STACKREF_INDEX) { | 
					
						
							| 
									
										
										
										
											2025-04-04 17:59:36 +01:00
										 |  |  |         if (ref.index == 0) { | 
					
						
							|  |  |  |             _Py_FatalErrorFormat(__func__, "Passing NULL to PyStackRef_CLOSE at %s:%d\n", filename, linenumber); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2024-12-20 16:52:20 +00:00
										 |  |  |         // Pre-allocated reference to None, False or True -- Do not clear
 | 
					
						
							| 
									
										
										
										
											2025-03-05 14:00:42 +00:00
										 |  |  |         TableEntry *entry = _Py_hashtable_get(interp->open_stackrefs_table, (void *)ref.index); | 
					
						
							| 
									
										
										
										
											2024-12-20 16:52:20 +00:00
										 |  |  |         obj = entry->obj; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							| 
									
										
										
										
											2025-03-05 14:00:42 +00:00
										 |  |  |         TableEntry *entry = _Py_hashtable_steal(interp->open_stackrefs_table, (void *)ref.index); | 
					
						
							| 
									
										
										
										
											2024-12-20 16:52:20 +00:00
										 |  |  |         if (entry == NULL) { | 
					
						
							| 
									
										
										
										
											2025-03-05 14:00:42 +00:00
										 |  |  | #ifdef Py_STACKREF_CLOSE_DEBUG
 | 
					
						
							|  |  |  |             entry = _Py_hashtable_get(interp->closed_stackrefs_table, (void *)ref.index); | 
					
						
							|  |  |  |             if (entry != NULL) { | 
					
						
							|  |  |  |                 _Py_FatalErrorFormat(__func__, | 
					
						
							|  |  |  |                     "Double close of ref ID %" PRIu64 " at %s:%d. Referred to instance of %s at %p. Closed at %s:%d\n", | 
					
						
							|  |  |  |                     (void *)ref.index, filename, linenumber, entry->classname, entry->obj, entry->filename, entry->linenumber); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2024-12-20 16:52:20 +00:00
										 |  |  |             _Py_FatalErrorFormat(__func__, "Invalid StackRef with ID %" PRIu64 "\n", (void *)ref.index); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         obj = entry->obj; | 
					
						
							|  |  |  |         free(entry); | 
					
						
							| 
									
										
										
										
											2025-03-05 14:00:42 +00:00
										 |  |  | #ifdef Py_STACKREF_CLOSE_DEBUG
 | 
					
						
							|  |  |  |         TableEntry *close_entry = make_table_entry(obj, filename, linenumber); | 
					
						
							|  |  |  |         if (close_entry == NULL) { | 
					
						
							|  |  |  |             Py_FatalError("No memory left for stackref debug table"); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (_Py_hashtable_set(interp->closed_stackrefs_table, (void *)ref.index, close_entry) < 0) { | 
					
						
							|  |  |  |             Py_FatalError("No memory left for stackref debug table"); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2024-12-20 16:52:20 +00:00
										 |  |  |     } | 
					
						
							|  |  |  |     return obj; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | _PyStackRef | 
					
						
							|  |  |  | _Py_stackref_create(PyObject *obj, const char *filename, int linenumber) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (obj == NULL) { | 
					
						
							|  |  |  |         Py_FatalError("Cannot create a stackref for NULL"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     PyInterpreterState *interp = PyInterpreterState_Get(); | 
					
						
							| 
									
										
										
										
											2025-04-29 18:00:35 +01:00
										 |  |  |     uint64_t new_id = interp->next_stackref; | 
					
						
							|  |  |  |     interp->next_stackref = new_id + 2; | 
					
						
							| 
									
										
										
										
											2024-12-20 16:52:20 +00:00
										 |  |  |     TableEntry *entry = make_table_entry(obj, filename, linenumber); | 
					
						
							|  |  |  |     if (entry == NULL) { | 
					
						
							|  |  |  |         Py_FatalError("No memory left for stackref debug table"); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-03-05 14:00:42 +00:00
										 |  |  |     if (_Py_hashtable_set(interp->open_stackrefs_table, (void *)new_id, entry) < 0) { | 
					
						
							| 
									
										
										
										
											2024-12-20 16:52:20 +00:00
										 |  |  |         Py_FatalError("No memory left for stackref debug table"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return (_PyStackRef){ .index = new_id }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | _Py_stackref_record_borrow(_PyStackRef ref, const char *filename, int linenumber) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2025-04-29 18:00:35 +01:00
										 |  |  |     if (ref.index < INITIAL_STACKREF_INDEX) { | 
					
						
							| 
									
										
										
										
											2024-12-20 16:52:20 +00:00
										 |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     PyInterpreterState *interp = PyInterpreterState_Get(); | 
					
						
							| 
									
										
										
										
											2025-03-05 14:00:42 +00:00
										 |  |  |     TableEntry *entry = _Py_hashtable_get(interp->open_stackrefs_table, (void *)ref.index); | 
					
						
							| 
									
										
										
										
											2024-12-20 16:52:20 +00:00
										 |  |  |     if (entry == NULL) { | 
					
						
							| 
									
										
										
										
											2025-03-05 14:00:42 +00:00
										 |  |  | #ifdef Py_STACKREF_CLOSE_DEBUG
 | 
					
						
							|  |  |  |         entry = _Py_hashtable_get(interp->closed_stackrefs_table, (void *)ref.index); | 
					
						
							|  |  |  |         if (entry != NULL) { | 
					
						
							|  |  |  |             _Py_FatalErrorFormat(__func__, | 
					
						
							|  |  |  |                 "Borrow of closed ref ID %" PRIu64 " at %s:%d. Referred to instance of %s at %p. Closed at %s:%d\n", | 
					
						
							|  |  |  |                 (void *)ref.index, filename, linenumber, entry->classname, entry->obj, entry->filename, entry->linenumber); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |         _Py_FatalErrorFormat(__func__, "Invalid StackRef with ID %" PRIu64 " at %s:%d\n", (void *)ref.index, filename, linenumber); | 
					
						
							| 
									
										
										
										
											2024-12-20 16:52:20 +00:00
										 |  |  |     } | 
					
						
							|  |  |  |     entry->filename_borrow = filename; | 
					
						
							|  |  |  |     entry->linenumber_borrow = linenumber; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | _Py_stackref_associate(PyInterpreterState *interp, PyObject *obj, _PyStackRef ref) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2025-04-29 18:00:35 +01:00
										 |  |  |     assert(ref.index < INITIAL_STACKREF_INDEX); | 
					
						
							| 
									
										
										
										
											2024-12-20 16:52:20 +00:00
										 |  |  |     TableEntry *entry = make_table_entry(obj, "builtin-object", 0); | 
					
						
							|  |  |  |     if (entry == NULL) { | 
					
						
							|  |  |  |         Py_FatalError("No memory left for stackref debug table"); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-03-05 14:00:42 +00:00
										 |  |  |     if (_Py_hashtable_set(interp->open_stackrefs_table, (void *)ref.index, (void *)entry) < 0) { | 
					
						
							| 
									
										
										
										
											2024-12-20 16:52:20 +00:00
										 |  |  |         Py_FatalError("No memory left for stackref debug table"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | report_leak(_Py_hashtable_t *ht, const void *key, const void *value, void *leak) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     TableEntry *entry = (TableEntry *)value; | 
					
						
							|  |  |  |     if (!_Py_IsStaticImmortal(entry->obj)) { | 
					
						
							|  |  |  |         *(int *)leak = 1; | 
					
						
							|  |  |  |         printf("Stackref leak. Refers to instance of %s at %p. Created at %s:%d", | 
					
						
							|  |  |  |                entry->classname, entry->obj, entry->filename, entry->linenumber); | 
					
						
							|  |  |  |         if (entry->filename_borrow != NULL) { | 
					
						
							|  |  |  |             printf(". Last borrow at %s:%d",entry->filename_borrow, entry->linenumber_borrow); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         printf("\n"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | _Py_stackref_report_leaks(PyInterpreterState *interp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int leak = 0; | 
					
						
							| 
									
										
										
										
											2025-03-05 14:00:42 +00:00
										 |  |  |     _Py_hashtable_foreach(interp->open_stackrefs_table, report_leak, &leak); | 
					
						
							| 
									
										
										
										
											2024-12-20 16:52:20 +00:00
										 |  |  |     if (leak) { | 
					
						
							| 
									
										
										
										
											2025-03-10 14:06:56 +00:00
										 |  |  |         fflush(stdout); | 
					
						
							| 
									
										
										
										
											2024-12-20 16:52:20 +00:00
										 |  |  |         Py_FatalError("Stackrefs leaked."); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-10 14:06:56 +00:00
										 |  |  | void | 
					
						
							| 
									
										
										
										
											2025-04-03 09:40:37 +01:00
										 |  |  | _PyStackRef_CLOSE_SPECIALIZED(_PyStackRef ref, destructor destruct, const char *filename, int linenumber) | 
					
						
							| 
									
										
										
										
											2025-03-10 14:06:56 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-04-03 09:40:37 +01:00
										 |  |  |     PyObject *obj = _Py_stackref_close(ref, filename, linenumber); | 
					
						
							| 
									
										
										
										
											2025-03-10 14:06:56 +00:00
										 |  |  |     _Py_DECREF_SPECIALIZED(obj, destruct); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-29 18:00:35 +01:00
										 |  |  | _PyStackRef PyStackRef_TagInt(intptr_t i) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return (_PyStackRef){ .index = (i << 1) + 1 }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | intptr_t | 
					
						
							|  |  |  | PyStackRef_UntagInt(_PyStackRef i) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     assert(PyStackRef_IsTaggedInt(i)); | 
					
						
							|  |  |  |     intptr_t val = (intptr_t)i.index; | 
					
						
							|  |  |  |     return Py_ARITHMETIC_RIGHT_SHIFT(intptr_t, val, 1); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool | 
					
						
							|  |  |  | PyStackRef_IsNullOrInt(_PyStackRef ref) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return PyStackRef_IsNull(ref) || PyStackRef_IsTaggedInt(ref); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-20 16:52:20 +00:00
										 |  |  | #endif
 |