[gcc-python-plugin] Makefile: add "release" target, to help me make releases
by David Hugh Malcolm
commit 229fe51ab776c3f3d9f52c2533de3fd40bcda5a3
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Mon Aug 22 16:24:27 2011 -0400
Makefile: add "release" target, to help me make releases
Makefile | 17 +++++++++++++++++
1 files changed, 17 insertions(+), 0 deletions(-)
---
diff --git a/Makefile b/Makefile
index d629eb2..3e01942 100644
--- a/Makefile
+++ b/Makefile
@@ -195,3 +195,20 @@ docs/tables-of-passes.rst: plugin generate-tables-of-passes-rst.py
# Likewise for this generated file:
docs/passes.svg: plugin generate-passes-svg.py
./gcc-with-python generate-passes-svg.py test.c
+
+# Utility target, to help me to make releases
+# - creates a tag in git, and pushes it
+# - creates a tarball
+# - publishes it below:
+# https://fedorahosted.org/releases/g/c/gcc-python-plugin/
+#
+# The following assumes that VERSION has been set e.g.
+# $ make release VERSION=0.4
+#
+# git push --tags
+# scp gcc-python-plugin-$(VERSION).tar.gz dmalcolm@fedorahosted.org:gcc-python-plugin
+
+release:
+ git tag -a v$(VERSION) -m"$(VERSION)"
+ git archive --format=tar --prefix=gcc-python-plugin-$(VERSION)/ v$(VERSION) | gzip > gcc-python-plugin-$(VERSION).tar.gz
+ cp gcc-python-plugin-$(VERSION).tar.gz ~/rpmbuild/SOURCES/
12 years, 9 months
[gcc-python-plugin] fix stray syntax error
by David Hugh Malcolm
commit 2b8f428919849291f3859b678a5bceaa8513f668
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Thu Aug 18 19:04:41 2011 -0400
fix stray syntax error
run-test-suite.py | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
---
diff --git a/run-test-suite.py b/run-test-suite.py
index 03c3ae1..82a67aa 100644
--- a/run-test-suite.py
+++ b/run-test-suite.py
@@ -100,7 +100,7 @@ class TestStream:
if writeback:
# Special-case mode: don't compare, instead refresh the "gold"
# output by writing back to disk:
- if self.expdata = '':
+ if self.expdata == '':
with open(self.exppath, 'w') as f:
f.write(actual)
return
12 years, 9 months
[gcc-python-plugin] libcpychecker: assume that PyObject* function arguments are non-NULL and sane
by David Hugh Malcolm
commit 8b884e19ca3a09015ae27817689e531ae5e8bf79
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Thu Aug 18 19:02:47 2011 -0400
libcpychecker: assume that PyObject* function arguments are non-NULL and sane
When running the refcount checker, assume that all function arguments that
are of type PyObject* (or some subclass):
- are non-NULL
- are borrowed references
- have non-NULL ob_type fields
This eliminates numerous false positives when running the checker on real-world
code
libcpychecker/absinterp.py | 4 +-
libcpychecker/refcounts.py | 30 ++++++++++
.../cpychecker/refcounts/correct_decref/stdout.txt | 20 +++++++
.../refcounts/correct_py_none/stdout.txt | 10 +++
.../refcounts/fold_conditional/stdout.txt | 20 +++++++
tests/cpychecker/refcounts/loop_n_times/stdout.txt | 60 ++++++++++++++++++++
.../cpychecker/refcounts/missing_decref/stdout.txt | 30 ++++++++++
.../refcounts/not_setting_exception/stdout.txt | 10 +++
.../refcounts/returning_dead_object/stdout.txt | 30 ++++++++++
.../cpychecker/refcounts/subclass/handling/input.c | 54 ++++++++++++++++++
.../refcounts/subclass/handling/script.py | 22 +++++++
.../refcounts/subclass/handling/stdout.txt | 18 ++++++
.../refcounts/too_many_increfs/stdout.txt | 20 +++++++
.../refcounts/use_after_dealloc/stdout.txt | 30 ++++++++++
14 files changed, 356 insertions(+), 2 deletions(-)
---
diff --git a/libcpychecker/absinterp.py b/libcpychecker/absinterp.py
index b40dd95..521439b 100644
--- a/libcpychecker/absinterp.py
+++ b/libcpychecker/absinterp.py
@@ -425,7 +425,7 @@ class State:
log('eval_lvalue: %r %s' % (expr, expr))
if loc:
assert isinstance(loc, gcc.Location)
- if isinstance(expr, gcc.VarDecl):
+ if isinstance(expr, (gcc.VarDecl, gcc.ParmDecl)):
region = self.var_region(expr)
assert isinstance(region, Region)
return region
@@ -700,7 +700,7 @@ class State:
return None
def init_for_function(self, fun):
- log('init_for_function(%r)' % fun)
+ log('State.init_for_function(%r)' % fun)
self.fun = fun
root_region = Region('root', None)
stack = Region('stack for %s' % fun.decl.name, root_region)
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index 7140fa6..8406854 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -174,6 +174,36 @@ class MyState(State):
def release(self, resource):
self.resources.release(resource)
+ def init_for_function(self, fun):
+ log('MyState.init_for_function(%r)' % fun)
+ State.init_for_function(self, fun)
+
+ # Initialize PyObject* arguments to sane values
+ # (assume that they're non-NULL)
+ nonnull_args = get_nonnull_arguments(fun.decl.type)
+ for idx, parm in enumerate(fun.decl.arguments):
+ region = self.eval_lvalue(parm, None)
+ if type_is_pyobjptr_subclass(parm.type):
+ # We have a PyObject* (or a derived class)
+ log('got python obj arg: %r' % region)
+ # Assume it's a non-NULL ptr:
+ objregion = Region('region-for-arg-%s' % parm, None)
+ self.region_for_var[objregion] = objregion
+ self.value_for_region[region] = PointerToRegion(parm.type,
+ parm.location,
+ objregion)
+ # Assume we have a borrowed reference:
+ ob_refcnt = self.make_field_region(objregion, 'ob_refcnt') # FIXME: this should be a memref and fieldref
+ self.value_for_region[ob_refcnt] = RefcountValue(0) # borrowed ref
+
+ # Assume it has a non-NULL ob_type:
+ ob_type = self.make_field_region(objregion, 'ob_type')
+ typeobjregion = Region('region-for-type-of-arg-%s' % parm, None)
+ self.value_for_region[ob_type] = PointerToRegion(get_PyTypeObject().pointer,
+ parm.location,
+ typeobjregion)
+ self.verify()
+
def make_assignment(self, key, value, desc, additional_ptr=None):
if desc:
assert isinstance(desc, str)
diff --git a/tests/cpychecker/refcounts/correct_decref/stdout.txt b/tests/cpychecker/refcounts/correct_decref/stdout.txt
index fb9a534..4ee1d1f 100644
--- a/tests/cpychecker/refcounts/correct_decref/stdout.txt
+++ b/tests/cpychecker/refcounts/correct_decref/stdout.txt
@@ -9,6 +9,16 @@ Trace 0:
str(): (struct PyObject *)&RegionOnHeap('PyLongObject', gcc.Location(file='tests/cpychecker/refcounts/correct_decref/input.c', line=30)) from tests/cpychecker/refcounts/correct_decref/input.c:30
r->ob_refcnt: RefcountValue(1)
r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/correct_decref/input.c', line=30), region=RegionForGlobal(gcc.VarDecl('PyLong_Type')))
+ 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/correct_decref/input.c', line=27), 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/correct_decref/input.c', line=27), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
Exception:
(struct PyObject *)0 from tests/cpychecker/refcounts/correct_decref/input.c:28
@@ -20,5 +30,15 @@ Trace 1:
Return value:
repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/correct_decref/input.c', line=33), value=0L)
str(): (struct PyObject *)0L from tests/cpychecker/refcounts/correct_decref/input.c:33
+ 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/correct_decref/input.c', line=27), 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/correct_decref/input.c', line=27), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
Exception:
RegionForGlobal(gcc.VarDecl('PyExc_MemoryError'))
diff --git a/tests/cpychecker/refcounts/correct_py_none/stdout.txt b/tests/cpychecker/refcounts/correct_py_none/stdout.txt
index 414d40d..72eaaf9 100644
--- a/tests/cpychecker/refcounts/correct_py_none/stdout.txt
+++ b/tests/cpychecker/refcounts/correct_py_none/stdout.txt
@@ -6,5 +6,15 @@ Trace 0:
str(): (struct PyObject *)&RegionForGlobal(gcc.VarDecl('_Py_NoneStruct')) from tests/cpychecker/refcounts/correct_py_none/input.c:25
r->ob_refcnt: RefcountValue(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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/correct_py_none/input.c', line=23), 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/correct_py_none/input.c', line=23), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
Exception:
(struct PyObject *)0 from tests/cpychecker/refcounts/correct_py_none/input.c:24
diff --git a/tests/cpychecker/refcounts/fold_conditional/stdout.txt b/tests/cpychecker/refcounts/fold_conditional/stdout.txt
index bc0bfc8..58d61b9 100644
--- a/tests/cpychecker/refcounts/fold_conditional/stdout.txt
+++ b/tests/cpychecker/refcounts/fold_conditional/stdout.txt
@@ -10,6 +10,16 @@ Trace 0:
str(): (struct PyObject *)&RegionOnHeap('PyListObject', gcc.Location(file='tests/cpychecker/refcounts/fold_conditional/input.c', line=26)) from tests/cpychecker/refcounts/fold_conditional/input.c:26
r->ob_refcnt: RefcountValue(1)
r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/fold_conditional/input.c', line=26), region=RegionForGlobal(gcc.VarDecl('PyList_Type')))
+ 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/fold_conditional/input.c', line=23), 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/fold_conditional/input.c', line=23), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
Exception:
(struct PyObject *)0 from tests/cpychecker/refcounts/fold_conditional/input.c:24
@@ -21,5 +31,15 @@ Trace 1:
Return value:
repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/fold_conditional/input.c', line=28), value=0L)
str(): (struct PyObject *)0L from tests/cpychecker/refcounts/fold_conditional/input.c:28
+ 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/fold_conditional/input.c', line=23), 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/fold_conditional/input.c', line=23), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
Exception:
RegionForGlobal(gcc.VarDecl('PyExc_MemoryError'))
diff --git a/tests/cpychecker/refcounts/loop_n_times/stdout.txt b/tests/cpychecker/refcounts/loop_n_times/stdout.txt
index b68ae82..d67d4ca 100644
--- a/tests/cpychecker/refcounts/loop_n_times/stdout.txt
+++ b/tests/cpychecker/refcounts/loop_n_times/stdout.txt
@@ -14,6 +14,16 @@ Trace 0:
str(): (struct PyObject *)&RegionOnHeap('PyListObject', gcc.Location(file='tests/cpychecker/refcounts/loop_n_times/input.c', line=38)) from tests/cpychecker/refcounts/loop_n_times/input.c:38
r->ob_refcnt: RefcountValue(1)
r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/loop_n_times/input.c', line=38), region=RegionForGlobal(gcc.VarDecl('PyList_Type')))
+ 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/loop_n_times/input.c', line=28), 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/loop_n_times/input.c', line=28), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
PyLongObject allocated at tests/cpychecker/refcounts/loop_n_times/input.c:42:
repr(): RegionOnHeap('PyLongObject', gcc.Location(file='tests/cpychecker/refcounts/loop_n_times/input.c', line=42))
str(): PyLongObject allocated at tests/cpychecker/refcounts/loop_n_times/input.c:42
@@ -39,6 +49,16 @@ Trace 1:
str(): (struct PyObject *)&RegionOnHeap('PyListObject', gcc.Location(file='tests/cpychecker/refcounts/loop_n_times/input.c', line=38)) from tests/cpychecker/refcounts/loop_n_times/input.c:38
r->ob_refcnt: RefcountValue(1)
r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/loop_n_times/input.c', line=38), region=RegionForGlobal(gcc.VarDecl('PyList_Type')))
+ 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/loop_n_times/input.c', line=28), 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/loop_n_times/input.c', line=28), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
PyLongObject allocated at tests/cpychecker/refcounts/loop_n_times/input.c:42:
repr(): RegionOnHeap('PyLongObject', gcc.Location(file='tests/cpychecker/refcounts/loop_n_times/input.c', line=42))
str(): PyLongObject allocated at tests/cpychecker/refcounts/loop_n_times/input.c:42
@@ -60,6 +80,16 @@ Trace 2:
Return value:
repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/loop_n_times/input.c', line=45), value=0L)
str(): (struct PyObject *)0L from tests/cpychecker/refcounts/loop_n_times/input.c:45
+ 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/loop_n_times/input.c', line=28), 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/loop_n_times/input.c', line=28), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
PyListObject allocated at tests/cpychecker/refcounts/loop_n_times/input.c:38:
repr(): RegionOnHeap('PyListObject', gcc.Location(file='tests/cpychecker/refcounts/loop_n_times/input.c', line=38))
str(): PyListObject allocated at tests/cpychecker/refcounts/loop_n_times/input.c:38
@@ -82,6 +112,16 @@ Trace 3:
Return value:
repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/loop_n_times/input.c', line=45), value=0L)
str(): (struct PyObject *)0L from tests/cpychecker/refcounts/loop_n_times/input.c:45
+ 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/loop_n_times/input.c', line=28), 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/loop_n_times/input.c', line=28), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
PyListObject allocated at tests/cpychecker/refcounts/loop_n_times/input.c:38:
repr(): RegionOnHeap('PyListObject', gcc.Location(file='tests/cpychecker/refcounts/loop_n_times/input.c', line=38))
str(): PyListObject allocated at tests/cpychecker/refcounts/loop_n_times/input.c:38
@@ -100,6 +140,16 @@ Trace 4:
Return value:
repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/loop_n_times/input.c', line=40), value=0L)
str(): (struct PyObject *)0L from tests/cpychecker/refcounts/loop_n_times/input.c:40
+ 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/loop_n_times/input.c', line=28), 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/loop_n_times/input.c', line=28), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
Exception:
RegionForGlobal(gcc.VarDecl('PyExc_MemoryError'))
@@ -111,5 +161,15 @@ Trace 5:
Return value:
repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/loop_n_times/input.c', line=35), value=0L)
str(): (struct PyObject *)0L from tests/cpychecker/refcounts/loop_n_times/input.c:35
+ 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/loop_n_times/input.c', line=28), 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/loop_n_times/input.c', line=28), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
Exception:
RegionForGlobal(gcc.VarDecl('PyExc_TypeError'))
diff --git a/tests/cpychecker/refcounts/missing_decref/stdout.txt b/tests/cpychecker/refcounts/missing_decref/stdout.txt
index 200be17..7adf9bd 100644
--- a/tests/cpychecker/refcounts/missing_decref/stdout.txt
+++ b/tests/cpychecker/refcounts/missing_decref/stdout.txt
@@ -11,6 +11,16 @@ Trace 0:
str(): (struct PyObject *)&RegionOnHeap('PyListObject', gcc.Location(file='tests/cpychecker/refcounts/missing_decref/input.c', line=27)) from tests/cpychecker/refcounts/missing_decref/input.c:27
r->ob_refcnt: RefcountValue(1)
r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/missing_decref/input.c', line=27), region=RegionForGlobal(gcc.VarDecl('PyList_Type')))
+ 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/missing_decref/input.c', line=23), 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/missing_decref/input.c', line=23), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
PyLongObject allocated at tests/cpychecker/refcounts/missing_decref/input.c:30:
repr(): RegionOnHeap('PyLongObject', gcc.Location(file='tests/cpychecker/refcounts/missing_decref/input.c', line=30))
str(): PyLongObject allocated at tests/cpychecker/refcounts/missing_decref/input.c:30
@@ -29,6 +39,16 @@ Trace 1:
Return value:
repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/missing_decref/input.c', line=34), value=0L)
str(): (struct PyObject *)0L from tests/cpychecker/refcounts/missing_decref/input.c:34
+ 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/missing_decref/input.c', line=23), 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/missing_decref/input.c', line=23), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
PyListObject allocated at tests/cpychecker/refcounts/missing_decref/input.c:27:
repr(): RegionOnHeap('PyListObject', gcc.Location(file='tests/cpychecker/refcounts/missing_decref/input.c', line=27))
str(): PyListObject allocated at tests/cpychecker/refcounts/missing_decref/input.c:27
@@ -45,5 +65,15 @@ Trace 2:
Return value:
repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/missing_decref/input.c', line=29), value=0L)
str(): (struct PyObject *)0L from tests/cpychecker/refcounts/missing_decref/input.c:29
+ 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/missing_decref/input.c', line=23), 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/missing_decref/input.c', line=23), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
Exception:
RegionForGlobal(gcc.VarDecl('PyExc_MemoryError'))
diff --git a/tests/cpychecker/refcounts/not_setting_exception/stdout.txt b/tests/cpychecker/refcounts/not_setting_exception/stdout.txt
index 7bd417e..89f9bf5 100644
--- a/tests/cpychecker/refcounts/not_setting_exception/stdout.txt
+++ b/tests/cpychecker/refcounts/not_setting_exception/stdout.txt
@@ -4,5 +4,15 @@ Trace 0:
Return value:
repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/not_setting_exception/input.c', line=32), value=0L)
str(): (struct PyObject *)0L from tests/cpychecker/refcounts/not_setting_exception/input.c:32
+ 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/not_setting_exception/input.c', line=26), 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/not_setting_exception/input.c', line=26), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
Exception:
(struct PyObject *)0 from tests/cpychecker/refcounts/not_setting_exception/input.c:27
diff --git a/tests/cpychecker/refcounts/returning_dead_object/stdout.txt b/tests/cpychecker/refcounts/returning_dead_object/stdout.txt
index 1e7ac9b..537f1b1 100644
--- a/tests/cpychecker/refcounts/returning_dead_object/stdout.txt
+++ b/tests/cpychecker/refcounts/returning_dead_object/stdout.txt
@@ -9,6 +9,16 @@ Trace 0:
str(): (struct PyObject *)&RegionOnHeap('PyLongObject', gcc.Location(file='tests/cpychecker/refcounts/returning_dead_object/input.c', line=29)) from tests/cpychecker/refcounts/returning_dead_object/input.c:29
r->ob_refcnt: RefcountValue(0)
r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/returning_dead_object/input.c', line=29), region=RegionForGlobal(gcc.VarDecl('PyLong_Type')))
+ 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/returning_dead_object/input.c', line=26), 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/returning_dead_object/input.c', line=26), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
Exception:
(struct PyObject *)0 from tests/cpychecker/refcounts/returning_dead_object/input.c:27
@@ -24,6 +34,16 @@ Trace 1:
str(): (struct PyObject *)&RegionOnHeap('PyLongObject', gcc.Location(file='tests/cpychecker/refcounts/returning_dead_object/input.c', line=29)) from tests/cpychecker/refcounts/returning_dead_object/input.c:29
r->ob_refcnt: None
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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/returning_dead_object/input.c', line=26), 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/returning_dead_object/input.c', line=26), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
Exception:
(struct PyObject *)0 from tests/cpychecker/refcounts/returning_dead_object/input.c:27
@@ -35,5 +55,15 @@ Trace 2:
Return value:
repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/returning_dead_object/input.c', line=32), value=0L)
str(): (struct PyObject *)0L from tests/cpychecker/refcounts/returning_dead_object/input.c:32
+ 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/returning_dead_object/input.c', line=26), 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/returning_dead_object/input.c', line=26), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
Exception:
RegionForGlobal(gcc.VarDecl('PyExc_MemoryError'))
diff --git a/tests/cpychecker/refcounts/subclass/handling/input.c b/tests/cpychecker/refcounts/subclass/handling/input.c
new file mode 100644
index 0000000..df64ede
--- /dev/null
+++ b/tests/cpychecker/refcounts/subclass/handling/input.c
@@ -0,0 +1,54 @@
+/*
+ Copyright 2011 David Malcolm <dmalcolm(a)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/>.
+*/
+
+#include <Python.h>
+
+/*
+ Verify that the checker can cope with PyObject subclasses
+*/
+
+/* The checker should treat this as a PyObject subclass */
+struct FooObject {
+ PyObject_HEAD
+ int i;
+};
+
+/*
+ The checker should make assumptions that "self" is sane
+ i.e. non-NULL, has a sane ob_type, etc:
+*/
+PyObject *
+test_function(struct FooObject *self, PyObject *args)
+{
+ return PyString_FromFormat("%s()",
+ Py_TYPE(self)->tp_name);
+}
+
+static PyMethodDef test_methods[] = {
+ {"test_method", (PyCFunction)test_function, METH_VARARGS, NULL},
+ {NULL, NULL, 0, NULL} /* Sentinel */
+};
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/refcounts/subclass/handling/script.py b/tests/cpychecker/refcounts/subclass/handling/script.py
new file mode 100644
index 0000000..fdd5ba3
--- /dev/null
+++ b/tests/cpychecker/refcounts/subclass/handling/script.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+# Copyright 2011 David Malcolm <dmalcolm(a)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/subclass/handling/stdout.txt b/tests/cpychecker/refcounts/subclass/handling/stdout.txt
new file mode 100644
index 0000000..ded0138
--- /dev/null
+++ b/tests/cpychecker/refcounts/subclass/handling/stdout.txt
@@ -0,0 +1,18 @@
+Trace 0:
+ Transitions:
+ 'returning'
+ Return value:
+ repr(): UnknownValue(gcctype=<gcc.PointerType object at 0xdeadbeef>, loc=gcc.Location(file='tests/cpychecker/refcounts/subclass/handling/input.c', line=39))
+ str(): unknown struct PyObject * from tests/cpychecker/refcounts/subclass/handling/input.c:39
+ 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/subclass/handling/input.c', line=37), 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/subclass/handling/input.c', line=37), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/refcounts/subclass/handling/input.c:38
diff --git a/tests/cpychecker/refcounts/too_many_increfs/stdout.txt b/tests/cpychecker/refcounts/too_many_increfs/stdout.txt
index c1f63cc..fc26723 100644
--- a/tests/cpychecker/refcounts/too_many_increfs/stdout.txt
+++ b/tests/cpychecker/refcounts/too_many_increfs/stdout.txt
@@ -7,6 +7,16 @@ Trace 0:
str(): (struct PyObject *)&RegionOnHeap('PyLongObject', gcc.Location(file='tests/cpychecker/refcounts/too_many_increfs/input.c', line=26)) from tests/cpychecker/refcounts/too_many_increfs/input.c:26
r->ob_refcnt: RefcountValue(2)
r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/too_many_increfs/input.c', line=26), region=RegionForGlobal(gcc.VarDecl('PyLong_Type')))
+ 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/too_many_increfs/input.c', line=23), 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/too_many_increfs/input.c', line=23), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
Exception:
(struct PyObject *)0 from tests/cpychecker/refcounts/too_many_increfs/input.c:24
@@ -15,5 +25,15 @@ Trace 1:
'PyLong_FromLong() fails'
error: NullPtrDereference()
error: dereferencing NULL (tmp->ob_refcnt) at tests/cpychecker/refcounts/too_many_increfs/input.c:30
+ 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/too_many_increfs/input.c', line=23), 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/too_many_increfs/input.c', line=23), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
Exception:
RegionForGlobal(gcc.VarDecl('PyExc_MemoryError'))
diff --git a/tests/cpychecker/refcounts/use_after_dealloc/stdout.txt b/tests/cpychecker/refcounts/use_after_dealloc/stdout.txt
index 607524e..286dd2c 100644
--- a/tests/cpychecker/refcounts/use_after_dealloc/stdout.txt
+++ b/tests/cpychecker/refcounts/use_after_dealloc/stdout.txt
@@ -9,6 +9,16 @@ Trace 0:
str(): (struct PyObject *)&RegionOnHeap('PyLongObject', gcc.Location(file='tests/cpychecker/refcounts/use_after_dealloc/input.c', line=30)) from tests/cpychecker/refcounts/use_after_dealloc/input.c:30
r->ob_refcnt: RefcountValue(1)
r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/use_after_dealloc/input.c', line=30), region=RegionForGlobal(gcc.VarDecl('PyLong_Type')))
+ 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/use_after_dealloc/input.c', line=27), 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/use_after_dealloc/input.c', line=27), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
Exception:
(struct PyObject *)0 from tests/cpychecker/refcounts/use_after_dealloc/input.c:28
@@ -20,6 +30,16 @@ Trace 1:
'calling tp_dealloc on PyLongObject allocated at tests/cpychecker/refcounts/use_after_dealloc/input.c:30'
error: ReadFromDeallocatedMemory()
error: reading from deallocated memory at tests/cpychecker/refcounts/use_after_dealloc/input.c:42: memory deallocated at tests/cpychecker/refcounts/use_after_dealloc/input.c:41
+ 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/use_after_dealloc/input.c', line=27), 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/use_after_dealloc/input.c', line=27), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
PyLongObject allocated at tests/cpychecker/refcounts/use_after_dealloc/input.c:30:
repr(): RegionOnHeap('PyLongObject', gcc.Location(file='tests/cpychecker/refcounts/use_after_dealloc/input.c', line=30))
str(): PyLongObject allocated at tests/cpychecker/refcounts/use_after_dealloc/input.c:30
@@ -36,5 +56,15 @@ Trace 2:
Return value:
repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/use_after_dealloc/input.c', line=33), value=0L)
str(): (struct PyObject *)0L from tests/cpychecker/refcounts/use_after_dealloc/input.c:33
+ 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/use_after_dealloc/input.c', line=27), 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: RefcountValue(0)
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/use_after_dealloc/input.c', line=27), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
Exception:
RegionForGlobal(gcc.VarDecl('PyExc_MemoryError'))
12 years, 9 months
[gcc-python-plugin] update expected stdout.txt containing pointers, reflecting the regeneration within run-test-suite.py
by David Hugh Malcolm
commit b8e2551412de239ee442e0b62c87af8ef738fa5f
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Thu Aug 18 18:57:00 2011 -0400
update expected stdout.txt containing pointers, reflecting the regeneration within run-test-suite.py
.../absinterp/nonnull-attribute/stdout.txt | 2 +-
.../read-through-global-ptr-checked/stdout.txt | 2 +-
.../read-through-global-ptr-unchecked/stdout.txt | 2 +-
tests/plugin/switch/stdout.txt | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
---
diff --git a/tests/cpychecker/absinterp/nonnull-attribute/stdout.txt b/tests/cpychecker/absinterp/nonnull-attribute/stdout.txt
index e0dbd97..716f3c2 100644
--- a/tests/cpychecker/absinterp/nonnull-attribute/stdout.txt
+++ b/tests/cpychecker/absinterp/nonnull-attribute/stdout.txt
@@ -2,7 +2,7 @@ Trace 0:
Transitions:
'returning'
Return value:
- repr(): UnknownValue(gcctype=<gcc.IntegerType object at 0x28fab70>, loc=gcc.Location(file='tests/cpychecker/absinterp/nonnull-attribute/input.c', line=39))
+ repr(): UnknownValue(gcctype=<gcc.IntegerType object at 0xdeadbeef>, loc=gcc.Location(file='tests/cpychecker/absinterp/nonnull-attribute/input.c', line=39))
str(): unknown int from tests/cpychecker/absinterp/nonnull-attribute/input.c:39
Exception:
(struct PyObject *)0 from tests/cpychecker/absinterp/nonnull-attribute/input.c:38
diff --git a/tests/cpychecker/absinterp/read-through-global-ptr-checked/stdout.txt b/tests/cpychecker/absinterp/read-through-global-ptr-checked/stdout.txt
index e684ff1..550d373 100644
--- a/tests/cpychecker/absinterp/read-through-global-ptr-checked/stdout.txt
+++ b/tests/cpychecker/absinterp/read-through-global-ptr-checked/stdout.txt
@@ -4,7 +4,7 @@ Trace 0:
'taking True path'
'returning'
Return value:
- repr(): UnknownValue(gcctype=<gcc.IntegerType object at 0x3481c90>, loc=gcc.Location(file='tests/cpychecker/absinterp/read-through-global-ptr-checked/input.c', line=36))
+ repr(): UnknownValue(gcctype=<gcc.IntegerType object at 0xdeadbeef>, loc=gcc.Location(file='tests/cpychecker/absinterp/read-through-global-ptr-checked/input.c', line=36))
str(): unknown int from tests/cpychecker/absinterp/read-through-global-ptr-checked/input.c:36
Exception:
(struct PyObject *)0 from tests/cpychecker/absinterp/read-through-global-ptr-checked/input.c:34
diff --git a/tests/cpychecker/absinterp/read-through-global-ptr-unchecked/stdout.txt b/tests/cpychecker/absinterp/read-through-global-ptr-unchecked/stdout.txt
index 201980a..3696a01 100644
--- a/tests/cpychecker/absinterp/read-through-global-ptr-unchecked/stdout.txt
+++ b/tests/cpychecker/absinterp/read-through-global-ptr-unchecked/stdout.txt
@@ -3,7 +3,7 @@ Trace 0:
"treating unknown struct coord * from tests/cpychecker/absinterp/read-through-global-ptr-unchecked/input.c:31 as (struct coord *)&Region('heap-region-0')"
'returning'
Return value:
- repr(): UnknownValue(gcctype=<gcc.IntegerType object at 0x2fc3b70>, loc=gcc.Location(file='tests/cpychecker/absinterp/read-through-global-ptr-unchecked/input.c', line=35))
+ repr(): UnknownValue(gcctype=<gcc.IntegerType object at 0xdeadbeef>, loc=gcc.Location(file='tests/cpychecker/absinterp/read-through-global-ptr-unchecked/input.c', line=35))
str(): unknown int from tests/cpychecker/absinterp/read-through-global-ptr-unchecked/input.c:35
Exception:
(struct PyObject *)0 from tests/cpychecker/absinterp/read-through-global-ptr-unchecked/input.c:34
diff --git a/tests/plugin/switch/stdout.txt b/tests/plugin/switch/stdout.txt
index 8e66e31..a1924d0 100644
--- a/tests/plugin/switch/stdout.txt
+++ b/tests/plugin/switch/stdout.txt
@@ -4,7 +4,7 @@ gimple[0]:
str(stmt): 'switch (i) <default: <L4>, case 1 ... 2: <L0>, case 4: <L0>, case 8: <L0>>'
repr(stmt): 'gcc.GimpleSwitch()'
stmt.indexvar: gcc.ParmDecl('i')
- stmt.labels: [<gcc.CaseLabelExpr object at 0x7fb4e6083330>, <gcc.CaseLabelExpr object at 0x7fb4e6083348>, <gcc.CaseLabelExpr object at 0x7fb4e6083360>, <gcc.CaseLabelExpr object at 0x7fb4e6083378>]
+ stmt.labels: [<gcc.CaseLabelExpr object at 0xdeadbeef>, <gcc.CaseLabelExpr object at 0xdeadbeef>, <gcc.CaseLabelExpr object at 0xdeadbeef>, <gcc.CaseLabelExpr object at 0xdeadbeef>]
label[0].low: None
label[0].high: None
label[0].target: gcc.LabelDecl(1591)
12 years, 9 months
[gcc-python-plugin] run-test-suite.py: add support for regenerating the "gold" outputs
by David Hugh Malcolm
commit 868995dabcdb2d096a3fcc7ae775309b90f1ea0d
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Thu Aug 18 18:55:22 2011 -0400
run-test-suite.py: add support for regenerating the "gold" outputs
run-test-suite.py | 13 ++++++++++---
1 files changed, 10 insertions(+), 3 deletions(-)
---
diff --git a/run-test-suite.py b/run-test-suite.py
index 102112c..03c3ae1 100644
--- a/run-test-suite.py
+++ b/run-test-suite.py
@@ -94,9 +94,16 @@ class TestStream:
result += line + '\n'
return result
- def check_for_diff(self, out, err, p, args, label):
+ def check_for_diff(self, out, err, p, args, label, writeback):
actual = self._cleanup(self.actual)
expdata = self._cleanup(self.expdata)
+ if writeback:
+ # Special-case mode: don't compare, instead refresh the "gold"
+ # output by writing back to disk:
+ if self.expdata = '':
+ with open(self.exppath, 'w') as f:
+ f.write(actual)
+ return
if actual != expdata:
raise UnexpectedOutput(out, err, p, args, self, label)
@@ -187,8 +194,8 @@ def run_test(testdir):
sys.stderr.write(err.diff('stderr'))
raise CompilationError(out.actual, err.actual, p, args)
- out.check_for_diff(out.actual, err.actual, p, args, 'stdout')
- err.check_for_diff(out.actual, err.actual, p, args, 'stderr')
+ out.check_for_diff(out.actual, err.actual, p, args, 'stdout', 0)
+ err.check_for_diff(out.actual, err.actual, p, args, 'stderr', 0)
from optparse import OptionParser
12 years, 9 months
[gcc-python-plugin] libcpychecker: implement the "O!" format code for the PyArg_Parse entrypoints
by David Hugh Malcolm
commit 4e570ebbdcdb794f15850b500718e459ac481dd3
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Thu Aug 18 18:06:40 2011 -0400
libcpychecker: implement the "O!" format code for the PyArg_Parse entrypoints
libcpychecker/PyArg_ParseTuple.py | 135 ++++++++++++++++++--
libcpychecker/types.py | 33 +++++
testcpychecker.py | 6 +-
.../PyArg_ParseTuple/code_O_bang/input.c | 110 ++++++++++++++++
.../PyArg_ParseTuple/code_O_bang/script.py | 19 +++
.../PyArg_ParseTuple/code_O_bang/stderr.txt | 19 +++
6 files changed, 307 insertions(+), 15 deletions(-)
---
diff --git a/libcpychecker/PyArg_ParseTuple.py b/libcpychecker/PyArg_ParseTuple.py
index 60084d8..853f7ee 100644
--- a/libcpychecker/PyArg_ParseTuple.py
+++ b/libcpychecker/PyArg_ParseTuple.py
@@ -137,7 +137,16 @@ class FormatUnit:
def __init__(self, code):
self.code = code
+ def get_expected_types(self):
+ # Return a list of the types expected for this format unit,
+ # either as gcc.Type instances, or as instances of AwkwardType
+ raise NotImplementedError
+
class ConcreteUnit(FormatUnit):
+ """
+ The common case: a fragment of a format string that corresponds to a
+ pre-determined list of types (often just a single element)
+ """
def __init__(self, code, expected_types):
FormatUnit.__init__(self, code)
self.expected_types = expected_types
@@ -148,9 +157,118 @@ class ConcreteUnit(FormatUnit):
def __repr__(self):
return 'ConcreteUnit(%r,%r)' % (self.code, self.expected_types)
+class AwkwardType:
+ """
+ Base class for expected types within a format unit that need special
+ handling (e.g. for "O!" and "O&")
+ """
+ def is_compatible(self, actual_type, actual_arg):
+ raise NotImplementedError
+
+ def describe(self):
+ raise NotImplementedError
+
+class TypeCheck(FormatUnit):
+ """
+ Handler for the "O!" format code
+ """
+ def __init__(self, code):
+ FormatUnit.__init__(self, code)
+ self.checker = TypeCheckCheckerType(self)
+ self.result = TypeCheckResultType(self)
+
+ # The gcc.VarDecl for the PyTypeObject, if we know it (or None):
+ self.typeobject = None
+
+ def get_expected_types(self):
+ # We will discover the actual types as we go, using "self" to bind
+ # together the two arguments
+ return [self.checker, self.result]
+
+ def get_other_type(self):
+ if not self.typeobject:
+ return None
+ return get_type_for_typeobject(self.typeobject)
+
+class TypeCheckCheckerType(AwkwardType):
+ def __init__(self, typecheck):
+ self.typecheck = typecheck
+
+ def is_compatible(self, actual_type, actual_arg):
+ # We should be encountering a pointer to a PyTypeObject
+ # The result type (next argument) should be either a PyObject* or
+ # an pointer to the corresponding type.
+ #
+ # For example, "O!" with
+ # &PyCode_Type, &code,
+ # then "code" should be either a PyObject* or a PyCodeObject*
+ # (and &code gets the usual extra level of indirection)
+
+ if str(actual_type) != 'struct PyTypeObject *':
+ return False
+
+ # OK, the type for this argument is good.
+ # Try to record the actual type object, assuming it's a pointer to a
+ # global:
+ if not isinstance(actual_arg, gcc.AddrExpr):
+ return True
+
+ if not isinstance(actual_arg.operand, gcc.VarDecl):
+ return True
+
+ # OK, we have a ptr to a global; record it:
+ self.typecheck.typeobject = actual_arg.operand
+
+ return True
+
+ def describe(self):
+ return '"struct PyTypeObject *"'
+
+class TypeCheckResultType(AwkwardType):
+ def __init__(self, typecheck):
+ self.typecheck = typecheck
+ self.base_type = get_PyObject().pointer.pointer
+
+ def is_compatible(self, actual_type, actual_arg):
+ if not isinstance(actual_type, gcc.PointerType):
+ return False
+
+ # If something went wrong with figuring out the type, we can only check
+ # against PyObject*:
+
+ # (PyObject **) is good:
+ if compatible_type(actual_type,
+ self.base_type):
+ return True
+
+ other_type = self.typecheck.get_other_type()
+ if other_type:
+ if compatible_type(actual_type,
+ other_type.pointer.pointer):
+ return True
+
+ return False
+
+ def describe(self):
+ other_type = self.typecheck.get_other_type()
+ if other_type:
+ return ('%s (based on PyTypeObject: %r) or %s'
+ % (describe_type(other_type.pointer),
+ self.typecheck.typeobject.name,
+ describe_type(self.base_type)))
+ else:
+ if self.typecheck.typeobject:
+ return ('"%s" (unfamiliar with PyTypeObject: %r)'
+ % (describe_type(self.base_type),
+ self.typecheck.typeobject.name))
+ else:
+ return '"%s" (unable to determine relevant PyTypeObject)' % describe_type(self.base_type)
+
-# Handle the "O&" format code
class Conversion(FormatUnit):
+ """
+ Handler for the "O&" format code
+ """
def __init__(self, code):
FormatUnit.__init__(self, code)
self.callback = ConverterCallbackType(self)
@@ -161,15 +279,12 @@ class Conversion(FormatUnit):
# together the two arguments
return [self.callback, self.result]
-class AwkwardType:
- pass
-
class ConverterCallbackType(AwkwardType):
def __init__(self, conv):
self.conv = conv
self.actual_type = None
- def is_compatible(self, actual_type):
+ def is_compatible(self, actual_type, actual_arg):
# We should be encountering a function pointer of type:
# int (fn)(PyObject *, T*)
# The result type (next argument) should be a T*
@@ -210,7 +325,7 @@ class ConverterResultType(AwkwardType):
self.conv = conv
self.type = None
- def is_compatible(self, actual_type):
+ def is_compatible(self, actual_type, actual_arg):
if not isinstance(actual_type, gcc.PointerType):
return False
@@ -340,9 +455,7 @@ class PyArgParseFmt:
get_PyObject().pointer.pointer)])
elif c == 'O': # object
if next == '!':
- result.add_argument('O!',
- [get_PyTypeObject().pointer,
- get_PyObject().pointer.pointer])
+ result.args.append(TypeCheck('O!'))
i += 1
elif next == '?':
raise UnhandledCode(richloc, fmt_string, c + next) # FIXME
@@ -489,9 +602,9 @@ def compatible_type(exp_type, actual_type, actualarg=None):
# Didn't match any of them:
return False
- # Support the "O&" converter code:
+ # Support the "O!" and "O&" converter codes:
if isinstance(exp_type, AwkwardType):
- return exp_type.is_compatible(actual_type)
+ return exp_type.is_compatible(actual_type, actualarg)
# Support the codes that can accept NULL:
if isinstance(exp_type, NullPointer):
diff --git a/libcpychecker/types.py b/libcpychecker/types.py
index 6990007..ec24d51 100644
--- a/libcpychecker/types.py
+++ b/libcpychecker/types.py
@@ -74,3 +74,36 @@ def get_Py_complex():
# Python 3:
def get_PyBytesObject():
return get_global_typedef('PyBytesObject')
+
+# Map from name of PyTypeObject global to the typedef for the corresponding
+# object structure:
+type_dict = {
+ 'PyBuffer_Type' : 'PyBufferObject',
+ 'PyComplex_Type' : 'PyComplexObject',
+ 'PyCode_Type' : 'PyCodeObject',
+ 'PyDict_Type' : 'PyDictObject',
+ 'PyFile_Type' : 'PyFileObject',
+ 'PyFloat_Type' : 'PyFloatObject',
+ 'PyFrame_Type' : 'PyFrameObject',
+ 'PyFunction_Type' : 'PyFunctionObject',
+ 'PyInt_Type' : 'PyIntObject',
+ 'PyList_Type' : 'PyListObject',
+ 'PyLong_Type' : 'PyLongObject',
+ 'PyModule_Type' : 'PyModuleObject',
+ 'PyCapsule_Type' : 'PyCapsuleObject',
+ 'PyRange_Type' : 'PyRangeObject',
+ 'PySet_Type' : 'PySetObject',
+ 'PyFrozenSet_Type' : 'PyFrozenSetObject',
+ 'PyString_Type' : 'PyStringObject',
+ 'PyTuple_Type' : 'PyTupleObject',
+ 'PyType_Type' : 'PyTypeObject',
+ 'PyUnicode_Type' : 'PyUnicodeObject',
+}
+
+def get_type_for_typeobject(typeobject):
+ assert isinstance(typeobject, gcc.VarDecl)
+ if typeobject.name not in type_dict:
+ return None
+
+ type_name = type_dict[typeobject.name]
+ return get_global_typedef(type_name)
diff --git a/testcpychecker.py b/testcpychecker.py
index 54beb69..d6d0659 100644
--- a/testcpychecker.py
+++ b/testcpychecker.py
@@ -386,10 +386,8 @@ correct_usage(PyObject *self, PyObject *args)
def test_format_code_O(self):
self._test_format_code('O', ['PyObject *'], '"struct PyObject * *"')
- def test_format_code_O_bang(self):
- self._test_format_code('O!',
- ['PyTypeObject', 'PyObject *'],
- ['"struct PyTypeObject *"', '"struct PyObject *"'])
+ # Code "O!" is tested by:
+ # tests/cpychecker/PyArg_ParseTuple/code_O_bang/
# Code "O&" is tested by:
# tests/cpychecker/PyArg_ParseTuple/correct_converter/
diff --git a/tests/cpychecker/PyArg_ParseTuple/code_O_bang/input.c b/tests/cpychecker/PyArg_ParseTuple/code_O_bang/input.c
new file mode 100644
index 0000000..5836715
--- /dev/null
+++ b/tests/cpychecker/PyArg_ParseTuple/code_O_bang/input.c
@@ -0,0 +1,110 @@
+/*
+ Copyright 2011 David Malcolm <dmalcolm(a)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/>.
+*/
+
+/*
+ The Python source tree uses "O!" in a couple of places with explicit PyObject
+ subclassses:
+
+Objects/funcobject.c: In function ‘func_new’:
+Objects/funcobject.c:377:37: error: Mismatching type in call to PyArg_ParseTupleAndKeywords with format code "O!O!|OOO:function" [-fpermissive]
+ argument 6 ("&code") had type
+ "struct PyCodeObject * *"
+ but was expecting
+ "PyObject * *"
+ for format code "O!"
+
+Objects/typeobject.c: In function ‘super_init’:
+Objects/typeobject.c:6632:26: error: Mismatching type in call to PyArg_ParseTuple with format code "O!|O:super" [-fpermissive]
+ argument 4 ("&type") had type
+ "struct PyTypeObject * *"
+ but was expecting
+ "PyObject * *"
+ for format code "O!"
+
+ "O!" does check that the object is of the appropriate type, so the usage is
+ reasonable.
+
+ This plugin itself uses it in a few places. FIXME: does it?
+
+ We can examine a struct and verify that it has the same first fields as the
+ fields of a PyObject.
+
+ But how to map back from a PyTypeObject instance to the struct?
+ (i.e. how to ensure that the specific subclass type is correct?
+ (we might now even have the defn of the type object)
+*/
+#include <Python.h>
+
+/*
+ Example of a PyTypeObject which we can't know the value of at compile time:
+*/
+extern PyTypeObject *unknown_type_obj_ptr;
+
+/*
+ Example of an extension type, where cpychecker has no way to associate
+ the PyTypeObject with, say, "struct ExtensionObject"
+*/
+
+PyTypeObject Extension_Type = {};
+
+PyObject *
+handle_subclasses(PyObject *self, PyObject *args)
+{
+ PyCodeObject *code_obj;
+ PyObject *base_obj;
+ PyTypeObject *type_obj = NULL;
+ PyLongObject *long_obj;
+ struct UnknownObject *unknown_obj;
+ struct ExtensionObject *extension_obj;
+
+ if (!PyArg_ParseTuple(args, "O!O!O!O!O!O!",
+ /* This is correct, PyCode_Type -> PyCodeObject */
+ &PyCode_Type, &code_obj,
+
+ /* This is correct: PyCode_Type -> PyObject also */
+ &PyCode_Type, &base_obj,
+
+ /* This is correct: PyType_Type -> PyTypeObject*/
+ &PyType_Type, &type_obj,
+
+ /* This is incorrect: wrong subclass (unicode vs long): */
+ &PyUnicode_Type, long_obj,
+
+ /* This must report a warning; we can't know which
+ PyTypeObject is in use, and thus can't know if
+ struct UnknownObject is OK: */
+ unknown_type_obj_ptr, &unknown_obj,
+
+ /* This must report a warning: we don't know about
+ the association between Extension_Type and
+ struct ExtensionObject: */
+ &Extension_Type, &extension_obj)) {
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/PyArg_ParseTuple/code_O_bang/script.py b/tests/cpychecker/PyArg_ParseTuple/code_O_bang/script.py
new file mode 100644
index 0000000..e120c53
--- /dev/null
+++ b/tests/cpychecker/PyArg_ParseTuple/code_O_bang/script.py
@@ -0,0 +1,19 @@
+# Copyright 2011 David Malcolm <dmalcolm(a)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()
diff --git a/tests/cpychecker/PyArg_ParseTuple/code_O_bang/stderr.txt b/tests/cpychecker/PyArg_ParseTuple/code_O_bang/stderr.txt
new file mode 100644
index 0000000..3f19795
--- /dev/null
+++ b/tests/cpychecker/PyArg_ParseTuple/code_O_bang/stderr.txt
@@ -0,0 +1,19 @@
+tests/cpychecker/PyArg_ParseTuple/code_O_bang/input.c: In function 'handle_subclasses':
+tests/cpychecker/PyArg_ParseTuple/code_O_bang/input.c:72:19: error: Mismatching type in call to PyArg_ParseTuple with format code "O!O!O!O!O!O!" [-fpermissive]
+ argument 10 ("gcc.VarDecl('long_obj')") had type
+ "struct PyLongObject *"
+ but was expecting
+ "struct PyUnicodeObject *" (based on PyTypeObject: 'PyUnicode_Type') or "struct PyObject * *"
+ for format code "O!"
+tests/cpychecker/PyArg_ParseTuple/code_O_bang/input.c:76:26: error: Mismatching type in call to PyArg_ParseTuple with format code "O!O!O!O!O!O!" [-fpermissive]
+ argument 12 ("&unknown_obj") had type
+ "struct UnknownObject * *"
+ but was expecting
+ ""struct PyObject * *"" (unable to determine relevant PyTypeObject)
+ for format code "O!"
+tests/cpychecker/PyArg_ParseTuple/code_O_bang/input.c:76:26: error: Mismatching type in call to PyArg_ParseTuple with format code "O!O!O!O!O!O!" [-fpermissive]
+ argument 14 ("&extension_obj") had type
+ "struct ExtensionObject * *"
+ but was expecting
+ ""struct PyObject * *"" (unfamiliar with PyTypeObject: 'Extension_Type')
+ for format code "O!"
12 years, 9 months
[gcc-python-plugin] libcpychecker: cope with nested structures
by David Hugh Malcolm
commit ed3c536a4941049f72f8a98e0b5cfc30376be1ca
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Thu Aug 18 16:57:20 2011 -0400
libcpychecker: cope with nested structures
libcpychecker/absinterp.py | 7 ++-
tests/cpychecker/absinterp/nested-fields/input.c | 59 ++++++++++++++++++++
tests/cpychecker/absinterp/nested-fields/script.py | 22 +++++++
.../cpychecker/absinterp/nested-fields/stdout.txt | 8 +++
4 files changed, 95 insertions(+), 1 deletions(-)
---
diff --git a/libcpychecker/absinterp.py b/libcpychecker/absinterp.py
index 7fe67af..b40dd95 100644
--- a/libcpychecker/absinterp.py
+++ b/libcpychecker/absinterp.py
@@ -579,7 +579,7 @@ class State:
if loc:
assert isinstance(loc, gcc.Location)
#cr.debug()
- log('target: %r' % cr.target)
+ log('target: %r %s ' % (cr.target, cr.target))
log('field: %r' % cr.field)
#fr = FIXME #self.make_field_region(target, field)
if 1: # fr not in self.region_for_var:
@@ -602,6 +602,11 @@ class State:
vr = self.var_region(cr.target)
log(vr)
return self.make_field_region(vr, cr.field.name)
+ elif isinstance(cr.target, gcc.ComponentRef):
+ # nested field:
+ vr = self.get_field_region(cr.target, loc)
+ log(vr)
+ return self.make_field_region(vr, cr.field.name)
log('cr: %r %s' % (cr, cr))
return self.region_for_var[cr]
diff --git a/tests/cpychecker/absinterp/nested-fields/input.c b/tests/cpychecker/absinterp/nested-fields/input.c
new file mode 100644
index 0000000..69d0b82
--- /dev/null
+++ b/tests/cpychecker/absinterp/nested-fields/input.c
@@ -0,0 +1,59 @@
+/*
+ Copyright 2011 David Malcolm <dmalcolm(a)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/>.
+*/
+
+#include <Python.h>
+
+/*
+ Test that we can cope with nested structures
+*/
+struct FooType {
+ int i;
+};
+
+struct BarType {
+ struct FooType foo;
+ int j;
+};
+
+struct BazType {
+ struct BarType bar;
+ int k;
+};
+
+int test_nested(void)
+{
+ struct BazType baz;
+
+ baz.bar.foo.i = 10;
+ baz.bar.j = 100;
+ baz.k = 1000;
+
+ /*
+ The analyser ought to be able to figure out that the return value is 1110
+ */
+ return baz.bar.foo.i + baz.bar.j + baz.k;
+}
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/absinterp/nested-fields/script.py b/tests/cpychecker/absinterp/nested-fields/script.py
new file mode 100644
index 0000000..6bb62fd
--- /dev/null
+++ b/tests/cpychecker/absinterp/nested-fields/script.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+# Copyright 2011 David Malcolm <dmalcolm(a)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)
diff --git a/tests/cpychecker/absinterp/nested-fields/stdout.txt b/tests/cpychecker/absinterp/nested-fields/stdout.txt
new file mode 100644
index 0000000..3bc65c9
--- /dev/null
+++ b/tests/cpychecker/absinterp/nested-fields/stdout.txt
@@ -0,0 +1,8 @@
+Trace 0:
+ Transitions:
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='int', loc=gcc.Location(file='tests/cpychecker/absinterp/nested-fields/input.c', line=50), value=1110L)
+ str(): (int)1110L from tests/cpychecker/absinterp/nested-fields/input.c:50
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/absinterp/nested-fields/input.c:40
12 years, 9 months
[gcc-python-plugin] python 3 fix
by David Hugh Malcolm
commit af30bc4d8e5a24fe651285fd856307b6e0a169c4
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Tue Aug 16 19:54:09 2011 -0400
python 3 fix
libcpychecker/absinterp.py | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)
---
diff --git a/libcpychecker/absinterp.py b/libcpychecker/absinterp.py
index 9c8d645..7fe67af 100644
--- a/libcpychecker/absinterp.py
+++ b/libcpychecker/absinterp.py
@@ -950,10 +950,11 @@ def iter_traces(fun, stateclass, prefix=None):
trace_with_err.add_error(err)
trace_with_err.log(log, 'FINISHED TRACE WITH ERROR: %s' % err, 1)
return [trace_with_err]
- except SplitValue, err:
+ except SplitValue:
# Split the state up, splitting into parallel worlds with different
# values for the given value
# FIXME: this doesn't work; it thinks it's a loop :(
+ err = sys.exc_info()[1]
transitions = err.split(curstate)
assert isinstance(transitions, list)
12 years, 9 months
[gcc-python-plugin] cpychecker: Implement detect of PyObject* subclasses
by David Hugh Malcolm
commit 31abcda1a680a7237b3af2cfa1df98fabd5bdc2f
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Tue Aug 16 19:53:44 2011 -0400
cpychecker: Implement detect of PyObject* subclasses
libcpychecker/refcounts.py | 33 +++++++++
libcpychecker/types.py | 7 ++
.../refcounts/subclass/detection/input.c | 49 ++++++++++++++
.../refcounts/subclass/detection/script.py | 69 ++++++++++++++++++++
.../refcounts/subclass/detection/stdout.txt | 29 ++++++++
5 files changed, 187 insertions(+), 0 deletions(-)
---
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index 9d34117..7140fa6 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -28,6 +28,7 @@ from gccutils import cfg_to_dot, invoke_dot, get_src_for_loc
from libcpychecker.absinterp import *
from libcpychecker.diagnostics import Reporter
from libcpychecker.PyArg_ParseTuple import log
+from libcpychecker.types import is_py3k, is_debug_build
def stmt_is_assignment_to_count(stmt):
if hasattr(stmt, 'lhs'):
@@ -45,6 +46,38 @@ def type_is_pyobjptr(t):
if str(t) == 'struct PyObject *':
return True
+def type_is_pyobjptr_subclass(t):
+ assert t is None or isinstance(t, gcc.Type)
+ # It must be a pointer:
+ if not isinstance(t, gcc.PointerType):
+ return False
+
+ # ...to a struct:
+ if not isinstance(t.dereference, gcc.RecordType):
+ return False
+
+ fieldnames = [f.name for f in t.dereference.fields]
+ if is_py3k():
+ # For Python 3, the first field must be "ob_base", or it must be "PyObject":
+ if str(t) == 'struct PyObject *':
+ return True
+ if fieldnames[0] != 'ob_base':
+ return False
+ else:
+ # For Python 2, the first two fields must be "ob_refcnt" and "ob_type".
+ # (In a debug build, these are preceded by _ob_next and _ob_prev)
+ # FIXME: debug builds!
+ if is_debug_build():
+ if fieldnames[:4] != ['_ob_next', '_ob_prev',
+ 'ob_refcnt', 'ob_type']:
+ return False
+ else:
+ if fieldnames[:2] != ['ob_refcnt', 'ob_type']:
+ return False
+
+ # Passed all tests:
+ return True
+
def stmt_is_assignment_to_objptr(stmt):
if hasattr(stmt, 'lhs'):
if stmt.lhs:
diff --git a/libcpychecker/types.py b/libcpychecker/types.py
index 8b1647b..6990007 100644
--- a/libcpychecker/types.py
+++ b/libcpychecker/types.py
@@ -30,6 +30,13 @@ def is_py3k():
else:
return True
+def is_debug_build():
+ """
+ Is the Python.h we're compiling against configured --with-pydebug ?
+ """
+ obj = get_global_typedef('PyObject')
+ return obj.type.fields[0].name == '_ob_next'
+
def get_Py_ssize_t():
return get_global_typedef('Py_ssize_t')
diff --git a/tests/cpychecker/refcounts/subclass/detection/input.c b/tests/cpychecker/refcounts/subclass/detection/input.c
new file mode 100644
index 0000000..f883d41
--- /dev/null
+++ b/tests/cpychecker/refcounts/subclass/detection/input.c
@@ -0,0 +1,49 @@
+/*
+ Copyright 2011 David Malcolm <dmalcolm(a)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/>.
+*/
+
+#include <Python.h>
+
+/*
+ Verify that the checker can detect PyObject subclasses
+*/
+
+/* The checker should treat this as a PyObject subclass */
+struct FooObject {
+ PyObject_HEAD
+ int i;
+};
+
+/* Likewise */
+struct BarObject {
+ PyObject_VAR_HEAD
+ char *str;
+};
+
+/* This isn't one: */
+struct NotAnObject {
+ PyObject *array[10];
+};
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/refcounts/subclass/detection/script.py b/tests/cpychecker/refcounts/subclass/detection/script.py
new file mode 100644
index 0000000..a014fbb
--- /dev/null
+++ b/tests/cpychecker/refcounts/subclass/detection/script.py
@@ -0,0 +1,69 @@
+# -*- coding: utf-8 -*-
+# Copyright 2011 David Malcolm <dmalcolm(a)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/>.
+
+import gcc
+from gccutils import get_global_typedef
+
+# Verify that this function works:
+from libcpychecker.refcounts import type_is_pyobjptr_subclass
+
+def get_struct(tagname):
+ for u in gcc.get_translation_units():
+ for v in u.block.vars:
+ if isinstance(v, gcc.TypeDecl):
+ if isinstance(v.type, gcc.RecordType):
+ if isinstance(v.type.name, gcc.IdentifierNode):
+ if v.type.name.name == tagname:
+ return v.type
+
+def check_type(t):
+ print('type: %s' % t)
+ print(' is PyObject* subclass?: %s\n' % type_is_pyobjptr_subclass(t))
+
+def check_typedef(typedef):
+ print('typedef: %r' % typedef)
+ decl = get_global_typedef(typedef)
+ ptr_type = decl.type.pointer
+ print(' ptr_type: %s' % ptr_type)
+ print(' is PyObject* subclass?: %s\n' % type_is_pyobjptr_subclass(ptr_type))
+
+def check_struct(typename):
+ print('typename: %r' % typename)
+ struct_type = get_struct(typename)
+ print(' struct_type: %s' % struct_type)
+ ptr_type = struct_type.pointer
+ print(' ptr_type: %s' % ptr_type)
+ print(' is PyObject* subclass?: %s\n' % type_is_pyobjptr_subclass(ptr_type))
+
+def on_pass_execution(p, fn):
+ if p.name == '*free_lang_data':
+ # The '*free_lang_data' pass is called once, rather than per-function,
+ # and occurs immediately after "*build_cgraph_edges", which is the
+ # pass that initially builds the callgraph
+ #
+ # So at this point we're likely to get a good view of the types:
+ check_type(gcc.Type.int())
+ check_type(gcc.Type.char().pointer)
+ check_typedef('PyObject')
+ check_typedef('PyTypeObject')
+ check_struct('FooObject')
+ check_struct('BarObject')
+ check_struct('NotAnObject')
+
+gcc.register_callback(gcc.PLUGIN_PASS_EXECUTION,
+ on_pass_execution)
diff --git a/tests/cpychecker/refcounts/subclass/detection/stdout.txt b/tests/cpychecker/refcounts/subclass/detection/stdout.txt
new file mode 100644
index 0000000..66b5147
--- /dev/null
+++ b/tests/cpychecker/refcounts/subclass/detection/stdout.txt
@@ -0,0 +1,29 @@
+type: int
+ is PyObject* subclass?: False
+
+type: char *
+ is PyObject* subclass?: False
+
+typedef: 'PyObject'
+ ptr_type: struct PyObject *
+ is PyObject* subclass?: True
+
+typedef: 'PyTypeObject'
+ ptr_type: struct PyTypeObject *
+ is PyObject* subclass?: True
+
+typename: 'FooObject'
+ struct_type: struct FooObject
+ ptr_type: struct FooObject *
+ is PyObject* subclass?: True
+
+typename: 'BarObject'
+ struct_type: struct BarObject
+ ptr_type: struct BarObject *
+ is PyObject* subclass?: True
+
+typename: 'NotAnObject'
+ struct_type: struct NotAnObject
+ ptr_type: struct NotAnObject *
+ is PyObject* subclass?: False
+
12 years, 9 months
[gcc-python-plugin] cpychecker: support switch statements
by David Hugh Malcolm
commit d79207f0872d6c4a5a2d8dc9947e968e7b60fd4b
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Wed Aug 10 18:45:37 2011 -0400
cpychecker: support switch statements
docs/cfg.rst | 5 ++
docs/tree.rst | 12 ++++--
gcc-python-cfg.c | 33 +++++++++++++++
gcc-python-wrappers.h | 3 +
generate-cfg-c.py | 8 ++++
generate-tree-c.py | 2 +-
libcpychecker/absinterp.py | 3 +
libcpychecker/refcounts.py | 56 ++++++++++++++++++++++----
tests/cpychecker/absinterp/switch/input.c | 49 ++++++++++++++++++++++
tests/cpychecker/absinterp/switch/script.py | 22 ++++++++++
tests/cpychecker/absinterp/switch/stdout.txt | 45 +++++++++++++++++++++
11 files changed, 225 insertions(+), 13 deletions(-)
---
diff --git a/docs/cfg.rst b/docs/cfg.rst
index ed6b7fa..8ba82ee 100644
--- a/docs/cfg.rst
+++ b/docs/cfg.rst
@@ -80,6 +80,11 @@ You can get at the control flow graph of a :py:class:`gcc.Function` via its
Instance of :py:class:`gcc.BasicBlock`: the final one within this CFG
+ .. py:method:: get_block_for_label(labeldecl)
+
+ Given a :py:class:`gcc.LabelDecl`, get the corresponding
+ :py:class:`gcc.BasicBlock`
+
You can use ``gccutils.cfg_to_dot`` to render a gcc.Cfg as a graphviz
diagram. It will render the diagram, showing each basic block, with
source code on the left-hand side, interleaved with the "gimple"
diff --git a/docs/tree.rst b/docs/tree.rst
index ce79eed..29f5b6d 100644
--- a/docs/tree.rst
+++ b/docs/tree.rst
@@ -849,13 +849,17 @@ Statements
.. py:attribute:: low
- The low value of the case label, as a :py:class:`gcc.Tree` (or `None`
- for the default)
+ * for single-valued case labels, the value, as a :py:class:`gcc.Tree`
+
+ * for range-valued case labels, the lower bound, as a :py:class:`gcc.Tree`
+
+ * `None` for the default label
.. py:attribute:: high
- The high value of the case label, as a :py:class:`gcc.Tree` (or `None`
- for the default)
+ For range-valued case labels, the upper bound, as a :py:class:`gcc.Tree`.
+
+ `None` for single-valued case labels, and for the default label
.. py:attribute:: target
diff --git a/gcc-python-cfg.c b/gcc-python-cfg.c
index 697f6e2..f533f72 100644
--- a/gcc-python-cfg.c
+++ b/gcc-python-cfg.c
@@ -461,6 +461,39 @@ gcc_Cfg_get_basic_blocks(PyGccCfg *self, void *closure)
return NULL;
}
+extern PyTypeObject gcc_LabelDeclType;
+
+PyObject *
+gcc_Cfg_get_block_for_label(PyObject *s, PyObject *args)
+{
+ struct PyGccCfg *self = (struct PyGccCfg *)s;
+ struct PyGccTree *label_decl;
+ int uid;
+ basic_block bb;
+
+ if (!PyArg_ParseTuple(args,
+ "O!:get_block_for_label",
+ &gcc_LabelDeclType, &label_decl)) {
+ return NULL;
+ }
+
+ /* See also gcc/tree-cfg.c: label_to_block_fn */
+ uid = LABEL_DECL_UID(label_decl->t);
+
+ if (uid < 0 ||
+ (VEC_length (basic_block, self->cfg->x_label_to_block_map)
+ <=
+ (unsigned int) uid)
+ ) {
+ return PyErr_Format(PyExc_ValueError,
+ "uid %i not found", uid);
+ }
+
+ bb = VEC_index(basic_block, self->cfg->x_label_to_block_map, uid);
+
+ return gcc_python_make_wrapper_basic_block(bb);
+}
+
PyObject *
gcc_python_make_wrapper_cfg(struct control_flow_graph *cfg)
{
diff --git a/gcc-python-wrappers.h b/gcc-python-wrappers.h
index 1a29a85..a0eeab0 100644
--- a/gcc-python-wrappers.h
+++ b/gcc-python-wrappers.h
@@ -79,6 +79,9 @@ gcc_BasicBlock_get_rtl(PyGccBasicBlock *self, void *closure);
PyObject *
gcc_Cfg_get_basic_blocks(PyGccCfg *self, void *closure);
+PyObject *
+gcc_Cfg_get_block_for_label(PyObject *self, PyObject *args);
+
/* gcc-python-tree.c: */
PyObject *
gcc_Tree_str(struct PyGccTree * self);
diff --git a/generate-cfg-c.py b/generate-cfg-c.py
index c9e9ea8..0de903d 100644
--- a/generate-cfg-c.py
+++ b/generate-cfg-c.py
@@ -163,6 +163,14 @@ def generate_cfg():
#tp_str = '(reprfunc)gcc_Cfg_repr',
tp_getset = getsettable.identifier,
)
+ methods = PyMethodTable('gcc_Cfg_methods', [])
+ methods.add_method('get_block_for_label',
+ 'gcc_Cfg_get_block_for_label',
+ 'METH_VARARGS',
+ "Given a gcc.LabelDecl, get the corresponding gcc.BasicBlock")
+ cu.add_defn(methods.c_defn())
+ pytype.tp_methods = methods.identifier
+
cu.add_defn(pytype.c_defn())
modinit_preinit += pytype.c_invoke_type_ready()
modinit_postinit += pytype.c_invoke_add_to_module()
diff --git a/generate-tree-c.py b/generate-tree-c.py
index ded1acf..44e4594 100644
--- a/generate-tree-c.py
+++ b/generate-tree-c.py
@@ -480,7 +480,7 @@ def generate_tree_code_classes():
"The low value of the case label, as a gcc.Tree (or None for the default)")
add_simple_getter('high',
'gcc_python_make_wrapper_tree(CASE_HIGH(self->t))',
- "The high value of the case label, as a gcc.Tree (or None for the default)")
+ "The high value of the case label, if any, as a gcc.Tree (None for the default and for single-valued case labels)")
add_simple_getter('target',
'gcc_python_make_wrapper_tree(CASE_LABEL(self->t))',
"The target of the case label, as a gcc.LabelDecl")
diff --git a/libcpychecker/absinterp.py b/libcpychecker/absinterp.py
index ec811a6..9c8d645 100644
--- a/libcpychecker/absinterp.py
+++ b/libcpychecker/absinterp.py
@@ -404,6 +404,8 @@ class State:
self.return_rvalue,
self.has_returned,
self.not_returning)
+ if hasattr(self, 'fun'):
+ c.fun = self.fun
return c
def verify(self):
@@ -694,6 +696,7 @@ class State:
def init_for_function(self, fun):
log('init_for_function(%r)' % fun)
+ self.fun = fun
root_region = Region('root', None)
stack = Region('stack for %s' % fun.decl.name, root_region)
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index 4884f12..9d34117 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -121,13 +121,16 @@ class MyState(State):
self.exception_rvalue = exception_rvalue
def copy(self):
- return self.__class__(self.loc,
- self.region_for_var.copy(),
- self.value_for_region.copy(),
- self.return_rvalue,
- self.owned_refs[:],
- self.resources.copy(),
- self.exception_rvalue)
+ c = self.__class__(self.loc,
+ self.region_for_var.copy(),
+ self.value_for_region.copy(),
+ self.return_rvalue,
+ self.owned_refs[:],
+ self.resources.copy(),
+ self.exception_rvalue)
+ if hasattr(self, 'fun'):
+ c.fun = self.fun
+ return c
def _extra(self):
return ' %s' % self.owned_refs
@@ -168,13 +171,17 @@ class MyState(State):
if isinstance(stmt, gcc.GimpleCall):
return self._get_transitions_for_GimpleCall(stmt)
elif isinstance(stmt, (gcc.GimpleDebug, gcc.GimpleLabel)):
- return [self.use_next_loc()]
+ return [Transition(self,
+ self.use_next_loc(),
+ None)]
elif isinstance(stmt, gcc.GimpleCond):
return self._get_transitions_for_GimpleCond(stmt)
elif isinstance(stmt, gcc.GimpleReturn):
return self._get_transitions_for_GimpleReturn(stmt)
elif isinstance(stmt, gcc.GimpleAssign):
return self._get_transitions_for_GimpleAssign(stmt)
+ elif isinstance(stmt, gcc.GimpleSwitch):
+ return self._get_transitions_for_GimpleSwitch(stmt)
else:
raise NotImplementedError("Don't know how to cope with %r (%s) at %s"
% (stmt, stmt, stmt.loc))
@@ -632,6 +639,39 @@ class MyState(State):
nextstate.has_returned = True
return [Transition(self, nextstate, 'returning')]
+ def _get_transitions_for_GimpleSwitch(self, stmt):
+ def get_labels_for_rvalue(self, stmt, rvalue):
+ # Gather all possible labels for the given rvalue
+ result = []
+ for label in stmt.labels:
+ # FIXME: for now, treat all labels as possible:
+ result.append(label)
+ return result
+ log('stmt.indexvar: %r' % stmt.indexvar)
+ log('stmt.labels: %r' % (stmt.labels,))
+ indexval = self.eval_rvalue(stmt.indexvar, stmt.loc)
+ log('indexval: %r' % indexval)
+ labels = get_labels_for_rvalue(self, stmt, indexval)
+ log('labels: %r' % labels)
+ result = []
+ for label in labels:
+ newstate = self.copy()
+ bb = self.fun.cfg.get_block_for_label(label.target)
+ newstate.loc = Location(bb, 0)
+ if label.low:
+ assert isinstance(label.low, gcc.IntegerCst)
+ if label.high:
+ assert isinstance(label.high, gcc.IntegerCst)
+ desc = 'following cases %i...%i' % (label.low.constant, label.high.constant)
+ else:
+ desc = 'following case %i' % label.low.constant
+ else:
+ desc = 'following default'
+ result.append(Transition(self,
+ newstate,
+ desc))
+ return result
+
def get_traces(fun):
return list(iter_traces(fun, MyState))
diff --git a/tests/cpychecker/absinterp/switch/input.c b/tests/cpychecker/absinterp/switch/input.c
new file mode 100644
index 0000000..aa4c83c
--- /dev/null
+++ b/tests/cpychecker/absinterp/switch/input.c
@@ -0,0 +1,49 @@
+/*
+ Copyright 2011 David Malcolm <dmalcolm(a)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/>.
+*/
+
+#include <Python.h>
+
+/*
+ Test of switch statement
+*/
+const char* test_switch(int i)
+{
+ switch (i) {
+ case 0:
+ return "Red";
+ default:
+ case 1:
+ return "Green";
+ case 2:
+ return "Blue";
+
+ case 3:
+ case 4:
+ case 5:
+ return NULL;
+ }
+}
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/absinterp/switch/script.py b/tests/cpychecker/absinterp/switch/script.py
new file mode 100644
index 0000000..6bb62fd
--- /dev/null
+++ b/tests/cpychecker/absinterp/switch/script.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+# Copyright 2011 David Malcolm <dmalcolm(a)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)
diff --git a/tests/cpychecker/absinterp/switch/stdout.txt b/tests/cpychecker/absinterp/switch/stdout.txt
new file mode 100644
index 0000000..1fbe6d7
--- /dev/null
+++ b/tests/cpychecker/absinterp/switch/stdout.txt
@@ -0,0 +1,45 @@
+Trace 0:
+ Transitions:
+ 'following default'
+ 'returning'
+ Return value:
+ repr(): PointerToRegion(gcctype='char[6] *', loc=gcc.Location(file='tests/cpychecker/absinterp/switch/input.c', line=32), region=RegionForStringConstant('Green'))
+ str(): (char[6] *)&RegionForStringConstant('Green') from tests/cpychecker/absinterp/switch/input.c:32
+ r->ob_refcnt: None
+ r->ob_type: None
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/absinterp/switch/input.c:26
+
+Trace 1:
+ Transitions:
+ 'following case 0'
+ 'returning'
+ Return value:
+ repr(): PointerToRegion(gcctype='char[4] *', loc=gcc.Location(file='tests/cpychecker/absinterp/switch/input.c', line=29), region=RegionForStringConstant('Red'))
+ str(): (char[4] *)&RegionForStringConstant('Red') from tests/cpychecker/absinterp/switch/input.c:29
+ r->ob_refcnt: None
+ r->ob_type: None
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/absinterp/switch/input.c:26
+
+Trace 2:
+ Transitions:
+ 'following case 2'
+ 'returning'
+ Return value:
+ repr(): PointerToRegion(gcctype='char[5] *', loc=gcc.Location(file='tests/cpychecker/absinterp/switch/input.c', line=34), region=RegionForStringConstant('Blue'))
+ str(): (char[5] *)&RegionForStringConstant('Blue') from tests/cpychecker/absinterp/switch/input.c:34
+ r->ob_refcnt: None
+ r->ob_type: None
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/absinterp/switch/input.c:26
+
+Trace 3:
+ Transitions:
+ 'following cases 3...5'
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='const char *', loc=gcc.Location(file='tests/cpychecker/absinterp/switch/input.c', line=39), value=0L)
+ str(): (const char *)0L from tests/cpychecker/absinterp/switch/input.c:39
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/absinterp/switch/input.c:26
12 years, 10 months