Skip to content

Commit

Permalink
[Go] Make sure that arguments for which use memcpy when calling C are
Browse files Browse the repository at this point in the history
still live after the call.  This ensures that they will not be
collected if the GC runs during the call.
  • Loading branch information
ianlancetaylor committed May 4, 2015
1 parent 1407820 commit 48263f4
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 33 deletions.
29 changes: 17 additions & 12 deletions Examples/test-suite/go_director_inout.i
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,14 @@ type GoRetStruct struct {
$result.str.assign($input.p, $input.n);
%}

%typemap(out) RetStruct
%typemap(out,fragment="AllocateString") RetStruct
%{
$result = _swig_makegostring($1.str.data(), $1.str.length());
$result = Swig_AllocateString($1.str.data(), $1.str.length());
%}

%typemap(goout) RetStruct
%typemap(goout,fragment="CopyString") RetStruct
%{
$result = GoRetStruct{Str: $input}
$result = GoRetStruct{Str: swigCopyString($input)}
%}

%typemap(godirectorout) RetStruct
Expand Down Expand Up @@ -81,21 +81,26 @@ type GoRetStruct struct {
}
%}

%typemap(directorin) MyStruct
%typemap(directorin,fragment="AllocateString") MyStruct
%{
$input = _swig_makegostring($1.str.data(), $1.str.length());
$input = Swig_AllocateString($1.str.data(), $1.str.length());
%}

%typemap(out) MyStruct
%typemap(godirectorin,fragment="CopyString") MyStruct
%{
$result = _swig_makegostring($1.str.data(), $1.str.length());
if err := json.Unmarshal([]byte(swigCopyString($input)), &$result); err != nil {
panic(err)
}
%}

%typemap(godirectorin) MyStruct
%typemap(out,fragment="AllocateString") MyStruct
%{
if err := json.Unmarshal([]byte($input), &$result); err != nil {
panic(err)
}
$result = Swig_AllocateString($1.str.data(), $1.str.length());
%}

%typemap(goout,fragment="CopyString") MyStruct
%{
$result = swigCopyString($input)
%}

%typemap(in) MyStruct
Expand Down
16 changes: 8 additions & 8 deletions Examples/test-suite/go_inout.i
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,14 @@ type In json.Marshaler

%typemap(imtype) RetStruct "string"

%typemap(out) RetStruct
%typemap(out,fragment="AllocateString") RetStruct
%{
$result = _swig_makegostring($1.str.data(), $1.str.length());
$result = Swig_AllocateString($1.str.data(), $1.str.length());
%}

%typemap(goout) RetStruct
%typemap(goout,fragment="CopyString") RetStruct
%{
if err := json.Unmarshal([]byte($1), &$result); err != nil {
if err := json.Unmarshal([]byte(swigCopyString($1)), &$result); err != nil {
panic(err)
}
%}
Expand Down Expand Up @@ -146,7 +146,7 @@ static void putuint64(std::string *s, size_t off, uint64_t v) {
%}

// Pack the vector into a string.
%typemap(argout) MyArray*
%typemap(argout,fragment="AllocateString") MyArray*
%{
{
size_t tot = 8;
Expand All @@ -164,15 +164,15 @@ static void putuint64(std::string *s, size_t off, uint64_t v) {
str.replace(off, p->size(), *p);
off += p->size();
}
*$input = _swig_makegostring(str.data(), str.size());
*$input = Swig_AllocateString(str.data(), str.size());
}
%}

// Unpack the string into a []string.
%typemap(goargout) MyArray*
%typemap(goargout,fragment="CopyString") MyArray*
%{
{
str := *$input
str := swigCopyString(*$input)
bin := binary.LittleEndian
size := bin.Uint64([]byte(str[:8]))
str = str[8:]
Expand Down
36 changes: 24 additions & 12 deletions Lib/go/goruntime.swg
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,22 @@ static char *_swig_topofstack() {
}
}

static void _swig_gopanic(const char *p) {
struct {
const char *p;
} a;
a.p = p;
crosscall2(_cgo_panic, &a, (int) sizeof a);
}

%}

#if !SWIGGO_CGO

/* This is here for backward compatibility, but it will not work
with Go 1.5 or later. Do not use it in new code. */
%insert(runtime) %{

static void *_swig_goallocate(size_t len) {
struct {
size_t len;
Expand All @@ -125,16 +139,10 @@ static void *_swig_goallocate(size_t len) {
return a.ret;
}

static void _swig_gopanic(const char *p) {
struct {
const char *p;
} a;
a.p = p;
crosscall2(_cgo_panic, &a, (int) sizeof a);
}

%}

#endif

#if !SWIGGO_CGO

/* Boilerplate for C code when using 6g/8g. This code is compiled
Expand Down Expand Up @@ -246,6 +254,8 @@ void SwigCgocallBackDone() {

#endif

#if !SWIGGO_CGO

%insert(runtime) %{

/* This is here for backward compatibility, but it will not work
Expand All @@ -258,6 +268,12 @@ static _gostring_ _swig_makegostring(const char *p, size_t l) {
return ret;
}

%}

#endif

%insert(runtime) %{

#define SWIG_contract_assert(expr, msg) \
if (!(expr)) { _swig_gopanic(msg); } else
%}
Expand Down Expand Up @@ -290,8 +306,6 @@ type _ unsafe.Pointer

%}

#if !SWIGGO_CGO

/* Swig_always_false is used to conditionally assign parameters to
Swig_escape_val so that the compiler thinks that they escape. We
only assign them if Swig_always_false is true, which it never is.
Expand All @@ -302,8 +316,6 @@ var Swig_escape_always_false bool
var Swig_escape_val interface{}
%}

#endif

/* Function pointers are translated by the code in go.cxx into
_swig_fnptr. Member pointers are translated to _swig_memberptr. */

Expand Down
28 changes: 27 additions & 1 deletion Source/Modules/go.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -2035,7 +2035,7 @@ class GO:public Language {
* needs to explicitly escape. This is true if the parameter has a
* non-empty argout or freearg typemap, because in those cases the
* Go argument might be or contain a pointer. We need to ensure
* that that pointer does not oint into the stack, which means that
* that that pointer does not point into the stack, which means that
* it needs to escape.
* ---------------------------------------------------------------------- */
bool paramNeedsEscape(Parm *p) {
Expand Down Expand Up @@ -2502,6 +2502,28 @@ class GO:public Language {
p = Getattr(p, "tmap:goargout:next");
}
}

// When using cgo, if we need to memcpy a parameter to pass it to
// the C code, the compiler may think that the parameter is not
// live during the function call. If the garbage collector runs
// while the C/C++ function is running, the parameter may be
// freed. Force the compiler to see the parameter as live across
// the C/C++ function.
if (cgo_flag) {
int parm_count = emit_num_arguments(parms);
p = parms;
for (int i = 0; i < parm_count; ++i) {
p = getParm(p);
bool c_struct_type;
Delete(cgoTypeForGoValue(p, Getattr(p, "type"), &c_struct_type));
if (c_struct_type) {
Printv(f_go_wrappers, "\tif Swig_escape_always_false {\n", NULL);
Printv(f_go_wrappers, "\t\tSwig_escape_val = ", Getattr(p, "emit:goinput"), "\n", NULL);
Printv(f_go_wrappers, "\t}\n", NULL);
}
p = nextParm(p);
}
}
}

/* -----------------------------------------------------------------------
Expand Down Expand Up @@ -3537,6 +3559,8 @@ class GO:public Language {
DelWrapper(dummy);

Swig_typemap_attach_parms("gotype", parms, NULL);
Swig_typemap_attach_parms("goin", parms, NULL);
Swig_typemap_attach_parms("goargout", parms, NULL);
Swig_typemap_attach_parms("imtype", parms, NULL);
int parm_count = emit_num_arguments(parms);

Expand Down Expand Up @@ -3689,6 +3713,8 @@ class GO:public Language {

Printv(f_go_wrappers, call, "\n", NULL);

goargout(parms);

Printv(f_go_wrappers, "\treturn p\n", NULL);
Printv(f_go_wrappers, "}\n\n", NULL);

Expand Down

0 comments on commit 48263f4

Please sign in to comment.