[prev in list] [next in list] [prev in thread] [next in thread] 

List:       gcc-python-plugin-commits
Subject:    [gcc-python-plugin] cpychecker: improved handling of unknown functions that return a PyObject * subc
From:       dmalcolm () fedoraproject ! org (dmalcolm)
Date:       2012-02-28 20:15:23
Message-ID: 20120228201524.10D5A158E () lists ! fedorahosted ! org
[Download RAW message or body]

commit ef9cdb8bb9af87e6b4b310ffd152b0dbc280ceba
Author: David Malcolm <dmalcolm at redhat.com>
Date:   Tue Feb 28 15:14:02 2012 -0500

    cpychecker: improved handling of unknown functions that return a PyObject * \
subclass  In 90d237c02bb8fe6bd7b40aa6c2420652c87ce906 unknown functions that return
    a PyObject* are assumed to return either a new reference, or return NULL
    setting an exception.
    
    Generalize this for functions returning PyObject subclasses.
    
    Fixes a false warning for psycopg:
    
       psycopg/connection_type.c: In function ‘psyco_conn_tpc_begin':
       psycopg/connection_type.c:228:1: warning: returning (PyObject*)NULL without \
setting an exception

 libcpychecker/absinterp.py                         |   10 ++-
 .../refcounts/unrecognized_function4/input.c       |   51 +++++++++++++
 .../refcounts/unrecognized_function4/script.py     |   22 ++++++
 .../refcounts/unrecognized_function4/stdout.txt    |   79 ++++++++++++++++++++
 4 files changed, 158 insertions(+), 4 deletions(-)
---
diff --git a/libcpychecker/absinterp.py b/libcpychecker/absinterp.py
index a7295d7..d7ecb89 100644
--- a/libcpychecker/absinterp.py
+++ b/libcpychecker/absinterp.py
@@ -211,8 +211,9 @@ class AbstractValue(object):
         check_isinstance(stmt, gcc.GimpleCall)
         returntype = stmt.fn.type.dereference.type
 
-        if str(returntype) == 'struct PyObject *':
-            log('Invocation of function pointer returning PyObject *')
+        from libcpychecker.refcounts import type_is_pyobjptr_subclass
+        if type_is_pyobjptr_subclass(returntype):
+            log('Invocation of function pointer returning PyObject * (or subclass)')
             # Assume that all such functions either:
             #   - return a new reference, or
             #   - return NULL and set an exception (e.g. MemoryError)
@@ -2212,8 +2213,9 @@ class State(object):
                     raise NotImplementedError('not yet implemented: %s' % fnname)
 
             # Unknown function returning (PyObject*):
-            if str(stmt.fn.operand.type.type) == 'struct PyObject *':
-                log('Invocation of unknown function returning PyObject *: %r' % \
fnname) +            from libcpychecker.refcounts import type_is_pyobjptr_subclass
+            if type_is_pyobjptr_subclass(stmt.fn.operand.type.type):
+                log('Invocation of unknown function returning PyObject * (or \
subclass): %r' % fnname)  
                 fnmeta = FnMeta(name=fnname)
 
diff --git a/tests/cpychecker/refcounts/unrecognized_function4/input.c \
b/tests/cpychecker/refcounts/unrecognized_function4/input.c new file mode 100644
index 0000000..e691582
--- /dev/null
+++ b/tests/cpychecker/refcounts/unrecognized_function4/input.c
@@ -0,0 +1,51 @@
+/*
+   Copyright 2012 David Malcolm <dmalcolm at redhat.com>
+   Copyright 2012 Red Hat, Inc.
+
+   This is free software: you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see
+   <http://www.gnu.org/licenses/>.
+*/
+
+#include <Python.h>
+
+/*
+  Ensure that the checker can cope with calls to a function that it doesn't
+  recognize that returns a PyObject subclass:
+*/
+typedef struct FooObject {
+    PyObject_HEAD
+    int i;
+} FooObject;
+
+extern FooObject *make_foo(int i);
+
+PyObject *
+test(PyObject *self, PyObject *args)
+{
+    FooObject *f = make_foo(42);
+    if (NULL == f) {
+        return NULL; /* we assume an exception was set by make_foo() */
+    }
+    Py_DECREF(f);
+
+    Py_RETURN_NONE;
+}
+
+/*
+  PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/refcounts/unrecognized_function4/script.py \
b/tests/cpychecker/refcounts/unrecognized_function4/script.py new file mode 100644
index 0000000..fdd5ba3
--- /dev/null
+++ b/tests/cpychecker/refcounts/unrecognized_function4/script.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+#   Copyright 2011 David Malcolm <dmalcolm at redhat.com>
+#   Copyright 2011 Red Hat, Inc.
+#
+#   This is free software: you can redistribute it and/or modify it
+#   under the terms of the GNU General Public License as published by
+#   the Free Software Foundation, either version 3 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful, but
+#   WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#   General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program.  If not, see
+#   <http://www.gnu.org/licenses/>.
+
+from libcpychecker import main
+main(verify_refcounting=True,
+     dump_traces=True,
+     show_traces=False)
diff --git a/tests/cpychecker/refcounts/unrecognized_function4/stdout.txt \
b/tests/cpychecker/refcounts/unrecognized_function4/stdout.txt new file mode 100644
index 0000000..c6479c7
--- /dev/null
+++ b/tests/cpychecker/refcounts/unrecognized_function4/stdout.txt
@@ -0,0 +1,79 @@
+Trace 0:
+  Transitions:
+    'when make_foo() succeeds'
+    'taking False path'
+    'when taking True path'
+    'returning'
+  Return value:
+    repr(): PointerToRegion(gcctype='struct PyObject *', \
loc=gcc.Location(file='tests/cpychecker/refcounts/unrecognized_function4/input.c', \
line=42), region=RegionForGlobal(gcc.VarDecl('_Py_NoneStruct'))) +    str(): (struct \
PyObject *)&RegionForGlobal(gcc.VarDecl('_Py_NoneStruct')) from \
tests/cpychecker/refcounts/unrecognized_function4/input.c:42 +    r->ob_refcnt: refs: \
1 + N where N >= 1 +    r->ob_type: None
+  Region("region-for-arg-gcc.ParmDecl('self')"):
+    repr(): Region("region-for-arg-gcc.ParmDecl('self')")
+    str(): Region("region-for-arg-gcc.ParmDecl('self')")
+    r->ob_refcnt: refs: 0 + N where N >= 1
+    r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', \
loc=gcc.Location(file='tests/cpychecker/refcounts/unrecognized_function4/input.c', \
line=34), region=Region("region-for-type-of-arg-gcc.ParmDecl('self')")) +  \
Region("region-for-arg-gcc.ParmDecl('args')"): +    repr(): \
Region("region-for-arg-gcc.ParmDecl('args')") +    str(): \
Region("region-for-arg-gcc.ParmDecl('args')") +    r->ob_refcnt: refs: 0 + N where N \
>= 1 +    r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', \
> loc=gcc.Location(file='tests/cpychecker/refcounts/unrecognized_function4/input.c', \
> line=34), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+  new ref from (unknown) make_foo allocated at \
tests/cpychecker/refcounts/unrecognized_function4/input.c:36: +    repr(): \
RegionOnHeap('new ref from (unknown) make_foo', \
gcc.Location(file='tests/cpychecker/refcounts/unrecognized_function4/input.c', \
line=36)) +    str(): new ref from (unknown) make_foo allocated at \
tests/cpychecker/refcounts/unrecognized_function4/input.c:36 +    r->ob_refcnt: refs: \
0 + N where N >= 0 +    r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', \
loc=gcc.Location(file='tests/cpychecker/refcounts/unrecognized_function4/input.c', \
line=36), region=Region('PyTypeObject for new ref from (unknown) make_foo')) +  \
Exception: +    (struct PyObject *)0 from \
tests/cpychecker/refcounts/unrecognized_function4/input.c:35 +
+Trace 1:
+  Transitions:
+    'when make_foo() succeeds'
+    'taking False path'
+    'when taking False path'
+    'calling tp_dealloc on new ref from (unknown) make_foo allocated at \
tests/cpychecker/refcounts/unrecognized_function4/input.c:36' +    'returning'
+  Return value:
+    repr(): PointerToRegion(gcctype='struct PyObject *', \
loc=gcc.Location(file='tests/cpychecker/refcounts/unrecognized_function4/input.c', \
line=42), region=RegionForGlobal(gcc.VarDecl('_Py_NoneStruct'))) +    str(): (struct \
PyObject *)&RegionForGlobal(gcc.VarDecl('_Py_NoneStruct')) from \
tests/cpychecker/refcounts/unrecognized_function4/input.c:42 +    r->ob_refcnt: refs: \
1 + N where N >= 1 +    r->ob_type: None
+  Region("region-for-arg-gcc.ParmDecl('self')"):
+    repr(): Region("region-for-arg-gcc.ParmDecl('self')")
+    str(): Region("region-for-arg-gcc.ParmDecl('self')")
+    r->ob_refcnt: refs: 0 + N where N >= 1
+    r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', \
loc=gcc.Location(file='tests/cpychecker/refcounts/unrecognized_function4/input.c', \
line=34), region=Region("region-for-type-of-arg-gcc.ParmDecl('self')")) +  \
Region("region-for-arg-gcc.ParmDecl('args')"): +    repr(): \
Region("region-for-arg-gcc.ParmDecl('args')") +    str(): \
Region("region-for-arg-gcc.ParmDecl('args')") +    r->ob_refcnt: refs: 0 + N where N \
>= 1 +    r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', \
> loc=gcc.Location(file='tests/cpychecker/refcounts/unrecognized_function4/input.c', \
> line=34), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+  new ref from (unknown) make_foo allocated at \
tests/cpychecker/refcounts/unrecognized_function4/input.c:36: +    repr(): \
RegionOnHeap('new ref from (unknown) make_foo', \
gcc.Location(file='tests/cpychecker/refcounts/unrecognized_function4/input.c', \
line=36)) +    str(): new ref from (unknown) make_foo allocated at \
tests/cpychecker/refcounts/unrecognized_function4/input.c:36 +    r->ob_refcnt: None
+    r->ob_type: None
+  Exception:
+    (struct PyObject *)0 from \
tests/cpychecker/refcounts/unrecognized_function4/input.c:35 +
+Trace 2:
+  Transitions:
+    'when make_foo() fails'
+    'taking True path'
+    'returning'
+  Return value:
+    repr(): ConcreteValue(gcctype='struct PyObject *', \
loc=gcc.Location(file='tests/cpychecker/refcounts/unrecognized_function4/input.c', \
line=38), value=0) +    str(): (struct PyObject *)0 from \
tests/cpychecker/refcounts/unrecognized_function4/input.c:38 +  \
Region("region-for-arg-gcc.ParmDecl('self')"): +    repr(): \
Region("region-for-arg-gcc.ParmDecl('self')") +    str(): \
Region("region-for-arg-gcc.ParmDecl('self')") +    r->ob_refcnt: refs: 0 + N where N \
>= 1 +    r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', \
> loc=gcc.Location(file='tests/cpychecker/refcounts/unrecognized_function4/input.c', \
> line=34), region=Region("region-for-type-of-arg-gcc.ParmDecl('self')"))
+  Region("region-for-arg-gcc.ParmDecl('args')"):
+    repr(): Region("region-for-arg-gcc.ParmDecl('args')")
+    str(): Region("region-for-arg-gcc.ParmDecl('args')")
+    r->ob_refcnt: refs: 0 + N where N >= 1
+    r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', \
loc=gcc.Location(file='tests/cpychecker/refcounts/unrecognized_function4/input.c', \
line=34), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')")) +  Exception:
+    (struct PyObject *)&RegionForGlobal(gcc.VarDecl('PyExc_MemoryError')) from \
tests/cpychecker/refcounts/unrecognized_function4/input.c:36


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic