The directorin typemap fixed a couple of commits ago is in the Unified
Typemap Library and so is shared by every language defining SWIG_Object,
but the test for it was written twice, once against PyObject * and once
against VALUE. Replace python_director_pyobject and ruby_director_value
with director_langobj, written against SWIG_Object in the same spirit as
the existing langobj test, and run it from the common test list so that
every language at least compiles it.
Runme files are added for the four languages that define both
SWIG_Object and SWIG_DIRECTOR_TYPEMAPS, that is the four that generate
director code and were affected: Python, Ruby, Perl and Octave. Tcl, R
and Scilab define SWIG_Object but have no directors, so they get compile
only coverage.
python_director_pyobject is kept for the Python specific parts that have
no equivalent in the other languages: the swig::SwigPtr_PyObject and
swig::SwigVar_PyObject smart pointers, by value and by const reference,
and a null PyObject * argument.
Assisted-by: Claude Code (Opus 5)
There is a directorin typemap for SWIG_Object but none for
SWIG_Object const &, so the const reference form fell through to the
generic SWIGTYPE *const& typemap and was wrapped as an opaque proxy
object instead of being passed through unchanged. Affects every
language defining both SWIG_Object and SWIG_DIRECTOR_TYPEMAPS, that is
Python, Ruby, Perl and Octave. For Ruby the generated code did not even
compile, as SWIG_as_voidptr cannot cast a VALUE to void *.
For Python this also covers swig::SwigPtr_PyObject const& and
swig::SwigVar_PyObject const&, which are %applied from PyObject *const&.
Additionally fix a reference count leak for a swig::SwigVar_PyObject
director method argument passed by value. Assigning it into the
SwigVar_PyObject wrapper variable selects the implicitly declared copy
assignment operator, which adds a reference of its own, so the typemap's
SWIG_Py_XINCREF was a second increment against a single decrement on
scope exit. Casting to PyObject * selects
SwigVar_PyObject::operator=(PyObject *), which does not adjust the
count, making the SWIG_Py_XINCREF the one and only increment for every
argument type the typemap handles.
The existing python_director_pyobject test did not catch the leak
because its C++ caller built the SwigVar_PyObject from a borrowed raw
pointer, so the temporary stole a reference and cancelled it out. The
callers are now reference count neutral, the const reference forms are
covered, and the test checks that the object arriving in Python is the
object passed from C++, not just that the count is stable.
Assisted-by: Claude Code (Opus 5)
Follow on fixes to the previous commit:
Use SWIG_Py_XINCREF instead of Py_INCREF. Py_INCREF crashes on a null
PyObject * argument, which a C++ caller may legitimately pass, turning
what was a recoverable director error into a segfault. SWIG_Py_XINCREF
also honours the stable ABI, where it expands to Py_IncRef, and matches
the macros used elsewhere in pyclasses.swg.
Declare the typemap before the %apply directives in pyclasses.swg. %apply
copies the typemaps that exist at the point it appears, so a directorin
typemap declared after them was never propagated to swig::SwigPtr_PyObject
or swig::SwigVar_PyObject, which suffered from the same underflow. Passing
a swig::SwigPtr_PyObject by value was worse, as the wrapper variable stole
the reference held by the argument.
Rename the test to python_director_pyobject following the naming used for
the other Python only tests, and extend it to cover swig::SwigPtr_PyObject,
swig::SwigVar_PyObject and a null argument. Remove the unused variable and
the no-op if from the runme file, check the reference count after the
garbage collection loop and drop the success message, as the tests are
silent when they pass.
Add the CHANGES.current entry.
Assisted-by: Claude Code (Opus 5)