From 9675e4233b65c0c4b67efd28e6ee0a8bec6bb078 Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Thu, 22 May 2003 15:13:22 +0000 Subject: [PATCH] Fix a bug in dangling reference/pointer detection. Thanks to Daniel Paull for reporting it. [SVN r18498] --- include/boost/python/call.hpp | 15 ++++++++++++--- include/boost/python/call_method.hpp | 15 ++++++++++++--- src/converter/from_python.cpp | 2 +- test/callbacks.py | 11 ++++++++++- 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/include/boost/python/call.hpp b/include/boost/python/call.hpp index d80cba3c..ff74eb36 100644 --- a/include/boost/python/call.hpp +++ b/include/boost/python/call.hpp @@ -52,13 +52,22 @@ call(PyObject* callable , boost::type* = 0 ) { - converter::return_from_python converter; - return converter( + PyObject* const result = PyEval_CallFunction( callable , const_cast("(" BOOST_PP_REPEAT_1ST(N, BOOST_PYTHON_FIXED, "O") ")") BOOST_PP_REPEAT_1ST(N, BOOST_PYTHON_FAST_ARG_TO_PYTHON_GET, nil) - )); + ); + + // This conversion *must not* be done in the same expression as + // the call, because, in the special case where the result is a + // reference a Python object which was created by converting a C++ + // argument for passing to PyEval_CallFunction, its reference + // count will be 2 until the end of the full expression containing + // the conversion, and that interferes with dangling + // pointer/reference detection. + converter::return_from_python converter; + return converter(result); } # undef N diff --git a/include/boost/python/call_method.hpp b/include/boost/python/call_method.hpp index 347f866c..4e9b9373 100644 --- a/include/boost/python/call_method.hpp +++ b/include/boost/python/call_method.hpp @@ -51,14 +51,23 @@ call_method(PyObject* self, char const* name , boost::type* = 0 ) { - converter::return_from_python converter; - return converter( + PyObject* const result = PyEval_CallMethod( self , const_cast(name) , const_cast("(" BOOST_PP_REPEAT_1ST(N, BOOST_PYTHON_FIXED, "O") ")") BOOST_PP_REPEAT_1ST(N, BOOST_PYTHON_FAST_ARG_TO_PYTHON_GET, nil) - )); + ); + + // This conversion *must not* be done in the same expression as + // the call, because, in the special case where the result is a + // reference a Python object which was created by converting a C++ + // argument for passing to PyEval_CallFunction, its reference + // count will be 2 until the end of the full expression containing + // the conversion, and that interferes with dangling + // pointer/reference detection. + converter::return_from_python converter; + return converter(result); } # undef N diff --git a/src/converter/from_python.cpp b/src/converter/from_python.cpp index e633debf..f0667254 100644 --- a/src/converter/from_python.cpp +++ b/src/converter/from_python.cpp @@ -218,7 +218,7 @@ namespace , char const* ref_type) { handle<> holder(source); - if (source->ob_refcnt <= 2) + if (source->ob_refcnt <= 1) { handle<> msg( ::PyString_FromFormat( diff --git a/test/callbacks.py b/test/callbacks.py index 937961fd..d5bf44d4 100644 --- a/test/callbacks.py +++ b/test/callbacks.py @@ -15,9 +15,18 @@ Once we have array conversion support, this test will fail. Er, succeed: >>> try: apply_to_string_literal(identity) -... except: pass # expected +... except ReferenceError: pass # expected ... else: print 'expected an exception!' +>>> try: apply_X_ref_handle(lambda ignored:X(42), None) +... except ReferenceError: pass # expected +... else: print 'expected an exception!' + +>>> x = X(42) +>>> x.y = X(7) +>>> apply_X_ref_handle(lambda z:z.y, x).value() +7 + >>> x = apply_X_X(identity, X(42)) >>> x.value() 42