summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOlly Betts <olly@survex.com>2022-10-13 18:27:48 +1300
committerOlly Betts <ojwbetts@gmail.com>2022-10-14 14:44:19 +1300
commit9ab9c716239d2855bdd6c0771e2e980c0767fb57 (patch)
treed7cba30e8043b14c866cde5b51e7623b27aabdc0
parent3dd7e93c77c0685e043da92d135a22b39a350484 (diff)
downloadswig-9ab9c716239d2855bdd6c0771e2e980c0767fb57.tar.gz
[lua] Run destructors of local C++ objects on SWIG_fail
Arrange that destructors of local C++ objects in the wrapper function get run on SWIG_fail (which calls lua_error() which calls longjmp()). We achieve this by putting almost everything in the function in its own block, and end that right before lua_error() at which point those destructors will get called.
-rw-r--r--CHANGES.current5
-rw-r--r--Examples/test-suite/exception_memory_leak.i12
-rw-r--r--Examples/test-suite/lua/exception_memory_leak_runme.lua29
-rw-r--r--Source/Modules/lua.cxx16
4 files changed, 58 insertions, 4 deletions
diff --git a/CHANGES.current b/CHANGES.current
index d0246156b..e10a46072 100644
--- a/CHANGES.current
+++ b/CHANGES.current
@@ -7,6 +7,11 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/
Version 4.1.0 (in progress)
===========================
+2022-10-14: olly
+ [Lua] Arrange that destructors of local C++ objects in the wrapper
+ function get run on SWIG_fail (which calls lua_error() which calls
+ longjmp()).
+
2022-10-13: wsfulton
[R] Add missing SWIGTYPE *const& typemaps for supporting pointers
by const reference.
diff --git a/Examples/test-suite/exception_memory_leak.i b/Examples/test-suite/exception_memory_leak.i
index 2589107b2..27d199c9f 100644
--- a/Examples/test-suite/exception_memory_leak.i
+++ b/Examples/test-suite/exception_memory_leak.i
@@ -22,6 +22,13 @@
}
$1 = NULL;
}
+%typemap(out) Foo trigger_internal_swig_exception
+{
+ SWIG_exception(SWIG_RuntimeError, "Let's see how the bindings manage this exception!");
+#ifdef SWIG_fail
+ SWIG_fail;
+#endif
+}
%inline %{
#include <string>
@@ -47,4 +54,9 @@
return (message == "null") ? NULL : foo;
}
+ static Foo trigger_internal_swig_exception(const std::string& message)
+ {
+ return Foo();
+ }
+
%}
diff --git a/Examples/test-suite/lua/exception_memory_leak_runme.lua b/Examples/test-suite/lua/exception_memory_leak_runme.lua
new file mode 100644
index 000000000..f3fc0f27b
--- /dev/null
+++ b/Examples/test-suite/lua/exception_memory_leak_runme.lua
@@ -0,0 +1,29 @@
+require("import") -- the import fn
+import("exception_memory_leak") -- import code
+eml=exception_memory_leak --alias
+
+-- catch "undefined" global variables
+local env = _ENV -- Lua 5.2
+if not env then env = getfenv () end -- Lua 5.1
+setmetatable(env, {__index=function (t,i) error("undefined global variable `"..i.."'",2) end})
+
+a = eml.Foo()
+assert(eml.Foo_get_count() == 1)
+b = eml.Foo()
+assert(eml.Foo_get_count() == 2)
+
+-- Normal behaviour
+eml.trigger_internal_swig_exception("no problem", a)
+assert(eml.Foo_get_count() == 2)
+assert(eml.Foo_get_freearg_count() == 1)
+
+-- SWIG exception triggered and handled (return new object case)
+ok,ex=pcall(eml.trigger_internal_swig_exception, "null", b)
+assert(ok==false)
+assert(eml.Foo_get_count() == 2)
+assert(eml.Foo_get_freearg_count() == 2)
+
+-- SWIG exception triggered and handled (return by value case).
+ok,ex=pcall(eml.trigger_internal_swig_exception, "null")
+assert(ok==false)
+assert(eml.Foo_get_count() == 2)
diff --git a/Source/Modules/lua.cxx b/Source/Modules/lua.cxx
index c2e65dba6..d3cf96b52 100644
--- a/Source/Modules/lua.cxx
+++ b/Source/Modules/lua.cxx
@@ -556,6 +556,12 @@ public:
this line adds this into the wrapper code
NEW LANGUAGE NOTE:END *********************************************** */
Printv(f->def, "static int ", wname, "(lua_State* L) {", NIL);
+ // SWIG_fail in lua leads to a call to lua_error() which calls longjmp()
+ // which means the destructors of any live function-local C++ objects won't
+ // get run. To avoid this happening, we wrap almost everything in the
+ // function in a block, and end that right before lua_error() at which
+ // point those destructors will get called.
+ if (CPlusPlus) Append(f->def, "\n{");
/* NEW LANGUAGE NOTE:***********************************************
this prints the list of args, eg for a C fn
@@ -766,10 +772,12 @@ public:
/* Close the function */
Printv(f->code, "return SWIG_arg;\n", NIL);
// add the failure cleanup code:
- Printv(f->code, "\nif(0) SWIG_fail;\n", NIL);
- Printv(f->code, "\nfail:\n", NIL);
- Printv(f->code, "$cleanup", "lua_error(L);\n", NIL);
- Printv(f->code, "return SWIG_arg;\n", NIL);
+ Printv(f->code, "\nfail: SWIGUNUSED;\n", "$cleanup", NIL);
+ if (CPlusPlus) Append(f->code, "}\n");
+ Printv(f->code, "lua_error(L);\n", NIL);
+ // lua_error() calls longjmp() but we need a dummy return to avoid compiler
+ // warnings.
+ Printv(f->code, "return 0;\n", NIL);
Printf(f->code, "}\n");
/* Substitute the cleanup code */