Index: third_party/cython/src/Cython/Runtime/refnanny.pyx |
diff --git a/third_party/cython/src/Cython/Runtime/refnanny.pyx b/third_party/cython/src/Cython/Runtime/refnanny.pyx |
new file mode 100644 |
index 0000000000000000000000000000000000000000..80ec3f4bafca5db7d97a838fb9d215e220c3a22a |
--- /dev/null |
+++ b/third_party/cython/src/Cython/Runtime/refnanny.pyx |
@@ -0,0 +1,194 @@ |
+from cpython.ref cimport PyObject, Py_INCREF, Py_DECREF, Py_XDECREF, Py_XINCREF |
+from cpython.exc cimport PyErr_Fetch, PyErr_Restore |
+from cpython.pystate cimport PyThreadState_Get |
+ |
+cimport cython |
+ |
+loglevel = 0 |
+reflog = [] |
+ |
+cdef log(level, action, obj, lineno): |
+ if loglevel >= level: |
+ reflog.append((lineno, action, id(obj))) |
+ |
+LOG_NONE, LOG_ALL = range(2) |
+ |
+@cython.final |
+cdef class Context(object): |
+ cdef readonly object name, filename |
+ cdef readonly dict refs |
+ cdef readonly list errors |
+ cdef readonly Py_ssize_t start |
+ |
+ def __cinit__(self, name, line=0, filename=None): |
+ self.name = name |
+ self.start = line |
+ self.filename = filename |
+ self.refs = {} # id -> (count, [lineno]) |
+ self.errors = [] |
+ |
+ cdef regref(self, obj, lineno, bint is_null): |
+ log(LOG_ALL, u'regref', u"<NULL>" if is_null else obj, lineno) |
+ if is_null: |
+ self.errors.append(u"NULL argument on line %d" % lineno) |
+ return |
+ id_ = id(obj) |
+ count, linenumbers = self.refs.get(id_, (0, [])) |
+ self.refs[id_] = (count + 1, linenumbers) |
+ linenumbers.append(lineno) |
+ |
+ cdef bint delref(self, obj, lineno, bint is_null) except -1: |
+ # returns whether it is ok to do the decref operation |
+ log(LOG_ALL, u'delref', u"<NULL>" if is_null else obj, lineno) |
+ if is_null: |
+ self.errors.append(u"NULL argument on line %d" % lineno) |
+ return False |
+ id_ = id(obj) |
+ count, linenumbers = self.refs.get(id_, (0, [])) |
+ if count == 0: |
+ self.errors.append(u"Too many decrefs on line %d, reference acquired on lines %r" % |
+ (lineno, linenumbers)) |
+ return False |
+ elif count == 1: |
+ del self.refs[id_] |
+ return True |
+ else: |
+ self.refs[id_] = (count - 1, linenumbers) |
+ return True |
+ |
+ cdef end(self): |
+ if self.refs: |
+ msg = u"References leaked:" |
+ for count, linenos in self.refs.itervalues(): |
+ msg += u"\n (%d) acquired on lines: %s" % (count, u", ".join([u"%d" % x for x in linenos])) |
+ self.errors.append(msg) |
+ if self.errors: |
+ return u"\n".join([u'REFNANNY: '+error for error in self.errors]) |
+ else: |
+ return None |
+ |
+cdef void report_unraisable(object e=None): |
+ try: |
+ if e is None: |
+ import sys |
+ e = sys.exc_info()[1] |
+ print u"refnanny raised an exception: %s" % e |
+ except: |
+ pass # We absolutely cannot exit with an exception |
+ |
+# All Python operations must happen after any existing |
+# exception has been fetched, in case we are called from |
+# exception-handling code. |
+ |
+cdef PyObject* SetupContext(char* funcname, int lineno, char* filename) except NULL: |
+ if Context is None: |
+ # Context may be None during finalize phase. |
+ # In that case, we don't want to be doing anything fancy |
+ # like caching and resetting exceptions. |
+ return NULL |
+ cdef (PyObject*) type = NULL, value = NULL, tb = NULL, result = NULL |
+ PyThreadState_Get() |
+ PyErr_Fetch(&type, &value, &tb) |
+ try: |
+ ctx = Context(funcname, lineno, filename) |
+ Py_INCREF(ctx) |
+ result = <PyObject*>ctx |
+ except Exception, e: |
+ report_unraisable(e) |
+ PyErr_Restore(type, value, tb) |
+ return result |
+ |
+cdef void GOTREF(PyObject* ctx, PyObject* p_obj, int lineno): |
+ if ctx == NULL: return |
+ cdef (PyObject*) type = NULL, value = NULL, tb = NULL |
+ PyErr_Fetch(&type, &value, &tb) |
+ try: |
+ try: |
+ if p_obj is NULL: |
+ (<Context>ctx).regref(None, lineno, True) |
+ else: |
+ (<Context>ctx).regref(<object>p_obj, lineno, False) |
+ except: |
+ report_unraisable() |
+ except: |
+ # __Pyx_GetException may itself raise errors |
+ pass |
+ PyErr_Restore(type, value, tb) |
+ |
+cdef int GIVEREF_and_report(PyObject* ctx, PyObject* p_obj, int lineno): |
+ if ctx == NULL: return 1 |
+ cdef (PyObject*) type = NULL, value = NULL, tb = NULL |
+ cdef bint decref_ok = False |
+ PyErr_Fetch(&type, &value, &tb) |
+ try: |
+ try: |
+ if p_obj is NULL: |
+ decref_ok = (<Context>ctx).delref(None, lineno, True) |
+ else: |
+ decref_ok = (<Context>ctx).delref(<object>p_obj, lineno, False) |
+ except: |
+ report_unraisable() |
+ except: |
+ # __Pyx_GetException may itself raise errors |
+ pass |
+ PyErr_Restore(type, value, tb) |
+ return decref_ok |
+ |
+cdef void GIVEREF(PyObject* ctx, PyObject* p_obj, int lineno): |
+ GIVEREF_and_report(ctx, p_obj, lineno) |
+ |
+cdef void INCREF(PyObject* ctx, PyObject* obj, int lineno): |
+ Py_XINCREF(obj) |
+ PyThreadState_Get() |
+ GOTREF(ctx, obj, lineno) |
+ |
+cdef void DECREF(PyObject* ctx, PyObject* obj, int lineno): |
+ if GIVEREF_and_report(ctx, obj, lineno): |
+ Py_XDECREF(obj) |
+ PyThreadState_Get() |
+ |
+cdef void FinishContext(PyObject** ctx): |
+ if ctx == NULL or ctx[0] == NULL: return |
+ cdef (PyObject*) type = NULL, value = NULL, tb = NULL |
+ cdef object errors = None |
+ cdef Context context |
+ PyThreadState_Get() |
+ PyErr_Fetch(&type, &value, &tb) |
+ try: |
+ try: |
+ context = <Context>ctx[0] |
+ errors = context.end() |
+ if errors: |
+ print u"%s: %s()" % (context.filename.decode('latin1'), |
+ context.name.decode('latin1')) |
+ print errors |
+ context = None |
+ except: |
+ report_unraisable() |
+ except: |
+ # __Pyx_GetException may itself raise errors |
+ pass |
+ Py_XDECREF(ctx[0]) |
+ ctx[0] = NULL |
+ PyErr_Restore(type, value, tb) |
+ |
+ctypedef struct RefNannyAPIStruct: |
+ void (*INCREF)(PyObject*, PyObject*, int) |
+ void (*DECREF)(PyObject*, PyObject*, int) |
+ void (*GOTREF)(PyObject*, PyObject*, int) |
+ void (*GIVEREF)(PyObject*, PyObject*, int) |
+ PyObject* (*SetupContext)(char*, int, char*) except NULL |
+ void (*FinishContext)(PyObject**) |
+ |
+cdef RefNannyAPIStruct api |
+api.INCREF = INCREF |
+api.DECREF = DECREF |
+api.GOTREF = GOTREF |
+api.GIVEREF = GIVEREF |
+api.SetupContext = SetupContext |
+api.FinishContext = FinishContext |
+ |
+cdef extern from "Python.h": |
+ object PyLong_FromVoidPtr(void*) |
+ |
+RefNannyAPI = PyLong_FromVoidPtr(<void*>&api) |