15f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)from cpython.ref cimport PyObject, Py_INCREF, Py_DECREF, Py_XDECREF, Py_XINCREF
25f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)from cpython.exc cimport PyErr_Fetch, PyErr_Restore
35f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)from cpython.pystate cimport PyThreadState_Get
45f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
55f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)cimport cython
65f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
75f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)loglevel = 0
85f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)reflog = []
95f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)cdef log(level, action, obj, lineno):
115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if loglevel >= level:
125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        reflog.append((lineno, action, id(obj)))
135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)LOG_NONE, LOG_ALL = range(2)
155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)@cython.final
175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)cdef class Context(object):
185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    cdef readonly object name, filename
195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    cdef readonly dict refs
205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    cdef readonly list errors
215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    cdef readonly Py_ssize_t start
225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    def __cinit__(self, name, line=0, filename=None):
245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        self.name = name
255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        self.start = line
265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        self.filename = filename
275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        self.refs = {} # id -> (count, [lineno])
285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        self.errors = []
295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    cdef regref(self, obj, lineno, bint is_null):
315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        log(LOG_ALL, u'regref', u"<NULL>" if is_null else obj, lineno)
325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if is_null:
335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            self.errors.append(u"NULL argument on line %d" % lineno)
345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            return
355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        id_ = id(obj)
365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        count, linenumbers = self.refs.get(id_, (0, []))
375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        self.refs[id_] = (count + 1, linenumbers)
385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        linenumbers.append(lineno)
395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    cdef bint delref(self, obj, lineno, bint is_null) except -1:
415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # returns whether it is ok to do the decref operation
425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        log(LOG_ALL, u'delref', u"<NULL>" if is_null else obj, lineno)
435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if is_null:
445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            self.errors.append(u"NULL argument on line %d" % lineno)
455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            return False
465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        id_ = id(obj)
475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        count, linenumbers = self.refs.get(id_, (0, []))
485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if count == 0:
495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            self.errors.append(u"Too many decrefs on line %d, reference acquired on lines %r" %
505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                (lineno, linenumbers))
515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            return False
525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        elif count == 1:
535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            del self.refs[id_]
545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            return True
555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        else:
565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            self.refs[id_] = (count - 1, linenumbers)
575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            return True
585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    cdef end(self):
605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if self.refs:
615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            msg = u"References leaked:"
625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            for count, linenos in self.refs.itervalues():
635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                msg += u"\n  (%d) acquired on lines: %s" % (count, u", ".join([u"%d" % x for x in linenos]))
645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            self.errors.append(msg)
655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if self.errors:
665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            return u"\n".join([u'REFNANNY: '+error for error in self.errors])
675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        else:
685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            return None
695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)cdef void report_unraisable(object e=None):
715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    try:
725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if e is None:
735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            import sys
745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            e = sys.exc_info()[1]
755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        print u"refnanny raised an exception: %s" % e
765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    except:
775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        pass # We absolutely cannot exit with an exception
785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)# All Python operations must happen after any existing
805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)# exception has been fetched, in case we are called from
815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)# exception-handling code.
825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)cdef PyObject* SetupContext(char* funcname, int lineno, char* filename) except NULL:
845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if Context is None:
855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # Context may be None during finalize phase.
865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # In that case, we don't want to be doing anything fancy
875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # like caching and resetting exceptions.
885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        return NULL
895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    cdef (PyObject*) type = NULL, value = NULL, tb = NULL, result = NULL
905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    PyThreadState_Get()
915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    PyErr_Fetch(&type, &value, &tb)
925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    try:
935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        ctx = Context(funcname, lineno, filename)
945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        Py_INCREF(ctx)
955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        result = <PyObject*>ctx
965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    except Exception, e:
975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        report_unraisable(e)
985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    PyErr_Restore(type, value, tb)
995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return result
1005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)cdef void GOTREF(PyObject* ctx, PyObject* p_obj, int lineno):
1025f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if ctx == NULL: return
1035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    cdef (PyObject*) type = NULL, value = NULL, tb = NULL
1045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    PyErr_Fetch(&type, &value, &tb)
1055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    try:
1065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        try:
1075f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            if p_obj is NULL:
1085f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                (<Context>ctx).regref(None, lineno, True)
1095f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            else:
1105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                (<Context>ctx).regref(<object>p_obj, lineno, False)
1115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        except:
1125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            report_unraisable()
1135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    except:
1145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # __Pyx_GetException may itself raise errors
1155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        pass
1165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    PyErr_Restore(type, value, tb)
1175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)cdef int GIVEREF_and_report(PyObject* ctx, PyObject* p_obj, int lineno):
1195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if ctx == NULL: return 1
1205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    cdef (PyObject*) type = NULL, value = NULL, tb = NULL
1215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    cdef bint decref_ok = False
1225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    PyErr_Fetch(&type, &value, &tb)
1235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    try:
1245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        try:
1255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            if p_obj is NULL:
1265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                decref_ok = (<Context>ctx).delref(None, lineno, True)
1275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            else:
1285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                decref_ok = (<Context>ctx).delref(<object>p_obj, lineno, False)
1295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        except:
1305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            report_unraisable()
1315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    except:
1325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # __Pyx_GetException may itself raise errors
1335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        pass
1345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    PyErr_Restore(type, value, tb)
1355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return decref_ok
1365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)cdef void GIVEREF(PyObject* ctx, PyObject* p_obj, int lineno):
1385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    GIVEREF_and_report(ctx, p_obj, lineno)
1395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)cdef void INCREF(PyObject* ctx, PyObject* obj, int lineno):
1415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    Py_XINCREF(obj)
1425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    PyThreadState_Get()
1435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    GOTREF(ctx, obj, lineno)
1445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)cdef void DECREF(PyObject* ctx, PyObject* obj, int lineno):
1465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if GIVEREF_and_report(ctx, obj, lineno):
1475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        Py_XDECREF(obj)
1485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    PyThreadState_Get()
1495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)cdef void FinishContext(PyObject** ctx):
1515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if ctx == NULL or ctx[0] == NULL: return
1525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    cdef (PyObject*) type = NULL, value = NULL, tb = NULL
1535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    cdef object errors = None
1545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    cdef Context context
1555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    PyThreadState_Get()
1565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    PyErr_Fetch(&type, &value, &tb)
1575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    try:
1585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        try:
1595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            context = <Context>ctx[0]
1605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            errors = context.end()
1615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            if errors:
1625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                print u"%s: %s()" % (context.filename.decode('latin1'),
1635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                     context.name.decode('latin1'))
1645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                print errors
1655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            context = None
1665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        except:
1675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            report_unraisable()
1685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    except:
1695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # __Pyx_GetException may itself raise errors
1705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        pass
1715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    Py_XDECREF(ctx[0])
1725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    ctx[0] = NULL
1735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    PyErr_Restore(type, value, tb)
1745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)ctypedef struct RefNannyAPIStruct:
1765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  void (*INCREF)(PyObject*, PyObject*, int)
1775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  void (*DECREF)(PyObject*, PyObject*, int)
1785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  void (*GOTREF)(PyObject*, PyObject*, int)
1795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  void (*GIVEREF)(PyObject*, PyObject*, int)
1805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  PyObject* (*SetupContext)(char*, int, char*) except NULL
1815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  void (*FinishContext)(PyObject**)
1825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)cdef RefNannyAPIStruct api
1845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)api.INCREF = INCREF
1855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)api.DECREF =  DECREF
1865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)api.GOTREF =  GOTREF
1875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)api.GIVEREF = GIVEREF
1885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)api.SetupContext = SetupContext
1895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)api.FinishContext = FinishContext
1905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)cdef extern from "Python.h":
1925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    object PyLong_FromVoidPtr(void*)
1935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)RefNannyAPI = PyLong_FromVoidPtr(<void*>&api)
195