Skip to content

Commit 9675e42

Browse files
committed
Fix a bug in dangling reference/pointer detection. Thanks to Daniel
Paull <dlp-at-fractaltechnologies.com> for reporting it. [SVN r18498]
1 parent e6a176b commit 9675e42

File tree

4 files changed

+35
-8
lines changed

4 files changed

+35
-8
lines changed

include/boost/python/call.hpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,22 @@ call(PyObject* callable
5252
, boost::type<R>* = 0
5353
)
5454
{
55-
converter::return_from_python<R> converter;
56-
return converter(
55+
PyObject* const result =
5756
PyEval_CallFunction(
5857
callable
5958
, const_cast<char*>("(" BOOST_PP_REPEAT_1ST(N, BOOST_PYTHON_FIXED, "O") ")")
6059
BOOST_PP_REPEAT_1ST(N, BOOST_PYTHON_FAST_ARG_TO_PYTHON_GET, nil)
61-
));
60+
);
61+
62+
// This conversion *must not* be done in the same expression as
63+
// the call, because, in the special case where the result is a
64+
// reference a Python object which was created by converting a C++
65+
// argument for passing to PyEval_CallFunction, its reference
66+
// count will be 2 until the end of the full expression containing
67+
// the conversion, and that interferes with dangling
68+
// pointer/reference detection.
69+
converter::return_from_python<R> converter;
70+
return converter(result);
6271
}
6372

6473
# undef N

include/boost/python/call_method.hpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,23 @@ call_method(PyObject* self, char const* name
5151
, boost::type<R>* = 0
5252
)
5353
{
54-
converter::return_from_python<R> converter;
55-
return converter(
54+
PyObject* const result =
5655
PyEval_CallMethod(
5756
self
5857
, const_cast<char*>(name)
5958
, const_cast<char*>("(" BOOST_PP_REPEAT_1ST(N, BOOST_PYTHON_FIXED, "O") ")")
6059
BOOST_PP_REPEAT_1ST(N, BOOST_PYTHON_FAST_ARG_TO_PYTHON_GET, nil)
61-
));
60+
);
61+
62+
// This conversion *must not* be done in the same expression as
63+
// the call, because, in the special case where the result is a
64+
// reference a Python object which was created by converting a C++
65+
// argument for passing to PyEval_CallFunction, its reference
66+
// count will be 2 until the end of the full expression containing
67+
// the conversion, and that interferes with dangling
68+
// pointer/reference detection.
69+
converter::return_from_python<R> converter;
70+
return converter(result);
6271
}
6372

6473
# undef N

src/converter/from_python.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ namespace
218218
, char const* ref_type)
219219
{
220220
handle<> holder(source);
221-
if (source->ob_refcnt <= 2)
221+
if (source->ob_refcnt <= 1)
222222
{
223223
handle<> msg(
224224
::PyString_FromFormat(

test/callbacks.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,18 @@
1515
succeed<wink>:
1616
1717
>>> try: apply_to_string_literal(identity)
18-
... except: pass # expected
18+
... except ReferenceError: pass # expected
1919
... else: print 'expected an exception!'
2020
21+
>>> try: apply_X_ref_handle(lambda ignored:X(42), None)
22+
... except ReferenceError: pass # expected
23+
... else: print 'expected an exception!'
24+
25+
>>> x = X(42)
26+
>>> x.y = X(7)
27+
>>> apply_X_ref_handle(lambda z:z.y, x).value()
28+
7
29+
2130
>>> x = apply_X_X(identity, X(42))
2231
>>> x.value()
2332
42

0 commit comments

Comments
 (0)