diff --git a/CHANGES.current b/CHANGES.current index e26366a74..6d7b0ca46 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -7,6 +7,15 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/ Version 4.0.0 (in progress) =========================== +2019-02-18: jakecobb + [Python] #945 #1234 Elements in std::vector memory access fix. + + Accessing an element in a std::vector obtains a reference to the element via an + iterator pointing to the element in the container. If the vector is garbage collected, + the SWIG wrapper containing the pointer to the element becomes invalid. The fix is + to obtain a back-reference to the container by the wrapper to the element in the Python + layer to prevent the garbage collector from destroying the underlying container. + 2019-02-17: wsfulton Fix typemap matching to expand template parameters when the name contains template parameters. In the %typemap below the type is T and the name is X::make diff --git a/Doc/Manual/CSharp.html b/Doc/Manual/CSharp.html index a4e0be799..7a9b7470b 100644 --- a/Doc/Manual/CSharp.html +++ b/Doc/Manual/CSharp.html @@ -1828,7 +1828,7 @@ Consider the following C++ code:
 struct Wheel {
   int size;
-  Wheel(int sz) : size(sz) {}
+  Wheel(int sz = 0) : size(sz) {}
 };
 
 class Bike {
diff --git a/Doc/Manual/Extending.html b/Doc/Manual/Extending.html
index 1deb1cb12..b01328380 100644
--- a/Doc/Manual/Extending.html
+++ b/Doc/Manual/Extending.html
@@ -3711,7 +3711,7 @@ Below are some practical steps that should help meet these requirements.
   
   
  • Copying an existing language module and adapting the source for it is likely to be the most efficient - approach to fully developing a new module as a numbe of corner cases are covered in the existing implementations. + approach to fully developing a new module as a number of corner cases are covered in the existing implementations. The most advanced scripting languages are Python and Ruby. The most advanced compiled target languages are Java and C#.
  • diff --git a/Doc/Manual/Java.html b/Doc/Manual/Java.html index 4c7b6d058..bf77c1562 100644 --- a/Doc/Manual/Java.html +++ b/Doc/Manual/Java.html @@ -8295,7 +8295,7 @@ Consider the following C++ code:
     struct Wheel {
       int size;
    -  Wheel(int sz) : size(sz) {}
    +  Wheel(int sz = 0) : size(sz) {}
     };
     
     class Bike {
    diff --git a/Doc/Manual/Python.html b/Doc/Manual/Python.html
    index 20e95775f..6a174fddf 100644
    --- a/Doc/Manual/Python.html
    +++ b/Doc/Manual/Python.html
    @@ -5452,7 +5452,7 @@ Consider the following C++ code:
     #include <iostream>
     struct Wheel {
       int size;
    -  Wheel(int sz) : size(sz) {}
    +  Wheel(int sz = 0) : size(sz) {}
       ~Wheel() { std::cout << "~Wheel" << std::endl; }
     };
     
    diff --git a/Doc/Manual/SWIGPlus.html b/Doc/Manual/SWIGPlus.html
    index a6cb93f63..8ad9daedf 100644
    --- a/Doc/Manual/SWIGPlus.html
    +++ b/Doc/Manual/SWIGPlus.html
    @@ -4616,7 +4616,7 @@ except Error, e:
     
     

    Details of how to tailor code for handling the caught C++ exception and converting it into the target language's exception/error handling mechanism -is outlined in the "throws" typemap section. +is outlined in the "throws" typemap section.

    diff --git a/Examples/ocaml/argout_ref/example.i b/Examples/ocaml/argout_ref/example.i index 472a83804..a3e6bf8a6 100644 --- a/Examples/ocaml/argout_ref/example.i +++ b/Examples/ocaml/argout_ref/example.i @@ -1,13 +1,13 @@ /* File : example.i */ %module example -%typemap(argout) (int &x, int &y) { +%typemap(argout) (const int &x, const int &y) { swig_result = caml_list_append(swig_result, caml_val_int(*$1)); swig_result = caml_list_append(swig_result, caml_val_int(*$2)); } %{ -extern "C" void factor(int &x, int &y); +extern "C" void factor(const int &x, const int &y); %} -extern "C" void factor(int &x, int &y); +extern "C" void factor(const int &x, const int &y); diff --git a/Examples/test-suite/common.mk b/Examples/test-suite/common.mk index 11c537cbb..5a72ba730 100644 --- a/Examples/test-suite/common.mk +++ b/Examples/test-suite/common.mk @@ -647,6 +647,7 @@ CPP_STD_TEST_CASES += \ li_std_pair_using \ li_std_string \ li_std_vector \ + li_std_vector_back_reference \ li_std_vector_enum \ li_std_vector_member_var\ li_std_vector_ptr \ diff --git a/Examples/test-suite/li_std_vector_back_reference.i b/Examples/test-suite/li_std_vector_back_reference.i new file mode 100644 index 000000000..be41f4740 --- /dev/null +++ b/Examples/test-suite/li_std_vector_back_reference.i @@ -0,0 +1,14 @@ +%module li_std_vector_back_reference + +%include + +%inline %{ +// #include +struct Wheel { + int size; + Wheel(int sz = 0) : size(sz) {} +// ~Wheel() { std::cout << "~Wheel" << std::endl; } +}; +%} + +%template(VectorWheel) std::vector; diff --git a/Examples/test-suite/ocaml/extend_placement_runme.ml b/Examples/test-suite/ocaml/extend_placement_runme.ml new file mode 100644 index 000000000..31d9ae671 --- /dev/null +++ b/Examples/test-suite/ocaml/extend_placement_runme.ml @@ -0,0 +1,53 @@ +open Swig +open Extend_placement + +let _ = + let f = new_Foo '() in + assert (f -> spam () as int = 1); + assert (new_Foo '(1) -> spam () as int = 1); + let f = new_Foo '(1, 1) in + assert (f -> spam () as int = 1); + assert (f -> spam ("hello") as int = 2); + assert (f -> spam (1) as int = 1); + assert (f -> spam (1, 2) as int = 3); + assert (f -> spam (2, 4, 6) as int = 6); + assert (f -> spam (f) as int = 0); + let arg = C_double 1. in + assert (f -> spam (f, arg) as int = 0); + + assert (new_Bar '() -> spam () as int = 1); + let b = new_Bar '(1) in + assert (b -> spam () as int = 1); + assert (b -> spam ("hello") as int = 2); + assert (b -> spam (1) as int = 1); + assert (b -> spam (1, 2) as int = 3); + assert (b -> spam (2, 4, 6) as int = 6); + assert (b -> spam (b) as int = 0); + let arg = C_double 1. in + assert (b -> spam (b, arg) as int = 0); + + assert (new_FooTi '() -> spam () as int = 1); + assert (new_FooTi '(1) -> spam () as int = 1); + let f = new_FooTi '(1, 1) in + assert (f -> spam () as int = 1); + assert (f -> spam ("hello") as int = 2); + assert (f -> spam (1) as int = 1); + assert (f -> spam (1, 2) as int = 3); + assert (f -> spam (2, 4, 6) as int = 6); + let foo = new_Foo '() in + assert (f -> spam (foo) as int = 0); + let arg = C_double 1. in + assert (f -> spam (foo, arg) as int = 0); + + assert (new_BarTi '() -> spam () as int = 1); + let b = new_BarTi '(1) in + assert (b -> spam () as int = 1); + assert (b -> spam ("hello") as int = 2); + assert (b -> spam (1) as int = 1); + assert (b -> spam (1, 2) as int = 3); + assert (b -> spam (2, 4, 6) as int = 6); + let bar = new_Bar '() in + assert (b -> spam (bar) as int = 0); + let arg = C_double 1. in + assert (b -> spam (bar, arg) as int = 0); +;; diff --git a/Examples/test-suite/ocaml/global_vars_runme.ml b/Examples/test-suite/ocaml/global_vars_runme.ml new file mode 100644 index 000000000..75df89499 --- /dev/null +++ b/Examples/test-suite/ocaml/global_vars_runme.ml @@ -0,0 +1,15 @@ +open Swig +open Global_vars + +_init '() + +let _ = + assert (_b '() as string = "string b"); + assert (_b '("a string value") as string = "a string value"); + assert (_b '() as string = "a string value"); + assert (_x '() as int = 1234); + assert (_x '(9876) as int = 9876); + assert (_x '() as int = 9876); + assert (_Hi '() as int = 0); + assert (_Hola '() as int = 1); +;; diff --git a/Examples/test-suite/ocaml/overload_template_runme.ml b/Examples/test-suite/ocaml/overload_template_runme.ml new file mode 100644 index 000000000..42a4a397a --- /dev/null +++ b/Examples/test-suite/ocaml/overload_template_runme.ml @@ -0,0 +1,60 @@ +open Swig +open Overload_template + +let _ = + assert (_foo '() as int = 3); + assert (_maximum '(3, 4) as int = 4); + assert (_maximum '(3.4, 5.2) as float > 5.); + assert (_mix1 '("hi") as int = 101); + assert (_mix1 '(1.0, 1.0) as int = 102); + assert (_mix1 '(1.0) as int = 103); + assert (_mix2 '("hi") as int = 101); + assert (_mix2 '(1.0, 1.0) as int = 102); + assert (_mix2 '(1.0) as int = 103); + assert (_mix3 '("hi") as int = 101); + assert (_mix3 '(1.0, 1.0) as int = 102); + assert (_mix3 '(1.0) as int = 103); + + assert (_overtparams1 '(100) as int = 10); + assert (_overtparams1 '(100.0, 100) as int = 20); + assert (_overtparams2 '(100.0, 100) as int = 40); + assert (_overloaded '() as int = 60); + assert (_overloaded '(100.0, 100) as int = 70); + assert (_overloadedagain '("hello") as int = 80); + assert (_overloadedagain '() as int = 90); + + assert (_specialization '(10) as int = 202); + assert (_specialization '(10.0) as int = 203); + assert (_specialization '(10, 10) as int = 204); + assert (_specialization '(10.0, 10.0) as int = 205); + assert (_specialization '("hi", "hi") as int = 201); + + assert (_xyz '() = C_void); + assert (_xyz_int '() = C_void); + assert (_xyz_double '() = C_void); + + assert (_overload '("hi") as int = 0); + assert (_overload '(1) as int = 10); + assert (_overload '(1, 1) as int = 20); + assert (_overload '(1, "hello") as int = 30); + let k = new_Klass '() in + assert (_overload '(k) as int = 10); + assert (_overload '(k, k) as int = 20); + assert (_overload '(k, "hello") as int = 30); + assert (_overload '(10.0, "hi") as int = 40); + assert (_overload '() as int = 50); + + assert (_nsoverload '("hi") as int = 1000); + assert (_nsoverload '(1) as int = 1010); + assert (_nsoverload '(1, 1) as int = 1020); + assert (_nsoverload '(1, "hello") as int = 1030); + assert (_nsoverload '(k) as int = 1010); + assert (_nsoverload '(k, k) as int = 1020); + assert (_nsoverload '(k, "hello") as int = 1030); + assert (_nsoverload '(10.0, "hi") as int = 1040); + assert (_nsoverload '() as int = 1050); + + assert (_A_foo '(1) = C_void); + let b = new_B '() in + assert (b -> foo(1) = C_void); +;; diff --git a/Examples/test-suite/ocaml/reference_global_vars_runme.ml b/Examples/test-suite/ocaml/reference_global_vars_runme.ml index aa1708774..adde1b82e 100644 --- a/Examples/test-suite/ocaml/reference_global_vars_runme.ml +++ b/Examples/test-suite/ocaml/reference_global_vars_runme.ml @@ -22,7 +22,7 @@ let _ = let _ = _var_short (_createref_short (C_short 10)) in assert (_value_short (_var_short '()) as int = 10); - + let _ = _var_unsigned_short (_createref_unsigned_short (C_ushort 10)) in assert (_value_unsigned_short (_var_unsigned_short '()) as int = 10); diff --git a/Examples/test-suite/ocaml/sizet_runme.ml b/Examples/test-suite/ocaml/sizet_runme.ml new file mode 100644 index 000000000..5f72459c9 --- /dev/null +++ b/Examples/test-suite/ocaml/sizet_runme.ml @@ -0,0 +1,10 @@ +open Swig +open Sizet + +let _ = + let s = C_int64 2000L in + assert (_test1 '(s) as int = 2000); + assert (_test2 '(s) as int = 2000); + assert (_test3 '(s) as int = 2000); + assert (_test4 '(s) as int = 2000); +;; diff --git a/Examples/test-suite/ocaml/template_default_arg_overloaded_extend_runme.ml b/Examples/test-suite/ocaml/template_default_arg_overloaded_extend_runme.ml new file mode 100644 index 000000000..27fb9b543 --- /dev/null +++ b/Examples/test-suite/ocaml/template_default_arg_overloaded_extend_runme.ml @@ -0,0 +1,17 @@ +open Swig +open Template_default_arg_overloaded_extend + +let _ = + let rs = new_ResultSet '() and sp = new_SearchPoint '() in + assert (rs -> go_get_method (0, sp) as int = -1); + assert (rs -> go_get_method (0, sp, 100) as int = 100); + assert (rs -> go_get_template (0, sp) as int = -2); + assert (rs -> go_get_template (0, sp, 100) as int = 100); + + assert (rs -> over () as string = "over(int)"); + assert (rs -> over (10) as string = "over(int)"); + assert (rs -> over (sp) as string = "over(giai2::SearchPoint, int)"); + assert (rs -> over (sp, 10) as string = "over(giai2::SearchPoint, int)"); + assert (rs -> over (true, sp) as string = "over(bool, gaia2::SearchPoint, int)"); + assert (rs -> over (true, sp, 10) as string = "over(bool, gaia2::SearchPoint, int)"); +;; diff --git a/Examples/test-suite/ocaml/template_default_arg_runme.ml b/Examples/test-suite/ocaml/template_default_arg_runme.ml new file mode 100644 index 000000000..94f2291f1 --- /dev/null +++ b/Examples/test-suite/ocaml/template_default_arg_runme.ml @@ -0,0 +1,52 @@ +open Swig +open Template_default_arg + +let _ = + let helloInt = new_Hello_int '() and enumArg = _hi '() in + assert (helloInt -> foo (enumArg) = C_void); + assert (helloInt -> foo () = C_void); + + let x = new_X_int '() in + assert (x -> meth (20.0, 200) as int = 200); + assert (x -> meth (20) as int = 20); + assert (x -> meth () as int = 0); + + let x = new_Y_unsigned '() in + let args = C_list [ C_double 20.0 ; C_uint 200l ] in + assert (x -> meth (args) as int = 200); + let args = C_uint 20l in + assert (x -> meth (args) as int = 20); + assert (x -> meth () as int = 0); + + let x = new_X_longlong '() in + assert (x -> meth (20.0) as int = 0); + let x = new_X_longlong '(20.0) in + assert (x -> meth (20.0) as int = 0); + let args = C_list [ C_double 20.0 ; C_int64 200L ] in + let x = new_X_longlong '(args) in + assert (x -> meth (20.0) as int = 0); + + let x = new_X_int '() in + assert (x -> meth (20.0) as int = 0); + let x = new_X_int '(20.0) in + assert (x -> meth (20.0) as int = 0); + let x = new_X_int '(20.0, 200) in + assert (x -> meth (20.0) as int = 0); + + let arg = new_Foo_int '() in + assert (_ott '(arg) as int = 30); + assert (_ott '() as int = 10); + assert (_ott '(1) as int = 10); + assert (_ott '(1, 1) as int = 10); + assert (_ott '("hi") as int = 20); + assert (_ott '("hi", 1) as int = 20); + assert (_ott '("hi", 1, 1) as int = 20); + + let arg = new_Hello_int '() in + assert (_ottstring '(arg, "hi") as int = 40); + assert (_ottstring '(arg) as int = 40); + assert (_ottint '(arg, 1) as int = 50); + assert (_ottint '(arg) as int = 50); + assert (_ott '(arg, 1.0) as int = 60); + assert (_ott '(arg) as int = 60); +;; diff --git a/Examples/test-suite/ocaml/typedef_reference_runme.ml b/Examples/test-suite/ocaml/typedef_reference_runme.ml new file mode 100644 index 000000000..4c9cc6fca --- /dev/null +++ b/Examples/test-suite/ocaml/typedef_reference_runme.ml @@ -0,0 +1,11 @@ +open Swig +open Typedef_reference + +let _ = + let i = _copy_intp '(2) in + assert (_somefunc '(i) as int = 2); + assert (_delete_intp '(i) = C_void); + let i = _copy_intp '(3) in + assert (_otherfunc '(i) as int = 3); + assert (_delete_intp '(i) = C_void); +;; diff --git a/Examples/test-suite/ocaml/wrapmacro_runme.ml b/Examples/test-suite/ocaml/wrapmacro_runme.ml new file mode 100644 index 000000000..f11136360 --- /dev/null +++ b/Examples/test-suite/ocaml/wrapmacro_runme.ml @@ -0,0 +1,10 @@ +open Swig +open Wrapmacro + +let _ = + let args = C_list [ C_int64 2L ; C_int64 1L ] in + assert (_maximum '(args) as int = 2); + let args = C_list [ C_double (2. /. 7.) ; C_double 256. ] in + assert (_maximum '(args) as float = 256.); + assert (_GUINT16_SWAP_LE_BE_CONSTANT '(0x1234) as int = 0x3412); +;; diff --git a/Examples/test-suite/python/li_std_vector_back_reference_runme.py b/Examples/test-suite/python/li_std_vector_back_reference_runme.py new file mode 100644 index 000000000..cec9e8cc4 --- /dev/null +++ b/Examples/test-suite/python/li_std_vector_back_reference_runme.py @@ -0,0 +1,10 @@ +from li_std_vector_back_reference import * + +def first_element(): + v = VectorWheel((Wheel(11), Wheel(22))) + # v will be deleted after exit from this method + return v[0] + +size = first_element().size +if size != 11: + raise RuntimeError("Back reference not working {}".format(size)) diff --git a/Lib/ocaml/ocaml.swg b/Lib/ocaml/ocaml.swg index 2da3fb769..3d552cc50 100644 --- a/Lib/ocaml/ocaml.swg +++ b/Lib/ocaml/ocaml.swg @@ -62,7 +62,7 @@ extern "C" { SWIG_TypeCheckStruct(source_type, dest_type ); #ifdef TYPE_CAST_VERBOSE fprintf( stderr, "Typecheck -> %s\n", - tc ? tc->str : "" ); + tc ? tc->type->str : "" ); #endif if( tc ) { int newmemory = 0; diff --git a/Lib/ocaml/std_common.i b/Lib/ocaml/std_common.i index 6523af0b5..7e64607d9 100644 --- a/Lib/ocaml/std_common.i +++ b/Lib/ocaml/std_common.i @@ -7,6 +7,7 @@ %include %apply size_t { std::size_t }; +%apply const size_t& { const std::size_t& }; %{ #include diff --git a/Lib/ocaml/std_string.i b/Lib/ocaml/std_string.i index 0ea9b4e2d..712c3bb73 100644 --- a/Lib/ocaml/std_string.i +++ b/Lib/ocaml/std_string.i @@ -86,6 +86,7 @@ class wstring; %typemap(out) string * { $result = caml_val_string_len((*$1).c_str(),(*$1).size()); } +%typemap(typecheck) string, const string & = char *; } #ifdef ENABLE_CHARPTR_ARRAY diff --git a/Lib/ocaml/typecheck.i b/Lib/ocaml/typecheck.i index 538b694d3..74d2727e1 100644 --- a/Lib/ocaml/typecheck.i +++ b/Lib/ocaml/typecheck.i @@ -72,7 +72,8 @@ long, signed long, unsigned long, long long, signed long long, unsigned long long, const long &, const signed long &, const unsigned long &, - const long long &, const signed long long &, const unsigned long long & + const long long &, const signed long long &, const unsigned long long &, + size_t, const size_t & { if( !Is_block($input) ) $1 = 0; else { @@ -135,24 +136,42 @@ } %typecheck(SWIG_TYPECHECK_POINTER) SWIGTYPE *, SWIGTYPE &, SWIGTYPE &&, SWIGTYPE [] { - void *ptr; - $1 = !caml_ptr_val_internal($input, &ptr,$descriptor); + if (!Is_block($input) || !(SWIG_Tag_val($input) == C_obj || SWIG_Tag_val($input) == C_ptr)) { + $1 = 0; + } else { + void *ptr; + $1 = !caml_ptr_val_internal($input, &ptr, $descriptor); + } } -#if 0 - %typecheck(SWIG_TYPECHECK_POINTER) SWIGTYPE { - void *ptr; - $1 = !caml_ptr_val_internal($input, &ptr, $&1_descriptor); + swig_type_info *typeinfo; + if (!Is_block($input)) { + $1 = 0; + } else { + switch (SWIG_Tag_val($input)) { + case C_obj: { + void *ptr; + $1 = !caml_ptr_val_internal($input, &ptr, $&1_descriptor); + break; + } + case C_ptr: { + typeinfo = (swig_type_info *)SWIG_Int64_val(SWIG_Field($input, 1)); + $1 = SWIG_TypeCheck("$1_type", typeinfo) != NULL; + break; + } + default: $1 = 0; break; + } + } } -#endif - %typecheck(SWIG_TYPECHECK_VOIDPTR) void * { void *ptr; $1 = !caml_ptr_val_internal($input, &ptr, 0); } +%typecheck(SWIG_TYPECHECK_SWIGOBJECT) CAML_VALUE "$1 = 1;" + %define INPUT_OUTPUT_INOUT_TYPEMAPS(type, c_to_ocaml, ocaml_to_c) %typemap(in) type *INPUT(type temp), type &INPUT(type temp) { temp = (type)ocaml_to_c($input); diff --git a/Lib/ocaml/typemaps.i b/Lib/ocaml/typemaps.i index 916b189d0..08a0c97c9 100644 --- a/Lib/ocaml/typemaps.i +++ b/Lib/ocaml/typemaps.i @@ -132,11 +132,11 @@ %typemap(varin) C_NAME { $1 = OCAML_TO_C($input); } -%typemap(in) C_NAME & ($*1_ltype temp) { +%typemap(in) const C_NAME & ($*1_ltype temp) { temp = ($*1_ltype) OCAML_TO_C($input); $1 = &temp; } -%typemap(varin) C_NAME & { +%typemap(varin) const C_NAME & { $1 = OCAML_TO_C($input); } %typemap(directorout) C_NAME { @@ -149,10 +149,10 @@ %typemap(varout) C_NAME { $result = C_TO_OCAML($1); } -%typemap(varout) C_NAME & { +%typemap(varout) const C_NAME & { $result = C_TO_OCAML($1); } -%typemap(out) C_NAME & { +%typemap(out) const C_NAME & { $result = C_TO_OCAML(*$1); } %typemap(directorin) C_NAME { diff --git a/Lib/python/pycontainer.swg b/Lib/python/pycontainer.swg index 36557d75e..5c2a981ee 100644 --- a/Lib/python/pycontainer.swg +++ b/Lib/python/pycontainer.swg @@ -36,6 +36,48 @@ %include +%fragment("container_owner_attribute_init", "init") { + // thread safe initialization + swig::container_owner_attribute(); +} + +%fragment("reference_container_owner", "header", fragment="container_owner_attribute_init") { +namespace swig { + PyObject* container_owner_attribute() { + static PyObject* attr = SWIG_Python_str_FromChar("__swig_container"); + return attr; + } + + template + struct container_owner { + // By default, do not add the back-reference (for value types) + // Specialization below will check the reference for pointer types. + static bool back_reference(PyObject* child, PyObject* owner) { + return false; + } + }; + + template <> + struct container_owner { + /* + * Call to add a back-reference to the owning object when returning a + * reference from a container. Will only set the reference if child + * is a SWIG wrapper object that does not own the pointer. + * + * returns whether the reference was set or not + */ + static bool back_reference(PyObject* child, PyObject* owner) { + SwigPyObject* swigThis = SWIG_Python_GetSwigThis(child); + if (swigThis && (swigThis->own & SWIG_POINTER_OWN) != SWIG_POINTER_OWN) { + PyObject_SetAttr(child, container_owner_attribute(), owner); + return true; + } + return false; + } + }; +} +} + %fragment(SWIG_Traits_frag(swig::SwigPtr_PyObject),"header",fragment="StdTraits") { namespace swig { template <> struct traits { @@ -766,6 +808,12 @@ namespace swig size_type __len__() const { return self->size(); } + + // Although __getitem__, front, back actually use a const value_type& return type, the typemaps below + // use non-const so that they can be easily overridden by users if necessary. + %typemap(ret, fragment="reference_container_owner", noblock=1) value_type& __getitem__, value_type& front, value_type& back { + (void)swig::container_owner::category>::back_reference($result, $self); + } } %enddef