diff options
author | Olly Betts <olly@survex.com> | 2022-10-13 18:27:48 +1300 |
---|---|---|
committer | Olly Betts <ojwbetts@gmail.com> | 2022-10-14 14:44:19 +1300 |
commit | 9ab9c716239d2855bdd6c0771e2e980c0767fb57 (patch) | |
tree | d7cba30e8043b14c866cde5b51e7623b27aabdc0 | |
parent | 3dd7e93c77c0685e043da92d135a22b39a350484 (diff) | |
download | swig-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.current | 5 | ||||
-rw-r--r-- | Examples/test-suite/exception_memory_leak.i | 12 | ||||
-rw-r--r-- | Examples/test-suite/lua/exception_memory_leak_runme.lua | 29 | ||||
-rw-r--r-- | Source/Modules/lua.cxx | 16 |
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 */ |