Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / third_party / cython / src / Cython / Runtime / refnanny.pyx
blob80ec3f4bafca5db7d97a838fb9d215e220c3a22a
1 from cpython.ref cimport PyObject, Py_INCREF, Py_DECREF, Py_XDECREF, Py_XINCREF
2 from cpython.exc cimport PyErr_Fetch, PyErr_Restore
3 from cpython.pystate cimport PyThreadState_Get
5 cimport cython
7 loglevel = 0
8 reflog = []
10 cdef log(level, action, obj, lineno):
11 if loglevel >= level:
12 reflog.append((lineno, action, id(obj)))
14 LOG_NONE, LOG_ALL = range(2)
16 @cython.final
17 cdef class Context(object):
18 cdef readonly object name, filename
19 cdef readonly dict refs
20 cdef readonly list errors
21 cdef readonly Py_ssize_t start
23 def __cinit__(self, name, line=0, filename=None):
24 self.name = name
25 self.start = line
26 self.filename = filename
27 self.refs = {} # id -> (count, [lineno])
28 self.errors = []
30 cdef regref(self, obj, lineno, bint is_null):
31 log(LOG_ALL, u'regref', u"<NULL>" if is_null else obj, lineno)
32 if is_null:
33 self.errors.append(u"NULL argument on line %d" % lineno)
34 return
35 id_ = id(obj)
36 count, linenumbers = self.refs.get(id_, (0, []))
37 self.refs[id_] = (count + 1, linenumbers)
38 linenumbers.append(lineno)
40 cdef bint delref(self, obj, lineno, bint is_null) except -1:
41 # returns whether it is ok to do the decref operation
42 log(LOG_ALL, u'delref', u"<NULL>" if is_null else obj, lineno)
43 if is_null:
44 self.errors.append(u"NULL argument on line %d" % lineno)
45 return False
46 id_ = id(obj)
47 count, linenumbers = self.refs.get(id_, (0, []))
48 if count == 0:
49 self.errors.append(u"Too many decrefs on line %d, reference acquired on lines %r" %
50 (lineno, linenumbers))
51 return False
52 elif count == 1:
53 del self.refs[id_]
54 return True
55 else:
56 self.refs[id_] = (count - 1, linenumbers)
57 return True
59 cdef end(self):
60 if self.refs:
61 msg = u"References leaked:"
62 for count, linenos in self.refs.itervalues():
63 msg += u"\n (%d) acquired on lines: %s" % (count, u", ".join([u"%d" % x for x in linenos]))
64 self.errors.append(msg)
65 if self.errors:
66 return u"\n".join([u'REFNANNY: '+error for error in self.errors])
67 else:
68 return None
70 cdef void report_unraisable(object e=None):
71 try:
72 if e is None:
73 import sys
74 e = sys.exc_info()[1]
75 print u"refnanny raised an exception: %s" % e
76 except:
77 pass # We absolutely cannot exit with an exception
79 # All Python operations must happen after any existing
80 # exception has been fetched, in case we are called from
81 # exception-handling code.
83 cdef PyObject* SetupContext(char* funcname, int lineno, char* filename) except NULL:
84 if Context is None:
85 # Context may be None during finalize phase.
86 # In that case, we don't want to be doing anything fancy
87 # like caching and resetting exceptions.
88 return NULL
89 cdef (PyObject*) type = NULL, value = NULL, tb = NULL, result = NULL
90 PyThreadState_Get()
91 PyErr_Fetch(&type, &value, &tb)
92 try:
93 ctx = Context(funcname, lineno, filename)
94 Py_INCREF(ctx)
95 result = <PyObject*>ctx
96 except Exception, e:
97 report_unraisable(e)
98 PyErr_Restore(type, value, tb)
99 return result
101 cdef void GOTREF(PyObject* ctx, PyObject* p_obj, int lineno):
102 if ctx == NULL: return
103 cdef (PyObject*) type = NULL, value = NULL, tb = NULL
104 PyErr_Fetch(&type, &value, &tb)
105 try:
106 try:
107 if p_obj is NULL:
108 (<Context>ctx).regref(None, lineno, True)
109 else:
110 (<Context>ctx).regref(<object>p_obj, lineno, False)
111 except:
112 report_unraisable()
113 except:
114 # __Pyx_GetException may itself raise errors
115 pass
116 PyErr_Restore(type, value, tb)
118 cdef int GIVEREF_and_report(PyObject* ctx, PyObject* p_obj, int lineno):
119 if ctx == NULL: return 1
120 cdef (PyObject*) type = NULL, value = NULL, tb = NULL
121 cdef bint decref_ok = False
122 PyErr_Fetch(&type, &value, &tb)
123 try:
124 try:
125 if p_obj is NULL:
126 decref_ok = (<Context>ctx).delref(None, lineno, True)
127 else:
128 decref_ok = (<Context>ctx).delref(<object>p_obj, lineno, False)
129 except:
130 report_unraisable()
131 except:
132 # __Pyx_GetException may itself raise errors
133 pass
134 PyErr_Restore(type, value, tb)
135 return decref_ok
137 cdef void GIVEREF(PyObject* ctx, PyObject* p_obj, int lineno):
138 GIVEREF_and_report(ctx, p_obj, lineno)
140 cdef void INCREF(PyObject* ctx, PyObject* obj, int lineno):
141 Py_XINCREF(obj)
142 PyThreadState_Get()
143 GOTREF(ctx, obj, lineno)
145 cdef void DECREF(PyObject* ctx, PyObject* obj, int lineno):
146 if GIVEREF_and_report(ctx, obj, lineno):
147 Py_XDECREF(obj)
148 PyThreadState_Get()
150 cdef void FinishContext(PyObject** ctx):
151 if ctx == NULL or ctx[0] == NULL: return
152 cdef (PyObject*) type = NULL, value = NULL, tb = NULL
153 cdef object errors = None
154 cdef Context context
155 PyThreadState_Get()
156 PyErr_Fetch(&type, &value, &tb)
157 try:
158 try:
159 context = <Context>ctx[0]
160 errors = context.end()
161 if errors:
162 print u"%s: %s()" % (context.filename.decode('latin1'),
163 context.name.decode('latin1'))
164 print errors
165 context = None
166 except:
167 report_unraisable()
168 except:
169 # __Pyx_GetException may itself raise errors
170 pass
171 Py_XDECREF(ctx[0])
172 ctx[0] = NULL
173 PyErr_Restore(type, value, tb)
175 ctypedef struct RefNannyAPIStruct:
176 void (*INCREF)(PyObject*, PyObject*, int)
177 void (*DECREF)(PyObject*, PyObject*, int)
178 void (*GOTREF)(PyObject*, PyObject*, int)
179 void (*GIVEREF)(PyObject*, PyObject*, int)
180 PyObject* (*SetupContext)(char*, int, char*) except NULL
181 void (*FinishContext)(PyObject**)
183 cdef RefNannyAPIStruct api
184 api.INCREF = INCREF
185 api.DECREF = DECREF
186 api.GOTREF = GOTREF
187 api.GIVEREF = GIVEREF
188 api.SetupContext = SetupContext
189 api.FinishContext = FinishContext
191 cdef extern from "Python.h":
192 object PyLong_FromVoidPtr(void*)
194 RefNannyAPI = PyLong_FromVoidPtr(<void*>&api)