commit b28d92ee0b4da0bf68c2f3ee63051be6e55090e2 Author: David Malcolm dmalcolm@redhat.com Date: Fri Sep 23 18:46:52 2011 -0400
cpychecker: implement PyErr_Occurred and PyInt_AsLong
Implement PyErr_Occurred and PyInt_AsLong (the selftests for PyInt_AsLong required PyErr_Occurred)
Also, propagate the value in PyInt_FromLong within ob_ival, so it round-trips through a subsequent call to PyInt_AsLong.
libcpychecker/refcounts.py | 60 +++++++++++++++- .../PyInt_AsLong/correct_PyIntObject/input.c | 54 +++++++++++++++ .../PyInt_AsLong/correct_PyIntObject/script.py | 22 ++++++ .../PyInt_AsLong/correct_PyIntObject/stdout.txt | 23 ++++++ .../refcounts/PyInt_AsLong/correct_cast/input.c | 62 +++++++++++++++++ .../refcounts/PyInt_AsLong/correct_cast/script.py | 22 ++++++ .../refcounts/PyInt_AsLong/correct_cast/stdout.txt | 72 ++++++++++++++++++++ 7 files changed, 311 insertions(+), 4 deletions(-) --- diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py index 82914b0..8a3af47 100644 --- a/libcpychecker/refcounts.py +++ b/libcpychecker/refcounts.py @@ -894,6 +894,19 @@ class MyState(State): t_next.dest.set_exception('PyExc_MemoryError', stmt.loc) return [t_next]
+ def impl_PyErr_Occurred(self, stmt): + # Declared in pyerrors.h: + # PyAPI_FUNC(PyObject *) PyErr_Occurred(void); + # + # Defined in Python/errors.c + # + # http://docs.python.org/c-api/exceptions.html#PyErr_Occurred + # Returns a borrowed reference; can't fail: + t_next = self.mktrans_assignment(stmt.lhs, + self.exception_rvalue, + 'PyErr_Occurred()') + return [t_next] + def impl_PyErr_Print(self, stmt): # Declared in pythonrun.h: # PyAPI_FUNC(void) PyErr_Print(void); @@ -1024,6 +1037,41 @@ class MyState(State): ######################################################################## # Py_Int* ######################################################################## + def impl_PyInt_AsLong(self, stmt): + # Declared in intobject.h as: + # PyAPI_FUNC(long) PyInt_AsLong(PyObject *); + # Defined in Objects/intobject.c + # + # http://docs.python.org/c-api/int.html#PyInt_AsLong + + # Can fail (gracefully) with NULL, and with non-int objects + + args = self.eval_stmt_args(stmt) + v_op = args[0] + + returntype = stmt.fn.type.dereference.type + + if self.object_ptr_has_global_ob_type(v_op, 'PyInt_Type'): + # We know it's a PyIntObject; the call will succeed: + # FIXME: cast: + v_ob_ival = self.get_value_of_field_by_region(v_op.region, + 'ob_ival') + t_success = self.mktrans_assignment(stmt.lhs, + v_ob_ival, + 'PyInt_AsLong() returns ob_ival') + return [t_success] + + # We don't know if it's a PyIntObject (or subclass); the call could + # fail: + t_success = self.mktrans_assignment(stmt.lhs, + UnknownValue(returntype, stmt.loc), + 'PyInt_AsLong() succeeds') + t_failure = self.mktrans_assignment(stmt.lhs, + ConcreteValue(returntype, stmt.loc, -1), + 'PyInt_AsLong() fails') + t_failure.dest.set_exception('PyExc_MemoryError', stmt.loc) + return [t_success, t_failure] + def impl_PyInt_FromLong(self, stmt): # Declared in intobject.h as: # PyAPI_FUNC(PyObject *) PyInt_FromLong(long); @@ -1038,14 +1086,18 @@ class MyState(State): args = self.eval_stmt_args(stmt) v_ival = args[0]
- newobj, success, failure = self.impl_object_ctor(stmt, - 'PyIntObject', 'PyInt_Type') + newobj, t_success, t_failure = self.impl_object_ctor(stmt, + 'PyIntObject', 'PyInt_Type') + # Set ob_size: + r_ob_size = t_success.dest.make_field_region(newobj, 'ob_ival') + t_success.dest.value_for_region[r_ob_size] = v_ival + if isinstance(v_ival, ConcreteValue): if v_ival.value >= -5 and v_ival.value < 257: # We know that failure isn't possible: - return [success] + return [t_success]
- return [success, failure] + return [t_success, t_failure]
######################################################################## # PyList_* diff --git a/tests/cpychecker/refcounts/PyInt_AsLong/correct_PyIntObject/input.c b/tests/cpychecker/refcounts/PyInt_AsLong/correct_PyIntObject/input.c new file mode 100644 index 0000000..c86c0ca --- /dev/null +++ b/tests/cpychecker/refcounts/PyInt_AsLong/correct_PyIntObject/input.c @@ -0,0 +1,54 @@ +/* + Copyright 2011 David Malcolm dmalcolm@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 PyInt_AsLong works for the case where we know it's a PyIntObject +*/ + +extern void __cpychecker_dump(long val); + +PyObject * +test(PyObject *self, PyObject *args) +{ + PyObject *int_obj; + long long_val; + + /* This call can't fail, since we're in the interval [-5..257) */ + int_obj = PyInt_FromLong(42); + + /* Verify that we can roundtrip the underlying int: */ + long_val = PyInt_AsLong(int_obj); + __cpychecker_dump(long_val); + + return int_obj; +} +static PyMethodDef test_methods[] = { + {"test_method", test, 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/PyInt_AsLong/correct_PyIntObject/script.py b/tests/cpychecker/refcounts/PyInt_AsLong/correct_PyIntObject/script.py new file mode 100644 index 0000000..fdd5ba3 --- /dev/null +++ b/tests/cpychecker/refcounts/PyInt_AsLong/correct_PyIntObject/script.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Copyright 2011 David Malcolm dmalcolm@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/PyInt_AsLong/correct_PyIntObject/stdout.txt b/tests/cpychecker/refcounts/PyInt_AsLong/correct_PyIntObject/stdout.txt new file mode 100644 index 0000000..462d8e4 --- /dev/null +++ b/tests/cpychecker/refcounts/PyInt_AsLong/correct_PyIntObject/stdout.txt @@ -0,0 +1,23 @@ +Trace 0: + Transitions: + 'PyInt_FromLong() succeeds' + 'PyInt_AsLong() returns ob_ival' + '__dump((long int)42 from tests/cpychecker/refcounts/PyInt_AsLong/correct_PyIntObject/input.c:35)' + 'returning' + Return value: + repr(): PointerToRegion(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyInt_AsLong/correct_PyIntObject/input.c', line=35), region=RegionOnHeap('PyIntObject', gcc.Location(file='tests/cpychecker/refcounts/PyInt_AsLong/correct_PyIntObject/input.c', line=35))) + str(): (struct PyObject *)&RegionOnHeap('PyIntObject', gcc.Location(file='tests/cpychecker/refcounts/PyInt_AsLong/correct_PyIntObject/input.c', line=35)) from tests/cpychecker/refcounts/PyInt_AsLong/correct_PyIntObject/input.c:35 + r->ob_refcnt: refs: 1 + N where N >= 0 + r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyInt_AsLong/correct_PyIntObject/input.c', line=35), region=RegionForGlobal(gcc.VarDecl('PyInt_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: refs: 0 + N where N >= 1 + r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyInt_AsLong/correct_PyIntObject/input.c', line=29), region=Region("region-for-type-of-arg-gcc.ParmDecl('self')")) + Region("region-for-arg-gcc.ParmDecl('args')"): + repr(): Region("region-for-arg-gcc.ParmDecl('args')") + str(): Region("region-for-arg-gcc.ParmDecl('args')") + r->ob_refcnt: refs: 0 + N where N >= 1 + r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyInt_AsLong/correct_PyIntObject/input.c', line=29), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')")) + Exception: + (struct PyObject *)0 from tests/cpychecker/refcounts/PyInt_AsLong/correct_PyIntObject/input.c:30 diff --git a/tests/cpychecker/refcounts/PyInt_AsLong/correct_cast/input.c b/tests/cpychecker/refcounts/PyInt_AsLong/correct_cast/input.c new file mode 100644 index 0000000..7e5bf62 --- /dev/null +++ b/tests/cpychecker/refcounts/PyInt_AsLong/correct_cast/input.c @@ -0,0 +1,62 @@ +/* + Copyright 2011 David Malcolm dmalcolm@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 PyInt_AsLong works for the case where we don't know if it's + a PyIntObject +*/ + +extern void __cpychecker_dump(long val); + +PyObject * +test(PyObject *self, PyObject *args) +{ + long long_val; + + long_val = PyInt_AsLong(self); + if (-1 == long_val) { + if (PyErr_Occurred()) { + /* Unsuccessful coercion to "long": */ + return NULL; + } else { + /* Successful coercion to "long"; + it just happened to be -1: */ + __cpychecker_dump(long_val); + } + } else { + /* Successful coercion to "long": */ + __cpychecker_dump(long_val); + } + + Py_RETURN_NONE; +} +static PyMethodDef test_methods[] = { + {"test_method", test, 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/PyInt_AsLong/correct_cast/script.py b/tests/cpychecker/refcounts/PyInt_AsLong/correct_cast/script.py new file mode 100644 index 0000000..fdd5ba3 --- /dev/null +++ b/tests/cpychecker/refcounts/PyInt_AsLong/correct_cast/script.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Copyright 2011 David Malcolm dmalcolm@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/PyInt_AsLong/correct_cast/stdout.txt b/tests/cpychecker/refcounts/PyInt_AsLong/correct_cast/stdout.txt new file mode 100644 index 0000000..30faf66 --- /dev/null +++ b/tests/cpychecker/refcounts/PyInt_AsLong/correct_cast/stdout.txt @@ -0,0 +1,72 @@ +Trace 0: + Transitions: + 'PyInt_AsLong() succeeds' + 'taking True path' + 'PyErr_Occurred()' + 'taking False path' + '__dump(unknown long int from tests/cpychecker/refcounts/PyInt_AsLong/correct_cast/input.c:34)' + 'returning' + Return value: + repr(): PointerToRegion(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyInt_AsLong/correct_cast/input.c', line=49), region=RegionForGlobal(gcc.VarDecl('_Py_NoneStruct'))) + str(): (struct PyObject *)&RegionForGlobal(gcc.VarDecl('_Py_NoneStruct')) from tests/cpychecker/refcounts/PyInt_AsLong/correct_cast/input.c:49 + r->ob_refcnt: refs: 1 + N where N >= 1 + r->ob_type: None + Region("region-for-arg-gcc.ParmDecl('self')"): + repr(): Region("region-for-arg-gcc.ParmDecl('self')") + str(): Region("region-for-arg-gcc.ParmDecl('self')") + r->ob_refcnt: refs: 0 + N where N >= 1 + r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyInt_AsLong/correct_cast/input.c', line=30), region=Region("region-for-type-of-arg-gcc.ParmDecl('self')")) + Region("region-for-arg-gcc.ParmDecl('args')"): + repr(): Region("region-for-arg-gcc.ParmDecl('args')") + str(): Region("region-for-arg-gcc.ParmDecl('args')") + r->ob_refcnt: refs: 0 + N where N >= 1 + r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyInt_AsLong/correct_cast/input.c', line=30), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')")) + Exception: + (struct PyObject *)0 from tests/cpychecker/refcounts/PyInt_AsLong/correct_cast/input.c:31 + +Trace 1: + Transitions: + 'PyInt_AsLong() succeeds' + 'taking False path' + '__dump(unknown long int from tests/cpychecker/refcounts/PyInt_AsLong/correct_cast/input.c:34)' + 'returning' + Return value: + repr(): PointerToRegion(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyInt_AsLong/correct_cast/input.c', line=49), region=RegionForGlobal(gcc.VarDecl('_Py_NoneStruct'))) + str(): (struct PyObject *)&RegionForGlobal(gcc.VarDecl('_Py_NoneStruct')) from tests/cpychecker/refcounts/PyInt_AsLong/correct_cast/input.c:49 + r->ob_refcnt: refs: 1 + N where N >= 1 + r->ob_type: None + Region("region-for-arg-gcc.ParmDecl('self')"): + repr(): Region("region-for-arg-gcc.ParmDecl('self')") + str(): Region("region-for-arg-gcc.ParmDecl('self')") + r->ob_refcnt: refs: 0 + N where N >= 1 + r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyInt_AsLong/correct_cast/input.c', line=30), region=Region("region-for-type-of-arg-gcc.ParmDecl('self')")) + Region("region-for-arg-gcc.ParmDecl('args')"): + repr(): Region("region-for-arg-gcc.ParmDecl('args')") + str(): Region("region-for-arg-gcc.ParmDecl('args')") + r->ob_refcnt: refs: 0 + N where N >= 1 + r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyInt_AsLong/correct_cast/input.c', line=30), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')")) + Exception: + (struct PyObject *)0 from tests/cpychecker/refcounts/PyInt_AsLong/correct_cast/input.c:31 + +Trace 2: + Transitions: + 'PyInt_AsLong() fails' + 'taking True path' + 'PyErr_Occurred()' + 'taking True path' + 'returning' + Return value: + repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyInt_AsLong/correct_cast/input.c', line=38), value=0) + str(): (struct PyObject *)0 from tests/cpychecker/refcounts/PyInt_AsLong/correct_cast/input.c:38 + Region("region-for-arg-gcc.ParmDecl('self')"): + repr(): Region("region-for-arg-gcc.ParmDecl('self')") + str(): Region("region-for-arg-gcc.ParmDecl('self')") + r->ob_refcnt: refs: 0 + N where N >= 1 + r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyInt_AsLong/correct_cast/input.c', line=30), region=Region("region-for-type-of-arg-gcc.ParmDecl('self')")) + Region("region-for-arg-gcc.ParmDecl('args')"): + repr(): Region("region-for-arg-gcc.ParmDecl('args')") + str(): Region("region-for-arg-gcc.ParmDecl('args')") + r->ob_refcnt: refs: 0 + N where N >= 1 + r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyInt_AsLong/correct_cast/input.c', line=30), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')")) + Exception: + (struct PyObject *)&RegionForGlobal(gcc.VarDecl('PyExc_MemoryError')) from tests/cpychecker/refcounts/PyInt_AsLong/correct_cast/input.c:34
gcc-python-plugin-commits@lists.stg.fedorahosted.org