[PHP] Fix cleanup code handling issues

Fix to call cleanup code in exception situations and not to invoke
the freearg typemap twice in certain situations.

Fixes https://sourceforge.net/p/swig/bugs/1211/
This commit is contained in:
Olly Betts 2022-02-17 13:02:05 +13:00
commit 1707d6b89b
9 changed files with 54 additions and 16 deletions

View file

@ -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) Version 4.1.0 (in progress)
=========================== ===========================
2022-02-17: olly
[PHP] https://sourceforge.net/p/swig/bugs/1211/
Fix to call cleanup code in exception situations and not to invoke
the freearg typemap twice in certain situations.
2022-02-15: olly 2022-02-15: olly
#300 #368 Improve parser handling of % followed immediately by #300 #368 Improve parser handling of % followed immediately by
an identifier. If it's not a recognised directive the scanner an identifier. If it's not a recognised directive the scanner

View file

@ -234,6 +234,7 @@ CPP_TEST_CASES += \
evil_diamond_ns \ evil_diamond_ns \
evil_diamond_prop \ evil_diamond_prop \
exception_classname \ exception_classname \
exception_memory_leak \
exception_order \ exception_order \
extend \ extend \
extend_constructor_destructor \ extend_constructor_destructor \

View file

@ -1,4 +1,4 @@
%module r_memory_leak %module exception_memory_leak
%include <std_string.i> %include <std_string.i>
@ -8,33 +8,38 @@
} }
%typemap(freearg) Foo* foo %typemap(freearg) Foo* foo
{ {
printf(" \" Object deleted\"\n"); Foo::inc_freearg_count();
delete $1; delete $1;
} }
%typemap(out) Foo* verify_no_memory_leak %typemap(out) Foo* verify_no_memory_leak
{ {
if ($1 == NULL) if ($1 == NULL)
SWIG_exception_fail(SWIG_RuntimeError, "Let's see how the bindings manage this exception!"); SWIG_exception_fail(SWIG_RuntimeError, "Let's see how the bindings manage this exception!");
$1 = NULL;
} }
%typemap(scoerceout) Foo*
%{ if (!is.null($result) && !is.logical($result)) {$result <- new("$R_class", ref=$result) ;}; %}
%inline %{ %inline %{
#include <string> #include <string>
class Foo { class Foo {
static unsigned count; static unsigned count;
static unsigned freearg_count;
public: public:
Foo() { ++count; } Foo() { ++count; }
~Foo() { --count; } ~Foo() { --count; }
static unsigned get_count() { return count; } static unsigned get_count() { return count; }
static unsigned get_freearg_count() { return freearg_count; }
#ifndef SWIG
static void inc_freearg_count() { ++freearg_count; }
#endif
}; };
unsigned Foo::count = 0; unsigned Foo::count = 0;
unsigned Foo::freearg_count = 0;
static Foo* trigger_internal_swig_exception(const std::string& message, Foo* foo) static Foo* trigger_internal_swig_exception(const std::string& message, Foo* foo)
{ {
return (message == "null") ? NULL : foo; return (message == "null") ? NULL : foo;
}; }
%} %}

View file

@ -0,0 +1,23 @@
<?php
require "tests.php";
check::functions(array('trigger_internal_swig_exception'));
check::classes(array('Foo', 'exception_memory_leak'));
// No new vars
check::globals(array());
$a = new Foo();
check::equal(Foo::get_count(), 1, "Should have 1 Foo objects");
$b = new Foo();
check::equal(Foo::get_count(), 2, "Should have 2 Foo objects");
// Normal behaviour
trigger_internal_swig_exception("no problem", $a);
check::equal(Foo::get_count(), 2, "Should have 2 Foo objects");
check::equal(Foo::get_freearg_count(), 1, "freearg should have been used once");
// SWIG exception triggered and handled.
trigger_internal_swig_exception("null", $b);
check::equal(Foo::get_count(), 2, "Should have 2 Foo objects");
check::equal(Foo::get_freearg_count(), 2, "freearg should have been used twice");

View file

@ -18,7 +18,6 @@ C_TEST_CASES += \
CPP_TEST_CASES += \ CPP_TEST_CASES += \
r_double_delete \ r_double_delete \
r_memory_leak \
r_overload_array \ r_overload_array \
r_sexp \ r_sexp \
r_overload_comma \ r_overload_comma \

View file

@ -1,8 +1,8 @@
clargs <- commandArgs(trailing=TRUE) clargs <- commandArgs(trailing=TRUE)
source(file.path(clargs[1], "unittest.R")) source(file.path(clargs[1], "unittest.R"))
dyn.load(paste("r_memory_leak", .Platform$dynlib.ext, sep="")) dyn.load(paste("exception_memory_leak", .Platform$dynlib.ext, sep=""))
source("r_memory_leak.R") source("exception_memory_leak.R")
cacheMetaData(1) cacheMetaData(1)
a <- Foo(); a <- Foo();
@ -13,6 +13,7 @@ unittest(Foo_get_count(), 2);
# Normal behaviour # Normal behaviour
invisible(trigger_internal_swig_exception("no problem", a)); invisible(trigger_internal_swig_exception("no problem", a));
unittest(Foo_get_count(), 2); unittest(Foo_get_count(), 2);
unittest(Foo_get_freearg_count(), 1);
# SWIG exception introduced # SWIG exception introduced
result <- tryCatch({ result <- tryCatch({
trigger_internal_swig_exception("null", b); trigger_internal_swig_exception("null", b);
@ -24,3 +25,4 @@ result <- tryCatch({
unittest(1,1); unittest(1,1);
}) })
unittest(Foo_get_count(), 2); unittest(Foo_get_count(), 2);
unittest(Foo_get_freearg_count(), 2);

View file

@ -191,7 +191,7 @@
if (!(Z_ISREF($input) && Z_ISNULL_P(Z_REFVAL($input)))) { if (!(Z_ISREF($input) && Z_ISNULL_P(Z_REFVAL($input)))) {
/* wasn't a pre/ref/thing, OR anything like an int thing */ /* wasn't a pre/ref/thing, OR anything like an int thing */
zend_throw_exception(zend_ce_type_error, "Type error in argument $arg of $symname", 0); zend_throw_exception(zend_ce_type_error, "Type error in argument $arg of $symname", 0);
return; goto fail;
} }
} }
force=0; force=0;
@ -555,18 +555,18 @@
unsigned long, unsigned long,
unsigned short %{ unsigned short %{
zend_throw_exception(NULL, "C++ $1_type exception thrown", $1); zend_throw_exception(NULL, "C++ $1_type exception thrown", $1);
return; goto fail;
%} %}
%typemap(throws) SWIGTYPE, SWIGTYPE &, SWIGTYPE &&, SWIGTYPE *, SWIGTYPE [], SWIGTYPE [ANY] %{ %typemap(throws) SWIGTYPE, SWIGTYPE &, SWIGTYPE &&, SWIGTYPE *, SWIGTYPE [], SWIGTYPE [ANY] %{
(void)$1; (void)$1;
zend_throw_exception(NULL, "C++ $1_type exception thrown", 0); zend_throw_exception(NULL, "C++ $1_type exception thrown", 0);
return; goto fail;
%} %}
%typemap(throws) char * %{ %typemap(throws) char * %{
zend_throw_exception(NULL, $1, 0); zend_throw_exception(NULL, $1, 0);
return; goto fail;
%} %}
/* Array reference typemaps */ /* Array reference typemaps */

View file

@ -51,7 +51,7 @@ namespace std {
%typemap(throws) string, const string& %{ %typemap(throws) string, const string& %{
zend_throw_exception(NULL, $1.c_str(), 0); zend_throw_exception(NULL, $1.c_str(), 0);
return; goto fail;
%} %}
%typemap(in, phptype="string") const string & ($*1_ltype temp) %{ %typemap(in, phptype="string") const string & ($*1_ltype temp) %{

View file

@ -1031,6 +1031,7 @@ public:
Printf(f->code, "\tWRONG_PARAM_COUNT;\n}\n\n"); Printf(f->code, "\tWRONG_PARAM_COUNT;\n}\n\n");
Printf(f->code, " if(!arg) {\n"); Printf(f->code, " if(!arg) {\n");
Printf(f->code, " zend_throw_exception(zend_ce_type_error, \"this pointer is NULL\", 0);\n"); Printf(f->code, " zend_throw_exception(zend_ce_type_error, \"this pointer is NULL\", 0);\n");
Printf(f->code, " return;\n");
Printf(f->code, " }\n"); Printf(f->code, " }\n");
Printf(f->code, " arg2 = Z_STR(args[0]);\n\n"); Printf(f->code, " arg2 = Z_STR(args[0]);\n\n");
@ -1405,7 +1406,7 @@ public:
Printv(f->code, outarg, NIL); Printv(f->code, outarg, NIL);
} }
if (cleanup) { if (static_setter && cleanup) {
Printv(f->code, cleanup, NIL); Printv(f->code, cleanup, NIL);
} }
@ -1698,8 +1699,10 @@ public:
"#ifndef SWIG_PHP_INTERFACE_", interface, "_CE\n", "#ifndef SWIG_PHP_INTERFACE_", interface, "_CE\n",
" {\n", " {\n",
" zend_class_entry *swig_interface_ce = zend_lookup_class(zend_string_init(\"", interface, "\", sizeof(\"", interface, "\") - 1, 0));\n", " zend_class_entry *swig_interface_ce = zend_lookup_class(zend_string_init(\"", interface, "\", sizeof(\"", interface, "\") - 1, 0));\n",
" if (!swig_interface_ce) zend_throw_exception(zend_ce_error, \"Interface \\\"", interface, "\\\" not found\", 0);\n", " if (swig_interface_ce)\n",
" zend_do_implement_interface(SWIG_Php_ce_", class_name, ", swig_interface_ce);\n", " zend_do_implement_interface(SWIG_Php_ce_", class_name, ", swig_interface_ce);\n",
" else\n",
" zend_throw_exception(zend_ce_error, \"Interface \\\"", interface, "\\\" not found\", 0);\n",
" }\n", " }\n",
"#endif\n", "#endif\n",
NIL); NIL);