Ensure 'cy locals' and 'cy exec' don't use uninitialized local variables (which fixes...
authorMark Florisson <markflorisson88@gmail.com>
Tue, 16 Nov 2010 11:58:49 +0000 (12:58 +0100)
committerMark Florisson <markflorisson88@gmail.com>
Tue, 16 Nov 2010 11:58:49 +0000 (12:58 +0100)
Cython/Compiler/ParseTreeTransforms.py
Cython/Debugger/libcython.py
Cython/Debugger/libpython.py

index bba734a07b0895133146f65b4a25630115a7d780..eecf56d540a5438d7d2be1ffcdac938951ad53ee 100644 (file)
@@ -1606,13 +1606,22 @@ class DebugTransform(CythonTransform):
             cname = entry.cname
             # if entry.type.is_extension_type:
                 # cname = entry.type.typeptr_cname
-
+            
+            if not entry.pos:
+                # this happens for variables that are not in the user's code,
+                # e.g. for the global __builtins__, __doc__, etc. We can just
+                # set the lineno to 0 for those.
+                lineno = '0'
+            else:
+                lineno = str(entry.pos[1])
+                
             attrs = dict(
                 name=entry.name,
                 cname=cname,
                 qualified_name=entry.qualified_name,
-                type=vartype)
-                
+                type=vartype,
+                lineno=lineno)
+            
             self.tb.start('LocalVar', attrs)
             self.tb.end('LocalVar')
         
index f02817bd4d319acd3e418bb9f088a1ef6f08fc1e..a781bcf0bf76e3e9d0921593ec09cde66213756e 100644 (file)
@@ -159,11 +159,12 @@ class CythonModule(object):
 
 class CythonVariable(object):
 
-    def __init__(self, name, cname, qualified_name, type):
+    def __init__(self, name, cname, qualified_name, type, lineno):
         self.name = name
         self.cname = cname
         self.qualified_name = qualified_name
         self.type = type
+        self.lineno = int(lineno)
 
 class CythonFunction(CythonVariable):
     def __init__(self, 
@@ -177,10 +178,10 @@ class CythonFunction(CythonVariable):
         super(CythonFunction, self).__init__(name, 
                                              cname, 
                                              qualified_name, 
-                                             type)
+                                             type,
+                                             lineno)
         self.module = module
         self.pf_cname = pf_cname
-        self.lineno = int(lineno)
         self.locals = {}
         self.arguments = []
         self.step_into_functions = set()
@@ -387,6 +388,10 @@ class CythonBase(object):
             print '%s%-*s = %s%s' % (prefix, max_name_length, name, typename, 
                                      value)
 
+    def is_initialized(self, cython_func, local_name):
+        cur_lineno = self.get_cython_lineno()
+        return (local_name in cython_func.arguments or 
+                cur_lineno > cython_func.locals[local_name].lineno)
 
 class SourceFileDescriptor(object):
     def __init__(self, filename, lexer, formatter=None):
@@ -1073,12 +1078,8 @@ class CyLocals(CythonCommand):
     completer_class = gdb.COMPLETE_NONE
     
     def _print_if_initialized(self, cyvar, max_name_length, prefix=''):
-        try:
+        if self.is_initialized(self.get_cython_function(), cyvar.name):
             value = gdb.parse_and_eval(cyvar.cname)
-        except RuntimeError:
-            # variable not initialized yet
-            pass
-        else:
             self.print_gdb_value(cyvar.name, value, max_name_length, prefix)
     
     @dispatch_on_frame(c_command='info locals', python_command='py-locals')
@@ -1134,20 +1135,12 @@ class CyExec(CythonCommand, libpython.PyExec):
     def _fill_locals_dict(self, executor, local_dict_pointer):
         "Fill a remotely allocated dict with values from the Cython C stack"
         cython_func = self.get_cython_function()
+        current_lineno = self.get_cython_lineno()
         
         for name, cyvar in cython_func.locals.iteritems():
-            if cyvar.type == PythonObject:
-                # skip unitialized Cython variables 
-                try:
-                    val = gdb.parse_and_eval(cyvar.cname)
-                except RuntimeError:
-                    continue
-                else:
-                    # Fortunately, Cython initializes all local (automatic)
-                    # variables to NULL
-                    if libpython.pointervalue(val) == 0:
-                        continue
-
+            if (cyvar.type == PythonObject and 
+                self.is_initialized(cython_func, name)):
+                
                 pystringp = executor.alloc_pystring(name)
                 code = '''
                     PyDict_SetItem(
@@ -1155,13 +1148,14 @@ class CyExec(CythonCommand, libpython.PyExec):
                         (PyObject *) %d, 
                         (PyObject *) %s)
                 ''' % (local_dict_pointer, pystringp, cyvar.cname)
-                
-                # PyDict_SetItem doesn't steal our reference
-                executor.decref(pystringp)
-                
-                if gdb.parse_and_eval(code) < 0:
-                    gdb.parse_and_eval('PyErr_Print()')
-                    raise gdb.GdbError("Unable to execute Python code.")
+
+                try:
+                    if gdb.parse_and_eval(code) < 0:
+                        gdb.parse_and_eval('PyErr_Print()')
+                        raise gdb.GdbError("Unable to execute Python code.")
+                finally:
+                    # PyDict_SetItem doesn't steal our reference
+                    executor.decref(pystringp)
     
     def _find_first_cython_or_python_frame(self):
         frame = gdb.selected_frame()
index 45e1fde257d40851923ef1c609bb162b63f0dd9a..64f2eb2f35b9af7ae97e636f5a442d90dbcf4b11 100644 (file)
@@ -1929,7 +1929,7 @@ class PythonCodeExecutor(object):
         
         pointer = pointervalue(chunk)
         if pointer == 0:
-            err("No memory could be allocated in the inferior.")
+            raise gdb.GdbError("No memory could be allocated in the inferior.")
         
         return pointer
         
@@ -1950,7 +1950,7 @@ class PythonCodeExecutor(object):
         
         pointer = pointervalue(result)
         if pointer == 0:
-            err("Unable to allocate Python string in "
+            raise gdb.GdbError("Unable to allocate Python string in "
                                "the inferior.")
         
         return pointer
@@ -1958,6 +1958,10 @@ class PythonCodeExecutor(object):
     def free(self, pointer):
         gdb.parse_and_eval("free((void *) %d)" % pointer)
     
+    def incref(self, pointer):
+        "Increment the reference count of a Python object in the inferior."
+        gdb.parse_and_eval('Py_IncRef((PyObject *) %d)' % pointer)
+    
     def decref(self, pointer):
         "Decrement the reference count of a Python object in the inferior."
         # Py_DecRef is like Py_XDECREF, but a function. So we don't have
@@ -1975,7 +1979,7 @@ class PythonCodeExecutor(object):
         leave the debuggee in an unsafe state or terminate it alltogether.
         """
         if '\0' in code:
-            err("String contains NUL byte.")
+            raise gdb.GdbError("String contains NUL byte.")
         
         code += '\0'