[gcc-python-plugin] cpychecker: show the history of all changes to an object's ob_refcnt when reporting a refcount error
by David Hugh Malcolm
commit 7f1deecfd61291e9015161acaeac8aa4acd6aea8
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Wed Aug 24 19:51:38 2011 -0400
cpychecker: show the history of all changes to an object's ob_refcnt when reporting a refcount error
Introduce Annotator class, to allow injecting extra messages when describing
the flow through a function (for use by both the text and HTML reports)
Add a RefcountAnnotator subclass, to inject notes into reference count error
messages, detailing whenever the ob_refcnt changes
libcpychecker/absinterp.py | 22 ++++++----
libcpychecker/diagnostics.py | 44 ++++++++++++++++++--
libcpychecker/refcounts.py | 44 ++++++++++++++++++-
libcpychecker/visualizations.py | 15 ++++++-
.../cpychecker/refcounts/missing_decref/stderr.txt | 1 +
tests/cpychecker/refcounts/object_leak/stderr.txt | 1 +
.../refcounts/returning_dead_object/stderr.txt | 2 +
.../refcounts/too_many_increfs/stderr.txt | 2 +
8 files changed, 115 insertions(+), 16 deletions(-)
---
diff --git a/libcpychecker/absinterp.py b/libcpychecker/absinterp.py
index b0489bf..f292ba0 100644
--- a/libcpychecker/absinterp.py
+++ b/libcpychecker/absinterp.py
@@ -767,19 +767,25 @@ class State:
def get_gcc_loc_or_none(self):
# Return the gcc.Location for this state, which could be None
- return self.loc.get_stmt().loc
+ stmt = self.loc.get_stmt()
+ if stmt:
+ return stmt.loc
def get_gcc_loc(self, fun):
# Return a non-None gcc.Location for this state
# Some statements have None for their location, but gcc.error() etc
# don't allow this. Use the end of the function for this case.
- log('%s %r' % (self.loc.get_stmt(), self.loc.get_stmt()))
- log(self.loc.get_stmt().loc)
- # grrr... not all statements have a non-NULL location
- gccloc = self.loc.get_stmt().loc
- if gccloc is None:
- gccloc = fun.end
- return gccloc
+ stmt = self.loc.get_stmt()
+ log('%s %r' % (stmt, stmt))
+ if stmt:
+ log(self.loc.get_stmt().loc)
+ # grrr... not all statements have a non-NULL location
+ gccloc = self.loc.get_stmt().loc
+ if gccloc is None:
+ gccloc = fun.end
+ return gccloc
+ else:
+ return fun.end
def raise_any_null_ptr_deref(self, expr, ptr):
if isinstance(ptr, ConcreteValue):
diff --git a/libcpychecker/diagnostics.py b/libcpychecker/diagnostics.py
index 852c29e..2881990 100644
--- a/libcpychecker/diagnostics.py
+++ b/libcpychecker/diagnostics.py
@@ -25,7 +25,31 @@ from gccutils import get_src_for_loc
from libcpychecker.visualizations import HtmlRenderer
from libcpychecker.PyArg_ParseTuple import log
-def describe_trace(trace, fun):
+class Annotator:
+ """
+ A collection of hooks for use when describing a trace (either as text,
+ or as an HTML report)
+ """
+ def get_notes(self, transition):
+ """
+ Return a list of Note instances giving extra information about the
+ transition
+ """
+ raise NotImplementedError
+
+class TestAnnotator(Annotator):
+ def get_notes(self, transition):
+ result = []
+ srcloc = transition.src.get_gcc_loc_or_none()
+ if srcloc:
+ if transition.src.loc != transition.dest.loc:
+ result.append(Note(srcloc,
+ ('transition from "%s" to "%s"'
+ % (transition.src.loc.get_stmt(),
+ transition.dest.loc.get_stmt()))))
+ return result
+
+def describe_trace(trace, fun, annotator):
"""
Print more details about the path through the function that
leads to the error, using gcc.inform()
@@ -33,8 +57,8 @@ def describe_trace(trace, fun):
awaiting_target = None
for t in trace.transitions:
log('transition: %s' % t)
+ srcloc = t.src.get_gcc_loc_or_none()
if t.desc:
- srcloc = t.src.get_gcc_loc_or_none()
if srcloc:
gcc.inform(t.src.get_gcc_loc(fun),
('when %s at: %s'
@@ -50,6 +74,12 @@ def describe_trace(trace, fun):
gcc.inform(destloc,
'reaching: %s' % get_src_for_loc(destloc))
+ if annotator:
+ notes = annotator.get_notes(t)
+ for note in notes:
+ if note.loc and note.loc == srcloc:
+ gcc.inform(note.loc, note.msg)
+
class Reporter:
"""
Error-reporting interface. Gathers information, sending it to GCC's
@@ -94,11 +124,13 @@ class Report:
self.loc = loc
self.msg = msg
self.trace = None
+ self._annotators = {}
self.notes = []
- def add_trace(self, trace):
+ def add_trace(self, trace, annotator=None):
self.trace = trace
- describe_trace(trace, self.fun)
+ self._annotators[trace] = annotator
+ describe_trace(trace, self.fun, annotator)
def add_note(self, loc, msg):
gcc.inform(loc, msg)
@@ -106,6 +138,10 @@ class Report:
self.notes.append(note)
return note
+ def get_annotator_for_trace(self, trace):
+ return self._annotators.get(trace)
+
+
class Note:
"""
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index 69f4d2e..ae26da4 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -26,7 +26,7 @@ import gcc
from gccutils import cfg_to_dot, invoke_dot, get_src_for_loc, check_isinstance
from libcpychecker.absinterp import *
-from libcpychecker.diagnostics import Reporter
+from libcpychecker.diagnostics import Reporter, Annotator, Note
from libcpychecker.PyArg_ParseTuple import log
from libcpychecker.types import is_py3k, is_debug_build
@@ -810,6 +810,34 @@ def dump_traces_to_stdout(traces):
if i + 1 < len(traces):
sys.stdout.write('\n')
+class RefcountAnnotator(Annotator):
+ """
+ Annotate a trace with information on the reference count of a particular
+ object
+ """
+ def __init__(self, region):
+ self.region = region
+
+ def get_notes(self, transition):
+ """
+ Add a note to every transition that changes the reference count of
+ our target object
+ """
+ result = []
+ src_refcnt = transition.src.get_value_of_field_by_region(self.region,
+ 'ob_refcnt')
+ dest_refcnt = transition.dest.get_value_of_field_by_region(self.region,
+ 'ob_refcnt')
+ if src_refcnt != dest_refcnt:
+ log('src_refcnt: %r' % src_refcnt, 0)
+ log('dest_refcnt: %r' % dest_refcnt, 0)
+ loc = transition.src.get_gcc_loc_or_none()
+ if loc:
+ result.append(Note(loc,
+ ('ob_refcnt is now %s' % dest_refcnt)))
+
+ return result
+
def check_refcounts(fun, dump_traces=False, show_traces=False):
"""
The top-level function of the refcount checker, checking the refcounting
@@ -916,8 +944,18 @@ def check_refcounts(fun, dump_traces=False, show_traces=False):
get_src_for_loc(alloc_loc))))
# Summarize the control flow we followed through the function:
- err.add_trace(trace)
-
+ if 1:
+ annotator = RefcountAnnotator(region)
+ else:
+ # Debug help:
+ from libcpychecker.diagnostics import TestAnnotator
+ annotator = TestAnnotator()
+ err.add_trace(trace, annotator)
+
+ if 0:
+ # Handy for debugging:
+ err.add_note(endstate.get_gcc_loc(fun),
+ 'this was trace %i' % i)
return err
# Here's where we verify the refcount:
diff --git a/libcpychecker/visualizations.py b/libcpychecker/visualizations.py
index 2521c0e..cc09843 100644
--- a/libcpychecker/visualizations.py
+++ b/libcpychecker/visualizations.py
@@ -327,6 +327,13 @@ pre {
lines = html.splitlines()
result = lines[0]
+ # Generate any notes from the report's annotator (if any):
+ notes = []
+ annotator = report.get_annotator_for_trace(trace)
+ if annotator:
+ for trans in trace.transitions:
+ notes += annotator.get_notes(trans)
+
# The rest contains the actual source lines:
lines = lines[1:]
for linenum, line in zip(range(start_line, end_line), lines):
@@ -352,10 +359,16 @@ pre {
src_loc = trans.src.get_gcc_loc_or_none()
if src_loc and src_loc.line == linenum:
result += '<span class="transition">%s</span>\n' % trans.desc
+ # Report the top-level message, if it happens here:
if report.loc.line == linenum:
result += '<span class="error">%s</span>\n' % report.msg
+ # Add any notes from the annotator:
+ for note in notes:
+ if note.loc and note.loc.line == linenum:
+ result += '<span class="transition">%s</span>\n' % note.msg
+ # Add additional notes attached to the report:
for note in report.notes:
- if note.loc.line == linenum:
+ if note.loc and note.loc.line == linenum:
result += '<span class="note">%s</span>\n' % note.msg
result += '\n'
diff --git a/tests/cpychecker/refcounts/missing_decref/stderr.txt b/tests/cpychecker/refcounts/missing_decref/stderr.txt
index 5c2cf22..77a9818 100644
--- a/tests/cpychecker/refcounts/missing_decref/stderr.txt
+++ b/tests/cpychecker/refcounts/missing_decref/stderr.txt
@@ -2,6 +2,7 @@ tests/cpychecker/refcounts/missing_decref/input.c: In function 'missing_decref':
tests/cpychecker/refcounts/missing_decref/input.c:38:1: error: ob_refcnt of PyListObject is 1 too high
tests/cpychecker/refcounts/missing_decref/input.c:27:10: note: PyListObject allocated at: list = PyList_New(1);
tests/cpychecker/refcounts/missing_decref/input.c:27:10: note: when PyList_New() succeeds at: list = PyList_New(1);
+tests/cpychecker/refcounts/missing_decref/input.c:27:10: note: ob_refcnt is now refs: 1
tests/cpychecker/refcounts/missing_decref/input.c:28:8: note: when taking False path at: if (!list)
tests/cpychecker/refcounts/missing_decref/input.c:30:10: note: reaching: item = PyLong_FromLong(42);
tests/cpychecker/refcounts/missing_decref/input.c:30:10: note: when PyLong_FromLong() fails at: item = PyLong_FromLong(42);
diff --git a/tests/cpychecker/refcounts/object_leak/stderr.txt b/tests/cpychecker/refcounts/object_leak/stderr.txt
index bb5b1b6..25f7b4c 100644
--- a/tests/cpychecker/refcounts/object_leak/stderr.txt
+++ b/tests/cpychecker/refcounts/object_leak/stderr.txt
@@ -2,6 +2,7 @@ tests/cpychecker/refcounts/object_leak/input.c: In function 'object_leak':
tests/cpychecker/refcounts/object_leak/input.c:29:5: error: ob_refcnt of PyLongObject is 1 too high
tests/cpychecker/refcounts/object_leak/input.c:26:15: note: PyLongObject allocated at: PyObject *tmp = PyLong_FromLong(0x1000);
tests/cpychecker/refcounts/object_leak/input.c:26:15: note: when PyLong_FromLong() succeeds at: PyObject *tmp = PyLong_FromLong(0x1000);
+tests/cpychecker/refcounts/object_leak/input.c:26:15: note: ob_refcnt is now refs: 1
tests/cpychecker/refcounts/object_leak/input.c:29:5: note: when returning at: return NULL;
tests/cpychecker/refcounts/object_leak/input.c:29:5: error: returning (PyObject*)NULL without setting an exception
tests/cpychecker/refcounts/object_leak/input.c:26:15: note: when PyLong_FromLong() succeeds at: PyObject *tmp = PyLong_FromLong(0x1000);
diff --git a/tests/cpychecker/refcounts/returning_dead_object/stderr.txt b/tests/cpychecker/refcounts/returning_dead_object/stderr.txt
index a77bf2b..6bbaf9e 100644
--- a/tests/cpychecker/refcounts/returning_dead_object/stderr.txt
+++ b/tests/cpychecker/refcounts/returning_dead_object/stderr.txt
@@ -2,8 +2,10 @@ tests/cpychecker/refcounts/returning_dead_object/input.c: In function 'returning
tests/cpychecker/refcounts/returning_dead_object/input.c:43:1: error: ob_refcnt of return value is 1 too low
tests/cpychecker/refcounts/returning_dead_object/input.c:29:15: note: PyLongObject allocated at: PyObject *tmp = PyLong_FromLong(0x1000);
tests/cpychecker/refcounts/returning_dead_object/input.c:29:15: note: when PyLong_FromLong() succeeds at: PyObject *tmp = PyLong_FromLong(0x1000);
+tests/cpychecker/refcounts/returning_dead_object/input.c:29:15: note: ob_refcnt is now refs: 1
tests/cpychecker/refcounts/returning_dead_object/input.c:31:8: note: when taking False path at: if (!tmp) {
tests/cpychecker/refcounts/returning_dead_object/input.c:39:5: note: reaching: Py_DECREF(tmp);
+tests/cpychecker/refcounts/returning_dead_object/input.c:39:5: note: ob_refcnt is now refs: 0
tests/cpychecker/refcounts/returning_dead_object/input.c:39:5: note: when taking True path at: Py_DECREF(tmp);
tests/cpychecker/refcounts/returning_dead_object/input.c:42:5: note: reaching: return tmp;
tests/cpychecker/refcounts/returning_dead_object/input.c:43:1: note: when returning
diff --git a/tests/cpychecker/refcounts/too_many_increfs/stderr.txt b/tests/cpychecker/refcounts/too_many_increfs/stderr.txt
index d4dfaf1..bd5df29 100644
--- a/tests/cpychecker/refcounts/too_many_increfs/stderr.txt
+++ b/tests/cpychecker/refcounts/too_many_increfs/stderr.txt
@@ -2,6 +2,8 @@ tests/cpychecker/refcounts/too_many_increfs/input.c: In function 'too_many_incre
tests/cpychecker/refcounts/too_many_increfs/input.c:31:5: error: ob_refcnt of return value is 1 too high
tests/cpychecker/refcounts/too_many_increfs/input.c:26:9: note: PyLongObject allocated at: tmp = PyLong_FromLong(0x1000);
tests/cpychecker/refcounts/too_many_increfs/input.c:26:9: note: when PyLong_FromLong() succeeds at: tmp = PyLong_FromLong(0x1000);
+tests/cpychecker/refcounts/too_many_increfs/input.c:26:9: note: ob_refcnt is now refs: 1
+tests/cpychecker/refcounts/too_many_increfs/input.c:30:5: note: ob_refcnt is now refs: 2
tests/cpychecker/refcounts/too_many_increfs/input.c:31:5: note: when returning at: return tmp;
tests/cpychecker/refcounts/too_many_increfs/input.c:30:5: error: dereferencing NULL (tmp->ob_refcnt) at tests/cpychecker/refcounts/too_many_increfs/input.c:30
tests/cpychecker/refcounts/too_many_increfs/input.c:26:9: note: when PyLong_FromLong() fails at: tmp = PyLong_FromLong(0x1000);
12 years, 9 months
[gcc-python-plugin] cpychecker: support accessing arrays via pointers
by David Hugh Malcolm
commit 7af438090644f440893f2ad3f4f36adafc70f479
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Wed Aug 24 06:16:34 2011 -0400
cpychecker: support accessing arrays via pointers
libcpychecker/absinterp.py | 28 +++++++++++++++
libcpychecker/refcounts.py | 4 +-
tests/cpychecker/absinterp/arrays2/input.c | 47 +++++++++++++++++++++++++
tests/cpychecker/absinterp/arrays2/script.py | 22 +++++++++++
tests/cpychecker/absinterp/arrays2/stdout.txt | 8 ++++
5 files changed, 107 insertions(+), 2 deletions(-)
---
diff --git a/libcpychecker/absinterp.py b/libcpychecker/absinterp.py
index 10c584c..b0489bf 100644
--- a/libcpychecker/absinterp.py
+++ b/libcpychecker/absinterp.py
@@ -560,6 +560,34 @@ class State:
log(' index: %r' % index)
if isinstance(index, ConcreteValue):
index = index.value
+ return self._array_region(parent, index)
+
+ def pointer_plus_region(self, stmt):
+ # Cope with treating pointers as arrays.
+ # The constant appears to be in bytes, rather than as units of the type
+ log('pointer_add_region')
+ assert stmt.exprcode == gcc.PointerPlusExpr
+ rhs = stmt.rhs
+ a = self.eval_rvalue(rhs[0], stmt.loc)
+ b = self.eval_rvalue(rhs[1], stmt.loc)
+ log('a: %r' % a)
+ log('b: %r' % b)
+ if isinstance(a, PointerToRegion) and isinstance(b, ConcreteValue):
+ parent = a.region
+ log(rhs[0].type)
+ log(rhs[0].type.dereference)
+ sizeof = rhs[0].type.dereference.sizeof
+ log(sizeof)
+ index = b.value / sizeof
+ return self._array_region(parent, index)
+ else:
+ raise NotImplementedError("Don't know how to cope with pointer addition of\n %r\nand\n %rat %s"
+ % (a, b, stmt.loc))
+
+ def _array_region(self, parent, index):
+ # Used by element_region, and pointer_add_region
+ check_isinstance(parent, Region)
+ check_isinstance(index, (int, long))
if index in parent.fields:
log('reusing')
return parent.fields[index]
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index 3ff0438..69f4d2e 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -654,8 +654,8 @@ class MyState(State):
elif stmt.exprcode == gcc.MemRef:
return self.eval_rvalue(rhs[0], stmt.loc)
elif stmt.exprcode == gcc.PointerPlusExpr:
- # For now, for pointer arithmetic, just return an unknown value:
- return UnknownValue(stmt.lhs.type, stmt.loc)
+ region = self.pointer_plus_region(stmt)
+ return PointerToRegion(stmt.lhs.type, stmt.loc, region)
else:
raise NotImplementedError("Don't know how to cope with exprcode: %r (%s) at %s"
% (stmt.exprcode, stmt.exprcode, stmt.loc))
diff --git a/tests/cpychecker/absinterp/arrays2/input.c b/tests/cpychecker/absinterp/arrays2/input.c
new file mode 100644
index 0000000..10b6f17
--- /dev/null
+++ b/tests/cpychecker/absinterp/arrays2/input.c
@@ -0,0 +1,47 @@
+/*
+ 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 reading and writing simple arrays via pointers
+*/
+int test_arrays2(void)
+{
+ int arr[10];
+ int *ptr = arr;
+
+ ptr[0] = 1;
+ ptr[1] = 2;
+ ptr[2] = 3;
+
+ /*
+ The analyser ought to be able to figure out that there's a single trace
+ with return value 6 (rather than "unknown", or uninitialized data):
+ */
+ return arr[0] + arr[1] + arr[2];
+}
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/absinterp/arrays2/script.py b/tests/cpychecker/absinterp/arrays2/script.py
new file mode 100644
index 0000000..6bb62fd
--- /dev/null
+++ b/tests/cpychecker/absinterp/arrays2/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/arrays2/stdout.txt b/tests/cpychecker/absinterp/arrays2/stdout.txt
new file mode 100644
index 0000000..70ffcfb
--- /dev/null
+++ b/tests/cpychecker/absinterp/arrays2/stdout.txt
@@ -0,0 +1,8 @@
+Trace 0:
+ Transitions:
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='int', loc=gcc.Location(file='tests/cpychecker/absinterp/arrays2/input.c', line=38), value=6)
+ str(): (int)6 from tests/cpychecker/absinterp/arrays2/input.c:38
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/absinterp/arrays2/input.c:26
12 years, 9 months
[gcc-python-plugin] Make gcc.Type.sizeof be an int, rather than a gcc.IntegerCst
by David Hugh Malcolm
commit 088043cb284ecc7be245975c5d2c738fcb120bbc
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Wed Aug 24 05:57:30 2011 -0400
Make gcc.Type.sizeof be an int, rather than a gcc.IntegerCst
docs/tree.rst | 3 ++-
gcc-python-tree.c | 20 +++++++++++++++++++-
tests/plugin/function-types/script.py | 6 ++++++
tests/plugin/function-types/stdout.txt | 1 +
tests/plugin/types/script.py | 4 ++--
tests/plugin/types/stdout.txt | 8 ++++----
6 files changed, 34 insertions(+), 8 deletions(-)
---
diff --git a/docs/tree.rst b/docs/tree.rst
index 125ed5f..f45d65c 100644
--- a/docs/tree.rst
+++ b/docs/tree.rst
@@ -269,7 +269,8 @@ Types
.. py:attribute:: sizeof
- `sizeof()` this type, as a :py:class:`gcc.IntegerCst`
+ `sizeof()` this type, as an `int`, or raising `TypeError` for those
+ types which don't have a well-defined size
The standard C types are accessible via class methods of gcc.Type.
They are only created by GCC after plugins are loaded, and so they're
diff --git a/gcc-python-tree.c b/gcc-python-tree.c
index f453bbf..ab839c8 100644
--- a/gcc-python-tree.c
+++ b/gcc-python-tree.c
@@ -221,8 +221,26 @@ gcc_Type_get_sizeof(struct PyGccTree *self, void *closure)
c_sizeof_or_alignof_type wants a location; we use a fake one
*/
tree t_sizeof = c_sizeof_or_alignof_type(input_location, self->t, true, 0);
+ PyObject *str;
- return gcc_python_make_wrapper_tree(t_sizeof);
+ /* This gives us either an INTEGER_CST or the dummy error type: */
+ if (INTEGER_CST == TREE_CODE(t_sizeof)) {
+ return gcc_python_int_from_double_int(TREE_INT_CST(t_sizeof),
+ 1);
+ }
+
+ /* Error handling: */
+ str = gcc_Tree_str(self);
+ if (str) {
+ PyErr_Format(PyExc_TypeError,
+ "type \"%s\" does not have a \"sizeof\"",
+ gcc_python_string_as_string(str));
+ Py_DECREF(str);
+ } else {
+ PyErr_Format(PyExc_TypeError,
+ "type does not have a \"sizeof\"");
+ }
+ return NULL;
}
PyObject *
diff --git a/tests/plugin/function-types/script.py b/tests/plugin/function-types/script.py
index 48fce8b..08bdf1b 100644
--- a/tests/plugin/function-types/script.py
+++ b/tests/plugin/function-types/script.py
@@ -30,6 +30,12 @@ def on_finish_unit():
print('str(fn_type.type): %r' % str(fn_type.type))
assert isinstance(fn_type.argument_types, tuple)
print('argument_types: %r' % [str(t) for t in fn_type.argument_types])
+ try:
+ fn_type.sizeof
+ assert 0 # an exception should have been raised
+ except:
+ err = sys.exc_info()[1]
+ print('err: %s' % err)
diff --git a/tests/plugin/function-types/stdout.txt b/tests/plugin/function-types/stdout.txt
index 024777c..63a7f2b 100644
--- a/tests/plugin/function-types/stdout.txt
+++ b/tests/plugin/function-types/stdout.txt
@@ -2,3 +2,4 @@ fn_type_decl.name: 'example_fn_type'
str(fn_type): 'int example_fn_type (char, float)'
str(fn_type.type): 'int'
argument_types: ['char', 'float']
+err: type "int example_fn_type (char, float)" does not have a "sizeof"
diff --git a/tests/plugin/types/script.py b/tests/plugin/types/script.py
index e0343a3..696db9c 100644
--- a/tests/plugin/types/script.py
+++ b/tests/plugin/types/script.py
@@ -30,7 +30,7 @@ def on_finish_unit():
assert isinstance(t.max_value, gcc.IntegerCst)
print(' t.min_value.constant: %r' % t.min_value.constant)
print(' t.max_value.constant: %r' % t.max_value.constant)
- assert isinstance(t.sizeof, gcc.IntegerCst)
+ assert isinstance(t.sizeof, int)
print(' t.sizeof: %r' % t.sizeof)
# gccutils.pprint(t)
@@ -43,7 +43,7 @@ def on_finish_unit():
print('gcc.Type: %r' % str(t))
print(' t.const: %r' % t.const)
print(' t.precision: %r' % t.precision)
- assert isinstance(t.sizeof, gcc.IntegerCst)
+ assert isinstance(t.sizeof, int)
print(' t.sizeof: %r' % t.sizeof)
dump_real_type(gcc.Type.float())
diff --git a/tests/plugin/types/stdout.txt b/tests/plugin/types/stdout.txt
index 0d9cfa5..f016cb2 100644
--- a/tests/plugin/types/stdout.txt
+++ b/tests/plugin/types/stdout.txt
@@ -4,19 +4,19 @@ gcc.Type: 'unsigned char'
t.precision: 8
t.min_value.constant: 0
t.max_value.constant: 255
- t.sizeof: gcc.IntegerCst(1)
+ t.sizeof: 1
gcc.Type: 'signed char'
t.const: False
t.unsigned: False
t.precision: 8
t.min_value.constant: -128
t.max_value.constant: 127
- t.sizeof: gcc.IntegerCst(1)
+ t.sizeof: 1
gcc.Type: 'float'
t.const: False
t.precision: 32
- t.sizeof: gcc.IntegerCst(4)
+ t.sizeof: 4
gcc.Type: 'double'
t.const: False
t.precision: 64
- t.sizeof: gcc.IntegerCst(8)
+ t.sizeof: 8
12 years, 9 months
[gcc-python-plugin] gcc_python_int_from_double_int: use PyIntObject instances where possible
by David Hugh Malcolm
commit 0381cd1f17caf4716e61ee577cd046cfc5de65b8
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Wed Aug 24 05:49:06 2011 -0400
gcc_python_int_from_double_int: use PyIntObject instances where possible
gcc-python.c | 28 +++++++++++++++++++-
tests/cpychecker/absinterp/arrays/stdout.txt | 4 +-
.../absinterp/custom-strdup-with-attr/stdout.txt | 4 +-
.../cpychecker/absinterp/custom-strdup/stdout.txt | 4 +-
.../cpychecker/absinterp/nested-fields/stdout.txt | 4 +-
.../read-through-global-ptr-checked/stdout.txt | 4 +-
tests/cpychecker/absinterp/switch/stdout.txt | 4 +-
.../cpychecker/refcounts/correct_decref/stdout.txt | 4 +-
.../refcounts/fold_conditional/stdout.txt | 4 +-
tests/cpychecker/refcounts/loop_n_times/stdout.txt | 16 +++++-----
.../cpychecker/refcounts/missing_decref/stdout.txt | 8 +++---
.../refcounts/not_setting_exception/stdout.txt | 4 +-
tests/cpychecker/refcounts/object_leak/stdout.txt | 4 +-
.../refcounts/returning_dead_object/stdout.txt | 4 +-
.../refcounts/use_after_dealloc/stdout.txt | 4 +-
tests/plugin/constants/stdout.txt | 12 ++++----
tests/plugin/functions/stdout.txt | 2 +-
tests/plugin/type-attributes/stdout.txt | 4 +-
tests/plugin/types/stdout.txt | 8 +++---
19 files changed, 76 insertions(+), 50 deletions(-)
---
diff --git a/gcc-python.c b/gcc-python.c
index a60c903..f6c5f5f 100644
--- a/gcc-python.c
+++ b/gcc-python.c
@@ -1194,9 +1194,35 @@ gcc_python_double_int_as_text(double_int di, bool is_unsigned,
PyObject *
gcc_python_int_from_double_int(double_int di, bool is_unsigned)
{
+ PyObject *long_obj;
+#if PY_MAJOR_VERSION < 3
+ long long_val;
+ int overflow;
+#endif
char buf[512]; /* FIXME */
gcc_python_double_int_as_text(di, is_unsigned, buf, sizeof(buf));
- return PyLong_FromString(buf, NULL, 10);
+
+ long_obj = PyLong_FromString(buf, NULL, 10);
+ if (!long_obj) {
+ return NULL;
+ }
+#if PY_MAJOR_VERSION >= 3
+ return long_obj;
+#else
+ long_val = PyLong_AsLongAndOverflow(long_obj, &overflow);
+ if (overflow) {
+ /* Doesn't fit in a PyIntObject; use the PyLongObject: */
+ return long_obj;
+ } else {
+ /* Fits in a PyIntObject: use that */
+ PyObject *int_obj = PyInt_FromLong(long_val);
+ if (!int_obj) {
+ return long_obj;
+ }
+ Py_DECREF(long_obj);
+ return int_obj;
+ }
+#endif
}
/*
diff --git a/tests/cpychecker/absinterp/arrays/stdout.txt b/tests/cpychecker/absinterp/arrays/stdout.txt
index 0cf8219..bc8a93a 100644
--- a/tests/cpychecker/absinterp/arrays/stdout.txt
+++ b/tests/cpychecker/absinterp/arrays/stdout.txt
@@ -2,7 +2,7 @@ Trace 0:
Transitions:
'returning'
Return value:
- repr(): ConcreteValue(gcctype='int', loc=gcc.Location(file='tests/cpychecker/absinterp/arrays/input.c', line=31), value=42L)
- str(): (int)42L from tests/cpychecker/absinterp/arrays/input.c:31
+ repr(): ConcreteValue(gcctype='int', loc=gcc.Location(file='tests/cpychecker/absinterp/arrays/input.c', line=31), value=42)
+ str(): (int)42 from tests/cpychecker/absinterp/arrays/input.c:31
Exception:
(struct PyObject *)0 from tests/cpychecker/absinterp/arrays/input.c:26
diff --git a/tests/cpychecker/absinterp/custom-strdup-with-attr/stdout.txt b/tests/cpychecker/absinterp/custom-strdup-with-attr/stdout.txt
index 87a9360..0e67ae8 100644
--- a/tests/cpychecker/absinterp/custom-strdup-with-attr/stdout.txt
+++ b/tests/cpychecker/absinterp/custom-strdup-with-attr/stdout.txt
@@ -18,7 +18,7 @@ Trace 1:
'taking True path'
'returning'
Return value:
- repr(): ConcreteValue(gcctype='char *', loc=gcc.Location(file='tests/cpychecker/absinterp/custom-strdup-with-attr/input.c', line=49), value=0L)
- str(): (char *)0L from tests/cpychecker/absinterp/custom-strdup-with-attr/input.c:49
+ repr(): ConcreteValue(gcctype='char *', loc=gcc.Location(file='tests/cpychecker/absinterp/custom-strdup-with-attr/input.c', line=49), value=0)
+ str(): (char *)0 from tests/cpychecker/absinterp/custom-strdup-with-attr/input.c:49
Exception:
(struct PyObject *)0 from tests/cpychecker/absinterp/custom-strdup-with-attr/input.c:42
diff --git a/tests/cpychecker/absinterp/custom-strdup/stdout.txt b/tests/cpychecker/absinterp/custom-strdup/stdout.txt
index 744149f..65fadf4 100644
--- a/tests/cpychecker/absinterp/custom-strdup/stdout.txt
+++ b/tests/cpychecker/absinterp/custom-strdup/stdout.txt
@@ -29,7 +29,7 @@ Trace 2:
'taking True path'
'returning'
Return value:
- repr(): ConcreteValue(gcctype='char *', loc=gcc.Location(file='tests/cpychecker/absinterp/custom-strdup/input.c', line=46), value=0L)
- str(): (char *)0L from tests/cpychecker/absinterp/custom-strdup/input.c:46
+ repr(): ConcreteValue(gcctype='char *', loc=gcc.Location(file='tests/cpychecker/absinterp/custom-strdup/input.c', line=46), value=0)
+ str(): (char *)0 from tests/cpychecker/absinterp/custom-strdup/input.c:46
Exception:
(struct PyObject *)0 from tests/cpychecker/absinterp/custom-strdup/input.c:39
diff --git a/tests/cpychecker/absinterp/nested-fields/stdout.txt b/tests/cpychecker/absinterp/nested-fields/stdout.txt
index 3bc65c9..95096f6 100644
--- a/tests/cpychecker/absinterp/nested-fields/stdout.txt
+++ b/tests/cpychecker/absinterp/nested-fields/stdout.txt
@@ -2,7 +2,7 @@ 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
+ repr(): ConcreteValue(gcctype='int', loc=gcc.Location(file='tests/cpychecker/absinterp/nested-fields/input.c', line=50), value=1110)
+ str(): (int)1110 from tests/cpychecker/absinterp/nested-fields/input.c:50
Exception:
(struct PyObject *)0 from tests/cpychecker/absinterp/nested-fields/input.c:40
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 550d373..e5c482d 100644
--- a/tests/cpychecker/absinterp/read-through-global-ptr-checked/stdout.txt
+++ b/tests/cpychecker/absinterp/read-through-global-ptr-checked/stdout.txt
@@ -15,7 +15,7 @@ Trace 1:
'taking False path'
'returning'
Return value:
- repr(): ConcreteValue(gcctype='int', loc=gcc.Location(file='tests/cpychecker/absinterp/read-through-global-ptr-checked/input.c', line=38), value=42L)
- str(): (int)42L from tests/cpychecker/absinterp/read-through-global-ptr-checked/input.c:38
+ repr(): ConcreteValue(gcctype='int', loc=gcc.Location(file='tests/cpychecker/absinterp/read-through-global-ptr-checked/input.c', line=38), value=42)
+ str(): (int)42 from tests/cpychecker/absinterp/read-through-global-ptr-checked/input.c:38
Exception:
(struct PyObject *)0 from tests/cpychecker/absinterp/read-through-global-ptr-checked/input.c:34
diff --git a/tests/cpychecker/absinterp/switch/stdout.txt b/tests/cpychecker/absinterp/switch/stdout.txt
index 1fbe6d7..9687757 100644
--- a/tests/cpychecker/absinterp/switch/stdout.txt
+++ b/tests/cpychecker/absinterp/switch/stdout.txt
@@ -39,7 +39,7 @@ Trace 3:
'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
+ repr(): ConcreteValue(gcctype='const char *', loc=gcc.Location(file='tests/cpychecker/absinterp/switch/input.c', line=39), value=0)
+ str(): (const char *)0 from tests/cpychecker/absinterp/switch/input.c:39
Exception:
(struct PyObject *)0 from tests/cpychecker/absinterp/switch/input.c:26
diff --git a/tests/cpychecker/refcounts/correct_decref/stdout.txt b/tests/cpychecker/refcounts/correct_decref/stdout.txt
index 4ee1d1f..eddd96c 100644
--- a/tests/cpychecker/refcounts/correct_decref/stdout.txt
+++ b/tests/cpychecker/refcounts/correct_decref/stdout.txt
@@ -28,8 +28,8 @@ Trace 1:
'taking True path'
'returning'
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
+ repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/correct_decref/input.c', line=33), value=0)
+ str(): (struct PyObject *)0 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')")
diff --git a/tests/cpychecker/refcounts/fold_conditional/stdout.txt b/tests/cpychecker/refcounts/fold_conditional/stdout.txt
index 58d61b9..a18a8a0 100644
--- a/tests/cpychecker/refcounts/fold_conditional/stdout.txt
+++ b/tests/cpychecker/refcounts/fold_conditional/stdout.txt
@@ -29,8 +29,8 @@ Trace 1:
'taking True path'
'returning'
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
+ repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/fold_conditional/input.c', line=28), value=0)
+ str(): (struct PyObject *)0 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')")
diff --git a/tests/cpychecker/refcounts/loop_n_times/stdout.txt b/tests/cpychecker/refcounts/loop_n_times/stdout.txt
index d67d4ca..c8bb07c 100644
--- a/tests/cpychecker/refcounts/loop_n_times/stdout.txt
+++ b/tests/cpychecker/refcounts/loop_n_times/stdout.txt
@@ -78,8 +78,8 @@ Trace 2:
'taking True path'
'returning'
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
+ repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/loop_n_times/input.c', line=45), value=0)
+ str(): (struct PyObject *)0 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')")
@@ -110,8 +110,8 @@ Trace 3:
'calling tp_dealloc on PyListObject allocated at tests/cpychecker/refcounts/loop_n_times/input.c:38'
'returning'
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
+ repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/loop_n_times/input.c', line=45), value=0)
+ str(): (struct PyObject *)0 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')")
@@ -138,8 +138,8 @@ Trace 4:
'taking True path'
'returning'
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
+ repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/loop_n_times/input.c', line=40), value=0)
+ str(): (struct PyObject *)0 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')")
@@ -159,8 +159,8 @@ Trace 5:
'taking True path'
'returning'
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
+ repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/loop_n_times/input.c', line=35), value=0)
+ str(): (struct PyObject *)0 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')")
diff --git a/tests/cpychecker/refcounts/missing_decref/stdout.txt b/tests/cpychecker/refcounts/missing_decref/stdout.txt
index 7adf9bd..d3526ea 100644
--- a/tests/cpychecker/refcounts/missing_decref/stdout.txt
+++ b/tests/cpychecker/refcounts/missing_decref/stdout.txt
@@ -37,8 +37,8 @@ Trace 1:
'taking True path'
'returning'
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
+ repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/missing_decref/input.c', line=34), value=0)
+ str(): (struct PyObject *)0 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')")
@@ -63,8 +63,8 @@ Trace 2:
'taking True path'
'returning'
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
+ repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/missing_decref/input.c', line=29), value=0)
+ str(): (struct PyObject *)0 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')")
diff --git a/tests/cpychecker/refcounts/not_setting_exception/stdout.txt b/tests/cpychecker/refcounts/not_setting_exception/stdout.txt
index 89f9bf5..3521f3d 100644
--- a/tests/cpychecker/refcounts/not_setting_exception/stdout.txt
+++ b/tests/cpychecker/refcounts/not_setting_exception/stdout.txt
@@ -2,8 +2,8 @@ Trace 0:
Transitions:
'returning'
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
+ repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/not_setting_exception/input.c', line=32), value=0)
+ str(): (struct PyObject *)0 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')")
diff --git a/tests/cpychecker/refcounts/object_leak/stdout.txt b/tests/cpychecker/refcounts/object_leak/stdout.txt
index 90c46b7..b95fae3 100644
--- a/tests/cpychecker/refcounts/object_leak/stdout.txt
+++ b/tests/cpychecker/refcounts/object_leak/stdout.txt
@@ -1,4 +1,4 @@
Trace 0:
- returned: ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/object_leak/input.c', line=29), value=0L)
+ returned: ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/object_leak/input.c', line=29), value=0)
Trace 1:
- returned: ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/object_leak/input.c', line=29), value=0L)
+ returned: ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/object_leak/input.c', line=29), value=0)
diff --git a/tests/cpychecker/refcounts/returning_dead_object/stdout.txt b/tests/cpychecker/refcounts/returning_dead_object/stdout.txt
index 537f1b1..00091c6 100644
--- a/tests/cpychecker/refcounts/returning_dead_object/stdout.txt
+++ b/tests/cpychecker/refcounts/returning_dead_object/stdout.txt
@@ -53,8 +53,8 @@ Trace 2:
'taking True path'
'returning'
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
+ repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/returning_dead_object/input.c', line=32), value=0)
+ str(): (struct PyObject *)0 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')")
diff --git a/tests/cpychecker/refcounts/use_after_dealloc/stdout.txt b/tests/cpychecker/refcounts/use_after_dealloc/stdout.txt
index 286dd2c..96a26ef 100644
--- a/tests/cpychecker/refcounts/use_after_dealloc/stdout.txt
+++ b/tests/cpychecker/refcounts/use_after_dealloc/stdout.txt
@@ -54,8 +54,8 @@ Trace 2:
'taking True path'
'returning'
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
+ repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/use_after_dealloc/input.c', line=33), value=0)
+ str(): (struct PyObject *)0 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')")
diff --git a/tests/plugin/constants/stdout.txt b/tests/plugin/constants/stdout.txt
index 95e935f..e39a37a 100644
--- a/tests/plugin/constants/stdout.txt
+++ b/tests/plugin/constants/stdout.txt
@@ -1,7 +1,7 @@
-i000: 0x0L
-i001: 0x1L
-i002: -0x1L
-i003: 0xffffffffL
-i004: 0xffffffffL
+i000: 0x0
+i001: 0x1
+i002: -0x1
+i003: 0xffffffff
+i004: 0xffffffff
i005: 0xffffffffffffffffL
-i006: -0x100000000L
+i006: -0x100000000
diff --git a/tests/plugin/functions/stdout.txt b/tests/plugin/functions/stdout.txt
index 067395e..85c29a9 100644
--- a/tests/plugin/functions/stdout.txt
+++ b/tests/plugin/functions/stdout.txt
@@ -19,7 +19,7 @@ local_decls[2]
local_decls[3]
type(local): <type 'gcc.VarDecl'>
local.name: 'i'
- local.initial.constant: 42L
+ local.initial.constant: 42
str(local.type): 'int'
local_decls[4]
type(local): <type 'gcc.VarDecl'>
diff --git a/tests/plugin/type-attributes/stdout.txt b/tests/plugin/type-attributes/stdout.txt
index 71c7239..69213ba 100644
--- a/tests/plugin/type-attributes/stdout.txt
+++ b/tests/plugin/type-attributes/stdout.txt
@@ -19,7 +19,7 @@ str(fn_type): 'int example2 (const char *, const char *, const char *)'
str(fn_type.type): 'int'
argument_types: ['const char *', 'const char *', 'const char *']
fn_type.attributes: {'nonnull': [gcc.IntegerCst(1), gcc.IntegerCst(3)]}
-gccutils.get_nonnull_arguments(fn_type): frozenset([0L, 2L])
+gccutils.get_nonnull_arguments(fn_type): frozenset([0, 2])
-----------------------------------------------------
fn_type_decl.name: 'example3'
@@ -27,6 +27,6 @@ str(fn_type): 'int example3 (const char *, const char *, const char *)'
str(fn_type.type): 'int'
argument_types: ['const char *', 'const char *', 'const char *']
fn_type.attributes: {'nonnull': [gcc.IntegerCst(1)]}
-gccutils.get_nonnull_arguments(fn_type): frozenset([0L])
+gccutils.get_nonnull_arguments(fn_type): frozenset([0])
-----------------------------------------------------
diff --git a/tests/plugin/types/stdout.txt b/tests/plugin/types/stdout.txt
index d13b24d..0d9cfa5 100644
--- a/tests/plugin/types/stdout.txt
+++ b/tests/plugin/types/stdout.txt
@@ -2,15 +2,15 @@ gcc.Type: 'unsigned char'
t.const: False
t.unsigned: True
t.precision: 8
- t.min_value.constant: 0L
- t.max_value.constant: 255L
+ t.min_value.constant: 0
+ t.max_value.constant: 255
t.sizeof: gcc.IntegerCst(1)
gcc.Type: 'signed char'
t.const: False
t.unsigned: False
t.precision: 8
- t.min_value.constant: -128L
- t.max_value.constant: 127L
+ t.min_value.constant: -128
+ t.max_value.constant: 127
t.sizeof: gcc.IntegerCst(1)
gcc.Type: 'float'
t.const: False
12 years, 9 months
[gcc-python-plugin] Add "sizeof" attribute to gcc.Type
by David Hugh Malcolm
commit 60904df438e0603be6c54bb9c4adbc7b19a0166b
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Wed Aug 24 05:01:31 2011 -0400
Add "sizeof" attribute to gcc.Type
docs/tree.rst | 4 ++++
gcc-python-compat.h | 6 +++++-
gcc-python-tree.c | 11 +++++++++++
gcc-python-wrappers.h | 3 +++
generate-tree-c.py | 4 ++++
tests/plugin/types/script.py | 4 ++++
tests/plugin/types/stdout.txt | 4 ++++
7 files changed, 35 insertions(+), 1 deletions(-)
---
diff --git a/docs/tree.rst b/docs/tree.rst
index 29f5b6d..125ed5f 100644
--- a/docs/tree.rst
+++ b/docs/tree.rst
@@ -267,6 +267,10 @@ Types
syntax), as a dictionary (mapping from attribute names to list of
values). Typically this will be the empty dictionary.
+ .. py:attribute:: sizeof
+
+ `sizeof()` this type, as a :py:class:`gcc.IntegerCst`
+
The standard C types are accessible via class methods of gcc.Type.
They are only created by GCC after plugins are loaded, and so they're
only visible during callbacks, not during the initial run of the code.
diff --git a/gcc-python-compat.h b/gcc-python-compat.h
index 3d31a66..3c3985e 100644
--- a/gcc-python-compat.h
+++ b/gcc-python-compat.h
@@ -43,7 +43,7 @@ dump_gimple_stmt (pretty_printer *buffer, gimple gs, int spc, int flags);
/*
- This is declated in gcc/tree-pretty-print.c (around line 580); it doesn't
+ This is declared in gcc/tree-pretty-print.c (around line 580); it doesn't
seem to be declared in any of the plugin headers:
*/
extern int
@@ -51,6 +51,10 @@ dump_generic_node (pretty_printer *buffer, tree node, int spc, int flags,
bool is_stmt);
+/* From c-family/c-common.h */
+extern tree c_sizeof_or_alignof_type (location_t, tree, bool, int);
+
+
/*
PEP-7
Local variables:
diff --git a/gcc-python-tree.c b/gcc-python-tree.c
index 5fc49f1..f453bbf 100644
--- a/gcc-python-tree.c
+++ b/gcc-python-tree.c
@@ -215,6 +215,17 @@ gcc_Type_get_attributes(struct PyGccTree *self, void *closure)
}
PyObject *
+gcc_Type_get_sizeof(struct PyGccTree *self, void *closure)
+{
+ /*
+ c_sizeof_or_alignof_type wants a location; we use a fake one
+ */
+ tree t_sizeof = c_sizeof_or_alignof_type(input_location, self->t, true, 0);
+
+ return gcc_python_make_wrapper_tree(t_sizeof);
+}
+
+PyObject *
gcc_FunctionType_get_argument_types(struct PyGccTree * self, void *closure)
{
PyObject *result;
diff --git a/gcc-python-wrappers.h b/gcc-python-wrappers.h
index a0eeab0..a9ca28f 100644
--- a/gcc-python-wrappers.h
+++ b/gcc-python-wrappers.h
@@ -111,6 +111,9 @@ PyObject *
gcc_Type_get_attributes(struct PyGccTree *self, void *closure);
PyObject *
+gcc_Type_get_sizeof(struct PyGccTree *self, void *closure);
+
+PyObject *
gcc_FunctionType_get_argument_types(struct PyGccTree * self,void *closure);
PyObject *
diff --git a/generate-tree-c.py b/generate-tree-c.py
index 44e4594..34287c6 100644
--- a/generate-tree-c.py
+++ b/generate-tree-c.py
@@ -188,6 +188,10 @@ gcc_Declaration_get_location(struct PyGccTree *self, void *closure)
'gcc_Type_get_attributes',
None,
'The user-defined attributes on this type')
+ getsettable.add_gsdef('sizeof',
+ 'gcc_Type_get_sizeof',
+ None,
+ 'sizeof() this type, as a gcc.IntegerCst')
methods = PyMethodTable('gcc_Type_methods', [])
diff --git a/tests/plugin/types/script.py b/tests/plugin/types/script.py
index 35e9de4..e0343a3 100644
--- a/tests/plugin/types/script.py
+++ b/tests/plugin/types/script.py
@@ -30,6 +30,8 @@ def on_finish_unit():
assert isinstance(t.max_value, gcc.IntegerCst)
print(' t.min_value.constant: %r' % t.min_value.constant)
print(' t.max_value.constant: %r' % t.max_value.constant)
+ assert isinstance(t.sizeof, gcc.IntegerCst)
+ print(' t.sizeof: %r' % t.sizeof)
# gccutils.pprint(t)
# Pick some types that ought to be arch-independent and thus suitable
@@ -41,6 +43,8 @@ def on_finish_unit():
print('gcc.Type: %r' % str(t))
print(' t.const: %r' % t.const)
print(' t.precision: %r' % t.precision)
+ assert isinstance(t.sizeof, gcc.IntegerCst)
+ print(' t.sizeof: %r' % t.sizeof)
dump_real_type(gcc.Type.float())
dump_real_type(gcc.Type.double())
diff --git a/tests/plugin/types/stdout.txt b/tests/plugin/types/stdout.txt
index 4032ac9..d13b24d 100644
--- a/tests/plugin/types/stdout.txt
+++ b/tests/plugin/types/stdout.txt
@@ -4,15 +4,19 @@ gcc.Type: 'unsigned char'
t.precision: 8
t.min_value.constant: 0L
t.max_value.constant: 255L
+ t.sizeof: gcc.IntegerCst(1)
gcc.Type: 'signed char'
t.const: False
t.unsigned: False
t.precision: 8
t.min_value.constant: -128L
t.max_value.constant: 127L
+ t.sizeof: gcc.IntegerCst(1)
gcc.Type: 'float'
t.const: False
t.precision: 32
+ t.sizeof: gcc.IntegerCst(4)
gcc.Type: 'double'
t.const: False
t.precision: 64
+ t.sizeof: gcc.IntegerCst(8)
12 years, 9 months
[gcc-python-plugin] Show both the str() and the repr() when a type error occurs
by David Hugh Malcolm
commit 8d3c4133c98cc7a059f5253b91fddcebd5b379da
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Mon Aug 22 19:56:11 2011 -0400
Show both the str() and the repr() when a type error occurs
gccutils.py | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
---
diff --git a/gccutils.py b/gccutils.py
index dd9c146..0fa49bb 100644
--- a/gccutils.py
+++ b/gccutils.py
@@ -627,4 +627,4 @@ def check_isinstance(obj, types):
but with better error messages
"""
if not isinstance(obj, types):
- raise TypeError('%s is not an instance of %s' % (obj, types))
+ raise TypeError('%s / %r is not an instance of %s' % (obj, obj, types))
12 years, 9 months
[gcc-python-plugin] Improve error messages within type checking
by David Hugh Malcolm
commit 06b26b90a2c568e0b61bfda7bd2ddfdf96e60d0c
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Mon Aug 22 19:48:27 2011 -0400
Improve error messages within type checking
gccutils.py | 25 +++++--
libcpychecker/absinterp.py | 150 +++++++++++++++++++-------------------
libcpychecker/refcounts.py | 60 ++++++++--------
libcpychecker/types.py | 4 +-
libcpychecker/visualizations.py | 4 +-
5 files changed, 126 insertions(+), 117 deletions(-)
---
diff --git a/gccutils.py b/gccutils.py
index a289ebf..dd9c146 100644
--- a/gccutils.py
+++ b/gccutils.py
@@ -25,8 +25,8 @@ def get_src_for_loc(loc):
return linecache.getline(loc.file, loc.line).rstrip()
def get_field_by_name(typeobj, name):
- assert isinstance(typeobj,
- (gcc.RecordType, gcc.UnionType, gcc.QualUnionType))
+ check_isinstance(typeobj,
+ (gcc.RecordType, gcc.UnionType, gcc.QualUnionType))
for field in typeobj.fields:
if field.name == name:
return field
@@ -72,7 +72,7 @@ def get_nonnull_arguments(funtype):
Compare with gcc/tree-vrp.c: nonnull_arg_p
"""
- assert isinstance(funtype, gcc.FunctionType)
+ check_isinstance(funtype, gcc.FunctionType)
if 'nonnull' in funtype.attributes:
result = []
nonnull = funtype.attributes['nonnull']
@@ -84,7 +84,7 @@ def get_nonnull_arguments(funtype):
else:
# Only the listed args are nonnull:
for val in nonnull:
- assert isinstance(val, gcc.IntegerCst)
+ check_isinstance(val, gcc.IntegerCst)
result.append(val.constant - 1)
return frozenset(result)
else:
@@ -182,7 +182,7 @@ class TextualPrettyPrinter(PrettyPrinter):
def str_for_kv(key, value):
return ' %s = %s\n' % (key, value)
- assert isinstance(obj, gcc.Tree)
+ check_isinstance(obj, gcc.Tree)
visited.add(obj.addr)
result = '<%s\n' % obj.__class__.__name__
@@ -462,7 +462,7 @@ class TreePrettyPrinter(DotPrettyPrinter):
# nodes it references, as a string
def __init__(self, root):
print('root: %s' % root)
- assert isinstance(root, gcc.Tree)
+ check_isinstance(root, gcc.Tree)
self.root = root
self.show_addr = False
self.maxdepth = 6 # for now
@@ -496,13 +496,13 @@ class TreePrettyPrinter(DotPrettyPrinter):
return 'id%s' % id(obj)
def tree_to_dot(self, obj):
- assert isinstance(obj, gcc.Tree)
+ check_isinstance(obj, gcc.Tree)
return (' %s [label=<%s>];\n'
% (self.tree_id(obj), self.label_for_tree(obj)))
def recursive_tree_to_dot(self, obj, visited, depth):
print('recursive_tree_to_dot(%r, %r)' % (obj, visited))
- assert isinstance(obj, gcc.Tree)
+ check_isinstance(obj, gcc.Tree)
result = self.tree_to_dot(obj)
visited.add(obj.addr)
if depth < self.maxdepth:
@@ -619,3 +619,12 @@ class CallgraphPrettyPrinter(DotPrettyPrinter):
def callgraph_to_dot():
pp = CallgraphPrettyPrinter()
return pp.to_dot()
+
+def check_isinstance(obj, types):
+ """
+ Like:
+ assert isinstance(obj, types)
+ but with better error messages
+ """
+ if not isinstance(obj, types):
+ raise TypeError('%s is not an instance of %s' % (obj, types))
diff --git a/libcpychecker/absinterp.py b/libcpychecker/absinterp.py
index 1f674d9..10c584c 100644
--- a/libcpychecker/absinterp.py
+++ b/libcpychecker/absinterp.py
@@ -19,7 +19,7 @@ import gcc
import gccutils
import sys
from six import StringIO
-from gccutils import get_src_for_loc, get_nonnull_arguments
+from gccutils import get_src_for_loc, get_nonnull_arguments, check_isinstance
from collections import OrderedDict
from libcpychecker.utils import log
from libcpychecker.types import *
@@ -27,9 +27,9 @@ from libcpychecker.types import *
class AbstractValue:
def __init__(self, gcctype, loc):
if gcctype:
- assert isinstance(gcctype, gcc.Type)
+ check_isinstance(gcctype, gcc.Type)
if loc:
- assert isinstance(loc, gcc.Location)
+ check_isinstance(loc, gcc.Location)
self.gcctype = gcctype
self.loc = loc
@@ -51,8 +51,8 @@ class AbstractValue:
For use for handling function pointers. Return a list of Transition
instances giving the outcome of calling this function ptr value
"""
- assert isinstance(state, State)
- assert isinstance(stmt, gcc.GimpleCall)
+ check_isinstance(state, State)
+ check_isinstance(stmt, gcc.GimpleCall)
returntype = stmt.fn.type.dereference.type
return [state.make_assignment(stmt.lhs,
UnknownValue(returntype, stmt.loc),
@@ -79,9 +79,9 @@ class ConcreteValue(AbstractValue):
A known, specific value (e.g. 0)
"""
def __init__(self, gcctype, loc, value):
- assert isinstance(gcctype, gcc.Type)
+ check_isinstance(gcctype, gcc.Type)
if loc:
- assert isinstance(loc, gcc.Location)
+ check_isinstance(loc, gcc.Location)
self.gcctype = gcctype
self.loc = loc
self.value = value
@@ -104,7 +104,7 @@ class PointerToRegion(AbstractValue):
"""A non-NULL pointer value, pointing at a specific Region"""
def __init__(self, gcctype, loc, region):
AbstractValue.__init__(self, gcctype, loc)
- assert isinstance(region, Region)
+ check_isinstance(region, Region)
self.region = region
def __str__(self):
@@ -148,9 +148,9 @@ class InvalidlyNullParameter(PredictedError):
class NullPtrDereference(PredictedError):
def __init__(self, state, expr, ptr, isdefinite):
- assert isinstance(state, State)
- assert isinstance(expr, (gcc.ComponentRef, gcc.MemRef))
- assert isinstance(ptr, AbstractValue)
+ check_isinstance(state, State)
+ check_isinstance(expr, (gcc.ComponentRef, gcc.MemRef))
+ check_isinstance(ptr, AbstractValue)
self.state = state
self.expr = expr
self.ptr = ptr
@@ -166,8 +166,8 @@ class NullPtrDereference(PredictedError):
class ReadFromDeallocatedMemory(PredictedError):
def __init__(self, stmt, value):
- assert isinstance(stmt, gcc.Gimple)
- assert isinstance(value, DeallocatedMemory)
+ check_isinstance(stmt, gcc.Gimple)
+ check_isinstance(value, DeallocatedMemory)
self.stmt = stmt
self.value = value
@@ -187,8 +187,8 @@ class Location:
"""A location within a CFG: a gcc.BasicBlock together with an index into
the gimple list. (We don't support SSA passes)"""
def __init__(self, bb, idx):
- assert isinstance(bb, gcc.BasicBlock)
- assert isinstance(idx, int)
+ check_isinstance(bb, gcc.BasicBlock)
+ check_isinstance(idx, int)
self.bb = bb
self.idx = idx
@@ -255,7 +255,7 @@ class RegionForGlobal(Region):
used to store a particular globa
"""
def __init__(self, vardecl):
- assert isinstance(vardecl, gcc.VarDecl)
+ check_isinstance(vardecl, gcc.VarDecl)
Region.__init__(self, vardecl.name, None)
self.vardecl = vardecl
@@ -267,7 +267,7 @@ class RegionOnHeap(Region):
Represents an area of memory allocated on the heap
"""
def __init__(self, name, alloc_stmt):
- assert isinstance(alloc_stmt, gcc.Gimple)
+ check_isinstance(alloc_stmt, gcc.Gimple)
Region.__init__(self, name, None)
self.alloc_stmt = alloc_stmt
@@ -338,19 +338,19 @@ class State:
"""A Location with memory state"""
def __init__(self, loc, region_for_var=None, value_for_region=None,
return_rvalue=None, has_returned=False, not_returning=False):
- assert isinstance(loc, Location)
+ check_isinstance(loc, Location)
self.loc = loc
# Mapping from VarDecl.name to Region:
if region_for_var:
- assert isinstance(region_for_var, OrderedDict)
+ check_isinstance(region_for_var, OrderedDict)
self.region_for_var = region_for_var
else:
self.region_for_var = OrderedDict()
# Mapping from Region to AbstractValue:
if value_for_region:
- assert isinstance(value_for_region, OrderedDict)
+ check_isinstance(value_for_region, OrderedDict)
self.value_for_region = value_for_region
else:
self.value_for_region = OrderedDict()
@@ -413,7 +413,7 @@ class State:
Perform self-tests to ensure sanity of this State
"""
for k in self.value_for_region:
- assert isinstance(k, Region)
+ check_isinstance(k, Region)
if not isinstance(self.value_for_region[k], AbstractValue):
raise TypeError('value for region %r is not an AbstractValue: %r'
% (k, self.value_for_region[k]))
@@ -424,27 +424,27 @@ class State:
"""
log('eval_lvalue: %r %s' % (expr, expr))
if loc:
- assert isinstance(loc, gcc.Location)
+ check_isinstance(loc, gcc.Location)
if isinstance(expr, (gcc.VarDecl, gcc.ParmDecl)):
region = self.var_region(expr)
- assert isinstance(region, Region)
+ check_isinstance(region, Region)
return region
elif isinstance(expr, gcc.ArrayRef):
region = self.element_region(expr, loc)
- assert isinstance(region, Region)
+ check_isinstance(region, Region)
return region
elif isinstance(expr, gcc.ComponentRef):
- assert isinstance(expr.field, gcc.FieldDecl)
+ check_isinstance(expr.field, gcc.FieldDecl)
return self.get_field_region(expr, loc)
elif isinstance(expr, gcc.StringCst):
region = self.string_constant_region(expr, loc)
- assert isinstance(region, Region)
+ check_isinstance(region, Region)
return region
elif isinstance(expr, gcc.MemRef):
# Write through a pointer:
dest_ptr = self.eval_rvalue(expr.operand, loc)
log('dest_ptr: %r' % dest_ptr)
- assert isinstance(dest_ptr, PointerToRegion)
+ check_isinstance(dest_ptr, PointerToRegion)
dest_region = dest_ptr.region
log('dest_region: %r' % dest_region)
return dest_region
@@ -457,7 +457,7 @@ class State:
"""
log('eval_rvalue: %r %s' % (expr, expr))
if loc:
- assert isinstance(loc, gcc.Location)
+ check_isinstance(loc, gcc.Location)
if isinstance(expr, AbstractValue):
return expr
@@ -467,15 +467,15 @@ class State:
return ConcreteValue(expr.type, loc, expr.constant)
if isinstance(expr, (gcc.VarDecl, gcc.ParmDecl)):
region = self.var_region(expr)
- assert isinstance(region, Region)
+ check_isinstance(region, Region)
value = self.get_store(region, expr.type, loc)
- assert isinstance(value, AbstractValue)
+ check_isinstance(value, AbstractValue)
return value
#return UnknownValue(expr.type, str(expr))
if isinstance(expr, gcc.ComponentRef):
- #assert isinstance(expr.field, gcc.FieldDecl)
+ #check_isinstance(expr.field, gcc.FieldDecl)
region = self.get_field_region(expr, loc)#.target, expr.field.name)
- assert isinstance(region, Region)
+ check_isinstance(region, Region)
log('got field region for %s: %r' % (expr, region))
try:
value = self.get_store(region, expr.type, loc)
@@ -483,33 +483,33 @@ class State:
except MissingValue:
value = UnknownValue(expr.type, loc)
log('no value; using: %r' % value)
- assert isinstance(value, AbstractValue)
+ check_isinstance(value, AbstractValue)
return value
if isinstance(expr, gcc.AddrExpr):
log('expr.operand: %r' % expr.operand)
lvalue = self.eval_lvalue(expr.operand, loc)
- assert isinstance(lvalue, Region)
+ check_isinstance(lvalue, Region)
return PointerToRegion(expr.type, loc, lvalue)
if isinstance(expr, gcc.ArrayRef):
log('expr.array: %r' % expr.array)
log('expr.index: %r' % expr.index)
lvalue = self.eval_lvalue(expr, loc)
- assert isinstance(lvalue, Region)
+ check_isinstance(lvalue, Region)
rvalue = self.get_store(lvalue, expr.type, loc)
- assert isinstance(rvalue, AbstractValue)
+ check_isinstance(rvalue, AbstractValue)
return rvalue
if isinstance(expr, gcc.MemRef):
log('expr.operand: %r' % expr.operand)
opvalue = self.eval_rvalue(expr.operand, loc)
- assert isinstance(opvalue, AbstractValue)
+ check_isinstance(opvalue, AbstractValue)
log('opvalue: %r' % opvalue)
self.raise_any_null_ptr_deref(expr, opvalue)
if isinstance(opvalue, UnknownValue):
# Split into null/non-null pointers:
self.raise_split_value(opvalue)
- assert isinstance(opvalue, PointerToRegion) # FIXME
+ check_isinstance(opvalue, PointerToRegion) # FIXME
rvalue = self.get_store(opvalue.region, expr.type, loc)
- assert isinstance(rvalue, AbstractValue)
+ check_isinstance(rvalue, AbstractValue)
return rvalue
raise NotImplementedError('eval_rvalue: %r %s' % (expr, expr))
return UnknownValue(expr.type, loc) # FIXME
@@ -518,17 +518,17 @@ class State:
log('assign(%r, %r)' % (lhs, rhs))
log('assign(%s, %s)' % (lhs, rhs))
if loc:
- assert isinstance(loc, gcc.Location)
+ check_isinstance(loc, gcc.Location)
dest_region = self.eval_lvalue(lhs, loc)
log('dest_region: %s %r' % (dest_region, dest_region))
value = self.eval_rvalue(rhs, loc)
log('value: %s %r' % (value, value))
- assert isinstance(value, AbstractValue)
- assert isinstance(dest_region, Region)
+ check_isinstance(value, AbstractValue)
+ check_isinstance(dest_region, Region)
self.value_for_region[dest_region] = value
def var_region(self, var):
- assert isinstance(var, (gcc.VarDecl, gcc.ParmDecl))
+ check_isinstance(var, (gcc.VarDecl, gcc.ParmDecl))
if var not in self.region_for_var:
# Presumably a reference to a global variable:
log('adding region for global var: %r' % var)
@@ -546,17 +546,17 @@ class State:
def element_region(self, ar, loc):
log('element_region: %s' % ar)
- assert isinstance(ar, gcc.ArrayRef)
+ check_isinstance(ar, gcc.ArrayRef)
if loc:
- assert isinstance(loc, gcc.Location)
+ check_isinstance(loc, gcc.Location)
log(' ar.array: %r' % ar.array)
log(' ar.index: %r' % ar.index)
parent = self.eval_lvalue(ar.array, loc)
- assert isinstance(parent, Region)
+ check_isinstance(parent, Region)
log(' parent: %r' % parent)
index = self.eval_rvalue(ar.index, loc)
- assert isinstance(index, AbstractValue)
+ check_isinstance(index, AbstractValue)
log(' index: %r' % index)
if isinstance(index, ConcreteValue):
index = index.value
@@ -571,9 +571,9 @@ class State:
return region
def get_field_region(self, cr, loc): #target, field):
- assert isinstance(cr, gcc.ComponentRef)
+ check_isinstance(cr, gcc.ComponentRef)
if loc:
- assert isinstance(loc, gcc.Location)
+ check_isinstance(loc, gcc.Location)
#cr.debug()
log('target: %r %s ' % (cr.target, cr.target))
log('field: %r' % cr.field)
@@ -591,7 +591,7 @@ class State:
# Non-NULL pointer:
log('splitting %s into non-NULL/NULL pointers' % cr)
self.raise_split_value(ptr)
- assert isinstance(ptr, PointerToRegion)
+ check_isinstance(ptr, PointerToRegion)
return self.make_field_region(ptr.region, cr.field.name)
elif isinstance(cr.target, gcc.VarDecl):
log('bar')
@@ -608,17 +608,17 @@ class State:
def string_constant_region(self, expr, loc):
log('string_constant_region: %s' % expr)
- assert isinstance(expr, gcc.StringCst)
+ check_isinstance(expr, gcc.StringCst)
if loc:
- assert isinstance(loc, gcc.Location)
+ check_isinstance(loc, gcc.Location)
region = RegionForStringConstant(expr.constant)
return region
def get_store(self, region, gcctype, loc):
if gcctype:
- assert isinstance(gcctype, gcc.Type)
+ check_isinstance(gcctype, gcc.Type)
if loc:
- assert isinstance(loc, gcc.Location)
+ check_isinstance(loc, gcc.Location)
try:
val = self._get_store_recursive(region, gcctype, loc)
return val
@@ -635,7 +635,7 @@ class State:
return UnknownValue(gcctype, loc)
def _get_store_recursive(self, region, gcctype, loc):
- assert isinstance(region, Region)
+ check_isinstance(region, Region)
# self.log(log, 0)
if region in self.value_for_region:
return self.value_for_region[region]
@@ -656,8 +656,8 @@ class State:
return region
def make_field_region(self, target, field):
- assert isinstance(target, Region)
- assert isinstance(field, str)
+ check_isinstance(target, Region)
+ check_isinstance(field, str)
log('make_field_region(%r, %r)' % (target, field))
if field in target.fields:
log('reusing')
@@ -674,8 +674,8 @@ class State:
# Lookup varname.field
# For use in writing selftests
log('get_value_of_field_by_varname(%r, %r)' % (varname, field), 0)
- assert isinstance(varname, str)
- assert isinstance(field, str)
+ check_isinstance(varname, str)
+ check_isinstance(field, str)
for k in self.region_for_var:
if isinstance(k, gcc.VarDecl):
if k.name == varname:
@@ -688,8 +688,8 @@ class State:
# Lookup region->field
# For use in writing selftests
log('get_value_of_field_by_region(%r, %r)' % (region, field), 0)
- assert isinstance(region, Region)
- assert isinstance(field, str)
+ check_isinstance(region, Region)
+ check_isinstance(field, str)
if field in region.fields:
field_region = region.fields[field]
return self.value_for_region.get(field_region, None)
@@ -721,7 +721,7 @@ class State:
def make_assignment(self, lhs, rhs, desc):
log('make_assignment(%r, %r, %r)' % (lhs, rhs, desc))
if desc:
- assert isinstance(desc, str)
+ check_isinstance(desc, str)
new = self.copy()
new.loc = self.loc.next_loc()
if lhs:
@@ -773,9 +773,9 @@ class State:
FIXME: we should split into multiple non-NULL values, covering the
various aliasing possibilities
"""
- assert isinstance(ptr_rvalue, AbstractValue)
- assert isinstance(ptr_rvalue, UnknownValue)
- assert isinstance(ptr_rvalue.gcctype, gcc.PointerType)
+ check_isinstance(ptr_rvalue, AbstractValue)
+ check_isinstance(ptr_rvalue, UnknownValue)
+ check_isinstance(ptr_rvalue.gcctype, gcc.PointerType)
global region_id
region = Region('heap-region-%i' % region_id, None)
region_id += 1
@@ -788,8 +788,8 @@ region_id = 0
class Transition:
def __init__(self, src, dest, desc):
- assert isinstance(src, State)
- assert isinstance(dest, State)
+ check_isinstance(src, State)
+ check_isinstance(dest, State)
self.src = src
self.dest = dest
self.desc = desc
@@ -810,7 +810,7 @@ class Trace:
self.err = None
def add(self, transition):
- assert isinstance(transition, Transition)
+ check_isinstance(transition, Transition)
self.states.append(transition.dest)
self.transitions.append(transition)
return self
@@ -914,7 +914,7 @@ def iter_traces(fun, stateclass, prefix=None):
ConcreteValue(get_PyObjectPtr(), fun.start, 0))
curstate.init_for_function(fun)
else:
- assert isinstance(prefix, Trace)
+ check_isinstance(prefix, Trace)
curstate = prefix.states[-1]
if curstate.has_returned:
@@ -942,7 +942,7 @@ def iter_traces(fun, stateclass, prefix=None):
log(' %s:%s' % (fun.decl.name, curstate.loc))
try:
transitions = curstate.get_transitions()
- assert isinstance(transitions, list)
+ check_isinstance(transitions, list)
except PredictedError:
# We're at a terminating state:
err = sys.exc_info()[1]
@@ -957,14 +957,14 @@ def iter_traces(fun, stateclass, prefix=None):
# FIXME: this doesn't work; it thinks it's a loop :(
err = sys.exc_info()[1]
transitions = err.split(curstate)
- assert isinstance(transitions, list)
+ check_isinstance(transitions, list)
log('transitions: %s' % transitions, 2)
if len(transitions) > 0:
result = []
for transition in transitions:
- assert isinstance(transition, Transition)
+ check_isinstance(transition, Transition)
transition.dest.verify()
# Recurse:
@@ -985,7 +985,7 @@ class StateGraph:
termination of the analysis
"""
def __init__(self, fun, logger, stateclass):
- assert isinstance(fun, gcc.Function)
+ check_isinstance(fun, gcc.Function)
self.fun = fun
self.states = []
self.transitions = []
@@ -1008,7 +1008,7 @@ class StateGraph:
try:
transitions = curstate.get_transitions()
#print transitions
- assert isinstance(transitions, list)
+ check_isinstance(transitions, list)
except PredictedError:
# We're at a terminating state:
err = sys.exc_info()[1]
@@ -1025,7 +1025,7 @@ class StateGraph:
if len(transitions) > 0:
for transition in transitions:
# FIXME: what about loops???
- assert isinstance(transition, Transition)
+ check_isinstance(transition, Transition)
self.states.append(transition.dest)
self.transitions.append(transition)
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index 8406854..3ff0438 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -23,7 +23,7 @@
import sys
import gcc
-from gccutils import cfg_to_dot, invoke_dot, get_src_for_loc
+from gccutils import cfg_to_dot, invoke_dot, get_src_for_loc, check_isinstance
from libcpychecker.absinterp import *
from libcpychecker.diagnostics import Reporter
@@ -113,15 +113,15 @@ class GenericTpDealloc(AbstractValue):
i.e. one that frees up the underlying memory
"""
def get_transitions_for_function_call(self, state, stmt):
- assert isinstance(state, State)
- assert isinstance(stmt, gcc.GimpleCall)
+ check_isinstance(state, State)
+ check_isinstance(stmt, gcc.GimpleCall)
returntype = stmt.fn.type.dereference.type
# Mark the arg as being deallocated:
value = state.eval_rvalue(stmt.args[0], stmt.loc)
- assert isinstance(value, PointerToRegion)
+ check_isinstance(value, PointerToRegion)
region = value.region
- assert isinstance(region, Region)
+ check_isinstance(region, Region)
log('generic tp_dealloc called for %s' % region)
# Get the description of the region before trashing it:
@@ -206,7 +206,7 @@ class MyState(State):
def make_assignment(self, key, value, desc, additional_ptr=None):
if desc:
- assert isinstance(desc, str)
+ check_isinstance(desc, str)
transition = State.make_assignment(self, key, value, desc)
if additional_ptr:
transition.dest.owned_refs.append(additional_ptr)
@@ -258,9 +258,9 @@ class MyState(State):
The list of standard exception classes can be seen at:
http://docs.python.org/c-api/exceptions.html#standard-exceptions
"""
- assert isinstance(exc_name, str)
+ check_isinstance(exc_name, str)
exc_decl = gccutils.get_global_vardecl_by_name(exc_name)
- assert isinstance(exc_decl, gcc.VarDecl)
+ check_isinstance(exc_decl, gcc.VarDecl)
exc_region = self.var_region(exc_decl)
self.exception_rvalue = exc_region
@@ -271,17 +271,17 @@ class MyState(State):
(newobj, success, failure)
triple, where newobj is a region, and success/failure are Transitions
"""
- assert isinstance(stmt, gcc.GimpleCall)
- assert isinstance(stmt.fn.operand, gcc.FunctionDecl)
- assert isinstance(typename, str)
+ check_isinstance(stmt, gcc.GimpleCall)
+ check_isinstance(stmt.fn.operand, gcc.FunctionDecl)
+ check_isinstance(typename, str)
# the C struct for the type
- assert isinstance(typeobjname, str)
+ check_isinstance(typeobjname, str)
# the C identifier of the global PyTypeObject for the type
# Get the gcc.VarDecl for the global PyTypeObject
typeobjdecl = gccutils.get_global_vardecl_by_name(typeobjname)
- assert isinstance(typeobjdecl, gcc.VarDecl)
+ check_isinstance(typeobjdecl, gcc.VarDecl)
fnname = stmt.fn.operand.name
@@ -333,7 +333,7 @@ class MyState(State):
def steal_reference(self, region):
log('steal_reference(%r)' % region)
- assert isinstance(region, Region)
+ check_isinstance(region, Region)
ob_refcnt = self.make_field_region(region, 'ob_refcnt')
value = self.value_for_region[ob_refcnt]
if isinstance(value, RefcountValue):
@@ -372,9 +372,9 @@ class MyState(State):
def make_transitions_for_fncall(self, stmt, success, failure):
- assert isinstance(stmt, gcc.GimpleCall)
- assert isinstance(success, State)
- assert isinstance(failure, State)
+ check_isinstance(stmt, gcc.GimpleCall)
+ check_isinstance(success, State)
+ check_isinstance(failure, State)
fnname = stmt.fn.operand.name
@@ -387,7 +387,7 @@ class MyState(State):
# PyObject* PyList_New(Py_ssize_t len)
# Returns a new reference, or raises MemoryError
lenarg = self.eval_rvalue(stmt.args[0], stmt.loc)
- assert isinstance(lenarg, AbstractValue)
+ check_isinstance(lenarg, AbstractValue)
newobj, success, failure = self.impl_object_ctor(stmt,
'PyListObject', 'PyList_Type')
# Set ob_size:
@@ -429,7 +429,7 @@ class MyState(State):
# FIXME: update refcounts
# "Steal" a reference to item:
if isinstance(arg_item, PointerToRegion):
- assert isinstance(arg_item.region, Region)
+ check_isinstance(arg_item.region, Region)
success.steal_reference(arg_item.region)
# and discards a
@@ -494,7 +494,7 @@ class MyState(State):
# Calling through a function pointer:
val = self.eval_rvalue(stmt.fn, stmt.loc)
log('val: %s' % val)
- assert isinstance(val, AbstractValue)
+ check_isinstance(val, AbstractValue)
return val.get_transitions_for_function_call(self, stmt)
if isinstance(stmt.fn.operand, gcc.FunctionDecl):
@@ -554,15 +554,15 @@ class MyState(State):
nextstate = make_transition_for_false(stmt)
return [nextstate]
else:
- assert isinstance(boolval, UnknownValue)
+ check_isinstance(boolval, UnknownValue)
# We don't have enough information; both branches are possible:
return [make_transition_for_true(stmt),
make_transition_for_false(stmt)]
def eval_condition(self, stmt):
def is_equal(lhs, rhs):
- assert isinstance(lhs, AbstractValue)
- assert isinstance(rhs, AbstractValue)
+ check_isinstance(lhs, AbstractValue)
+ check_isinstance(rhs, AbstractValue)
if isinstance(rhs, ConcreteValue):
if isinstance(lhs, PointerToRegion) and rhs.value == 0:
log('ptr to region vs 0: %s is definitely not equal to %s' % (lhs, rhs))
@@ -668,7 +668,7 @@ class MyState(State):
value = self.eval_rhs(stmt)
log('value from eval_rhs: %r' % value)
- assert isinstance(value, AbstractValue)
+ check_isinstance(value, AbstractValue)
if isinstance(value, DeallocatedMemory):
raise ReadFromDeallocatedMemory(stmt, value)
@@ -722,9 +722,9 @@ class MyState(State):
bb = self.fun.cfg.get_block_for_label(label.target)
newstate.loc = Location(bb, 0)
if label.low:
- assert isinstance(label.low, gcc.IntegerCst)
+ check_isinstance(label.low, gcc.IntegerCst)
if label.high:
- assert isinstance(label.high, gcc.IntegerCst)
+ check_isinstance(label.high, gcc.IntegerCst)
desc = 'following cases %i...%i' % (label.low.constant, label.high.constant)
else:
desc = 'following case %i' % label.low.constant
@@ -746,7 +746,7 @@ def dump_traces_to_stdout(traces):
objects, etc)
"""
def dump_object(rvalue, title):
- assert isinstance(rvalue, AbstractValue)
+ check_isinstance(rvalue, AbstractValue)
print(' %s:' % title)
print(' repr(): %r' % rvalue)
print(' str(): %s' % rvalue)
@@ -757,7 +757,7 @@ def dump_traces_to_stdout(traces):
% endstate.get_value_of_field_by_region(rvalue.region, 'ob_type'))
def dump_region(region, title):
- assert isinstance(region, Region)
+ check_isinstance(region, Region)
print(' %s:' % title)
print(' repr(): %r' % region)
print(' str(): %s' % region)
@@ -827,7 +827,7 @@ def check_refcounts(fun, dump_traces=False, show_traces=False):
log('check_refcounts(%r, %r, %r)' % (fun, dump_traces, show_traces))
- assert isinstance(fun, gcc.Function)
+ check_isinstance(fun, gcc.Function)
if show_traces:
from libcpychecker.visualizations import StateGraphPrettyPrinter
@@ -877,7 +877,7 @@ def check_refcounts(fun, dump_traces=False, show_traces=False):
region = endstate.region_for_var[k]
log('considering ob_refcnt of %r' % region)
- assert isinstance(region, Region)
+ check_isinstance(region, Region)
# Consider those for which we know something about an "ob_refcnt"
# field:
diff --git a/libcpychecker/types.py b/libcpychecker/types.py
index ec24d51..a450a92 100644
--- a/libcpychecker/types.py
+++ b/libcpychecker/types.py
@@ -19,7 +19,7 @@
Helper functions for looking up various CPython implementation types.
"""
import gcc
-from gccutils import get_global_typedef
+from gccutils import get_global_typedef, check_isinstance
def is_py3k():
"""
@@ -101,7 +101,7 @@ type_dict = {
}
def get_type_for_typeobject(typeobject):
- assert isinstance(typeobject, gcc.VarDecl)
+ check_isinstance(typeobject, gcc.VarDecl)
if typeobject.name not in type_dict:
return None
diff --git a/libcpychecker/visualizations.py b/libcpychecker/visualizations.py
index b646c46..2521c0e 100644
--- a/libcpychecker/visualizations.py
+++ b/libcpychecker/visualizations.py
@@ -16,7 +16,7 @@
# <http://www.gnu.org/licenses/>.
import gcc
-from gccutils import CfgPrettyPrinter, get_src_for_loc
+from gccutils import CfgPrettyPrinter, get_src_for_loc, check_isinstance
class StatePrettyPrinter(CfgPrettyPrinter):
"""
@@ -180,7 +180,7 @@ class HtmlRenderer:
which requires JavaScript and the HTML <canvas> element
"""
def __init__(self, fun):
- assert isinstance(fun, gcc.Function)
+ check_isinstance(fun, gcc.Function)
self.fun = fun
from pygments.styles import get_style_by_name
12 years, 9 months
[gcc-python-plugin] cpychecker: rewrite State.assign in terms of State.eval_lvalue
by David Hugh Malcolm
commit b09a4885c6e4d0de15c810248e9c1ec570a05b78
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Mon Aug 22 19:32:16 2011 -0400
cpychecker: rewrite State.assign in terms of State.eval_lvalue
These methods both tried to do the same thing, but covered different
subsets of expression types. Consolidate the handling of the various
expression types ino State.eval_lvalue()
As a by-product, the checker can now deal with writes to arrays
libcpychecker/absinterp.py | 32 +++++++++++--------------
tests/cpychecker/absinterp/arrays/input.c | 3 +-
tests/cpychecker/absinterp/arrays/stdout.txt | 4 +-
3 files changed, 18 insertions(+), 21 deletions(-)
---
diff --git a/libcpychecker/absinterp.py b/libcpychecker/absinterp.py
index 521439b..1f674d9 100644
--- a/libcpychecker/absinterp.py
+++ b/libcpychecker/absinterp.py
@@ -433,10 +433,21 @@ class State:
region = self.element_region(expr, loc)
assert isinstance(region, Region)
return region
+ elif isinstance(expr, gcc.ComponentRef):
+ assert isinstance(expr.field, gcc.FieldDecl)
+ return self.get_field_region(expr, loc)
elif isinstance(expr, gcc.StringCst):
region = self.string_constant_region(expr, loc)
assert isinstance(region, Region)
return region
+ elif isinstance(expr, gcc.MemRef):
+ # Write through a pointer:
+ dest_ptr = self.eval_rvalue(expr.operand, loc)
+ log('dest_ptr: %r' % dest_ptr)
+ assert isinstance(dest_ptr, PointerToRegion)
+ dest_region = dest_ptr.region
+ log('dest_region: %r' % dest_region)
+ return dest_region
raise NotImplementedError('eval_lvalue: %r %s' % (expr, expr))
def eval_rvalue(self, expr, loc):
@@ -508,27 +519,12 @@ class State:
log('assign(%s, %s)' % (lhs, rhs))
if loc:
assert isinstance(loc, gcc.Location)
- if isinstance(lhs, (gcc.VarDecl, gcc.ParmDecl)):
- dest_region = self.var_region(lhs)
- elif isinstance(lhs, gcc.ComponentRef):
- assert isinstance(lhs.field, gcc.FieldDecl)
- dest_region = self.get_field_region(lhs, loc)
- #elif isinstance(lhs, gcc.ArrayRef):
- # assert isinstance(lhs.field, gcc.FieldDecl)
- # dest_region = self.get_array_region(lhs)
- elif isinstance(lhs, gcc.MemRef):
- # Write through a pointer:
- dest_ptr = self.eval_rvalue(lhs.operand, loc)
- log('dest_ptr: %r' % dest_ptr)
- assert isinstance(dest_ptr, PointerToRegion)
- dest_region = dest_ptr.region
- log('dest_region: %r' % dest_region)
- else:
- raise NotImplementedError("Don't know how to cope with assignment to %r (%s)"
- % (lhs, lhs))
+ dest_region = self.eval_lvalue(lhs, loc)
+ log('dest_region: %s %r' % (dest_region, dest_region))
value = self.eval_rvalue(rhs, loc)
log('value: %s %r' % (value, value))
assert isinstance(value, AbstractValue)
+ assert isinstance(dest_region, Region)
self.value_for_region[dest_region] = value
def var_region(self, var):
diff --git a/tests/cpychecker/absinterp/arrays/input.c b/tests/cpychecker/absinterp/arrays/input.c
index 0a09dad..3ed7ace 100644
--- a/tests/cpychecker/absinterp/arrays/input.c
+++ b/tests/cpychecker/absinterp/arrays/input.c
@@ -27,7 +27,8 @@ int test_arrays(void)
int arr[10];
int *ptr = &arr[5];
- *ptr = 42;
+ *ptr = 41;
+ arr[5] += 1;
/*
The analyser ought to be able to figure out that there's a single trace
diff --git a/tests/cpychecker/absinterp/arrays/stdout.txt b/tests/cpychecker/absinterp/arrays/stdout.txt
index 722617f..0cf8219 100644
--- a/tests/cpychecker/absinterp/arrays/stdout.txt
+++ b/tests/cpychecker/absinterp/arrays/stdout.txt
@@ -2,7 +2,7 @@ Trace 0:
Transitions:
'returning'
Return value:
- repr(): ConcreteValue(gcctype='int', loc=gcc.Location(file='tests/cpychecker/absinterp/arrays/input.c', line=30), value=42L)
- str(): (int)42L from tests/cpychecker/absinterp/arrays/input.c:30
+ repr(): ConcreteValue(gcctype='int', loc=gcc.Location(file='tests/cpychecker/absinterp/arrays/input.c', line=31), value=42L)
+ str(): (int)42L from tests/cpychecker/absinterp/arrays/input.c:31
Exception:
(struct PyObject *)0 from tests/cpychecker/absinterp/arrays/input.c:26
12 years, 9 months
[gcc-python-plugin] run-test-suite.py: fix typo
by David Hugh Malcolm
commit 102d63bf2b1c349fa7546859eb536a27a9303a75
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Mon Aug 22 17:51:45 2011 -0400
run-test-suite.py: fix typo
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 82a67aa..04a4a43 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] cpychecker: Add selftest for a simple getter
by David Hugh Malcolm
commit 89b00bcd9136a9420238ad6b9525f734b3e989b9
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Mon Aug 22 17:29:28 2011 -0400
cpychecker: Add selftest for a simple getter
tests/cpychecker/refcounts/getter/input.c | 60 ++++++++++++++++++++++++++
tests/cpychecker/refcounts/getter/script.py | 22 +++++++++
tests/cpychecker/refcounts/getter/stdout.txt | 13 ++++++
3 files changed, 95 insertions(+), 0 deletions(-)
---
diff --git a/tests/cpychecker/refcounts/getter/input.c b/tests/cpychecker/refcounts/getter/input.c
new file mode 100644
index 0000000..a3f213d
--- /dev/null
+++ b/tests/cpychecker/refcounts/getter/input.c
@@ -0,0 +1,60 @@
+/*
+ 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 analyser can cope with getters for
+ PyObject subclasses
+*/
+
+struct MySubclass {
+ PyObject_HEAD
+ struct foo *f;
+};
+
+extern PyObject *make_wrapper(struct foo *f);
+
+static PyObject *
+simple_getter(struct MySubclass *self, void *closure)
+{
+ /*
+ The analyser should assume that self is non-NULL
+ and thus not report a NULL ptr dereference below:
+ */
+
+ return make_wrapper(self->f);
+}
+
+static PyGetSetDef gcc_Variable_getset_table[] = {
+ {"test_attrib",
+ (getter)simple_getter,
+ (setter)NULL,
+ NULL,
+ NULL},
+ {NULL} /* Sentinel */
+};
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/refcounts/getter/script.py b/tests/cpychecker/refcounts/getter/script.py
new file mode 100644
index 0000000..6bb62fd
--- /dev/null
+++ b/tests/cpychecker/refcounts/getter/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/refcounts/getter/stdout.txt b/tests/cpychecker/refcounts/getter/stdout.txt
new file mode 100644
index 0000000..581721c
--- /dev/null
+++ b/tests/cpychecker/refcounts/getter/stdout.txt
@@ -0,0 +1,13 @@
+Trace 0:
+ Transitions:
+ 'returning'
+ Return value:
+ repr(): UnknownValue(gcctype=<gcc.PointerType object at 0xdeadbeef>, loc=gcc.Location(file='tests/cpychecker/refcounts/getter/input.c', line=42))
+ str(): unknown struct PyObject * from tests/cpychecker/refcounts/getter/input.c:42
+ 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/getter/input.c', line=35), region=Region("region-for-type-of-arg-gcc.ParmDecl('self')"))
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/refcounts/getter/input.c:36
12 years, 9 months