[Go] Add goargout typemap.

This commit is contained in:
Ian Lance Taylor 2014-09-09 11:28:04 -07:00
commit acaaa0f31f
6 changed files with 286 additions and 44 deletions

View file

@ -5,6 +5,9 @@ See the RELEASENOTES file for a summary of changes in each release.
Version 3.0.3 (in progress)
===========================
2014-09-09: ianlancetaylor
[Go] Add goargout typemap.
2014-09-09: olly
[PHP] Fix segmentation faults with directors in PHP >= 5.4, and
reenable runme tests for director_basic testcase. Fix from

View file

@ -773,6 +773,27 @@ to gotype. If this is not defined no conversion is done.
</td>
</tr>
<tr>
<td>argout</td>
<td>
C/C++ code to adjust an argument value when returning from a function.
This is called after the real C/C++ function has run. This uses the
internally generated C/C++ type, based on imtype. This is only useful
for a pointer type of some sort. If this is not defined nothing will
be done.
</td>
</tr>
<tr>
<td>goargout</td>
<td>
Go code to adjust an argument value when returning from a function.
This is called after the real C/C++ function has run. The value will
be in imtype. This is only useful for a pointer type of some sort.
If this is not defined nothing will be done.
</td>
</tr>
<tr>
<td>directorin</td>
<td>

View file

@ -32,4 +32,12 @@ func main() {
fmt.Println("got", m, "want", want)
panic(m)
}
a := []string{"a", "bc", "def"}
go_inout.DoubleArray(&a)
dwant := []string{"a", "bc", "def", "aa", "bcbc", "defdef"}
if !reflect.DeepEqual(a, dwant) {
fmt.Println("got", a, "want", dwant)
panic(a)
}
}

View file

@ -1,9 +1,11 @@
// Test the goin and goout typemaps.
// Test the goin, goout, and goargout typemaps.
%module go_inout
%{
#include <string>
#include <vector>
#include <stdint.h>
%}
%inline
@ -21,7 +23,7 @@ struct RetStruct {
// Write a typemap that calls C++ by converting in and out of JSON.
%go_import("encoding/json")
%go_import("encoding/json", "bytes", "encoding/binary")
%insert(go_header)
%{
@ -77,3 +79,121 @@ RetStruct Same(MyStruct s)
}
%}
%inline
%{
struct MyArray {
std::vector<std::string> strings;
};
static uint64_t getuint64(const char* s) {
uint64_t ret = 0;
for (int i = 0; i < 8; i++, s++) {
ret |= static_cast<uint64_t>(*s) << i * 8;
}
return ret;
}
static void putuint64(std::string *s, size_t off, uint64_t v) {
for (int i = 0; i < 8; i++) {
(*s)[off + i] = (v >> (i * 8)) & 0xff;
}
}
%}
%typemap(gotype) MyArray* "*[]string"
%typemap(imtype) MyArray* "*string"
// Encode the slice as a single string, with length prefixes.
%typemap(goin) MyArray*
%{
{
var buf bytes.Buffer
bin := binary.LittleEndian
var b [8]byte
bin.PutUint64(b[:], uint64(len(*$input)))
buf.Write(b[:])
for _, s := range *$input {
bin.PutUint64(b[:], uint64(len(s)))
buf.Write(b[:])
buf.WriteString(s)
}
str := buf.String()
$result = &str
}
%}
// Unpack the string holding the packed slice.
%typemap(in) MyArray* (MyArray t)
%{
{
_gostring_ *s = $input;
const char *p = static_cast<const char *>(s->p);
uint64_t len = getuint64(p);
p += 8;
t.strings.resize(len);
for (uint64_t i = 0; i < len; i++) {
uint64_t slen = getuint64(p);
p += 8;
t.strings[i].assign(p, slen);
p += slen;
}
$1 = &t;
}
%}
// Pack the vector into a string.
%typemap(argout) MyArray*
%{
{
size_t tot = 8;
std::vector<std::string>::const_iterator p;
for (p = $1->strings.begin(); p != $1->strings.end(); ++p) {
tot += 8 + p->size();
}
std::string str;
str.resize(tot);
putuint64(&str, 0, $1->strings.size());
size_t off = 8;
for (p = $1->strings.begin(); p != $1->strings.end(); ++p) {
putuint64(&str, off, p->size());
off += 8;
str.replace(off, p->size(), *p);
off += p->size();
}
*$input = _swig_makegostring(str.data(), str.size());
}
%}
// Unpack the string into a []string.
%typemap(goargout) MyArray*
%{
{
str := *$input
bin := binary.LittleEndian
size := bin.Uint64([]byte(str[:8]))
str = str[8:]
r := make([]string, size)
for i := range r {
len := bin.Uint64([]byte(str[:8]))
str = str[8:]
r[i] = str[:len]
str = str[len:]
}
*$1 = r
}
%}
%inline
%{
void DoubleArray(MyArray* v) {
size_t size = v->strings.size();
for (size_t i = 0; i < size; i++) {
v->strings.push_back(v->strings[i] + v->strings[i]);
}
}
%}

View file

@ -225,13 +225,13 @@
%{ $1 = *($&1_ltype)&$input; %}
%typemap(out) SWIGTYPE *
%{ *($&1_ltype)&$result = $1; %}
%{ *($&1_ltype)&$result = ($1_ltype)$1; %}
%typemap(directorin) SWIGTYPE *
%{ $input = ($1_ltype)$1; %}
%{ *($&1_ltype)&$input = ($1_ltype)$1; %}
%typemap(directorout) SWIGTYPE *
%{ $result = ($1_ltype)$input; %}
%{ $result = *($&1_ltype)&$input; %}
%apply SWIGTYPE * { SWIGTYPE *const }

View file

@ -868,6 +868,7 @@ private:
Swig_typemap_attach_parms("default", parms, dummy);
Swig_typemap_attach_parms("gotype", parms, dummy);
Swig_typemap_attach_parms("goin", parms, dummy);
Swig_typemap_attach_parms("goargout", parms, dummy);
Swig_typemap_attach_parms("imtype", parms, dummy);
int parm_count = emit_num_arguments(parms);
int required_count = emit_num_required(parms);
@ -892,20 +893,25 @@ private:
bool needs_wrapper = (gccgo_flag || receiver || is_constructor || is_destructor || parm_count > required_count);
bool has_goout = false;
if (goout) {
has_goout = true;
}
// See whether any of the function parameters are represented by
// interface values. When calling the C++ code, we need to convert
// back to a uintptr.
if (!needs_wrapper) {
Parm *p = parms;
for (int i = 0; i < parm_count; ++i) {
p = getParm(p);
String *ty = Getattr(p, "type");
if (goTypeIsInterface(p, ty) || Getattr(p, "tmap:goin") != NULL) {
needs_wrapper = true;
break;
}
p = nextParm(p);
Parm *p = parms;
for (int i = 0; i < parm_count; ++i) {
p = getParm(p);
String *ty = Getattr(p, "type");
if (Getattr(p, "tmap:goargout")) {
has_goout = true;
needs_wrapper = true;
} else if (goTypeIsInterface(p, ty) || Getattr(p, "tmap:goin")) {
needs_wrapper = true;
}
p = nextParm(p);
}
if (goTypeIsInterface(n, result) || goout != NULL) {
needs_wrapper = true;
@ -982,7 +988,7 @@ private:
Printv(f_go_wrappers, "func ", NULL);
Parm *p = parms;
p = parms;
int pi = 0;
// Add the receiver if this is a method.
@ -1097,7 +1103,7 @@ private:
}
if (gccgo_flag) {
if (goout != NULL || is_constructor) {
if (has_goout || is_constructor) {
Printv(call, "\tfunc() {\n", NULL);
}
Printv(call, "\tdefer SwigCgocallDone()\n", NULL);
@ -1143,6 +1149,7 @@ private:
if ((i == 0 && is_destructor) || ((i > 0 || !receiver || base || is_constructor) && goTypeIsInterface(p, pt))) {
Printv(call, ".Swigcptr()", NULL);
}
Setattr(p, "emit:goinput", ln);
} else {
String *ivar = NewString("");
Printf(ivar, "_swig_i_%d", i);
@ -1154,19 +1161,22 @@ private:
Printv(f_go_wrappers, goin, NULL);
Delete(goin);
Printv(call, ivar, NULL);
Setattr(p, "emit:goinput", ivar);
}
p = nextParm(p);
}
Printv(call, ")\n", NULL);
if (gccgo_flag && (goout != NULL || is_constructor)) {
if (gccgo_flag && (has_goout || is_constructor)) {
Printv(call, "\t}()\n", NULL);
}
Printv(f_go_wrappers, call, NULL);
Delete(call);
goargout(parms, parm_count);
if (need_return_var) {
if (goout == NULL) {
Printv(f_go_wrappers, "\treturn swig_r\n", NULL);
@ -1676,6 +1686,32 @@ private:
}
}
/* -----------------------------------------------------------------------
* goargout()
*
* Handle Go argument output code if any. This is used for the Go
* function. This assumes that each parameter has an "emit:goinput"
* property with the name to use to refer to that parameter.
* ----------------------------------------------------------------------- */
void goargout(ParmList *parms, int parm_count) {
Parm *p = parms;
for (int i = 0; i < parm_count; ++i) {
p = getParm(p);
String *tm = Getattr(p, "tmap:goargout");
if (!tm) {
p = nextSibling(p);
} else {
tm = Copy(tm);
Replaceall(tm, "$result", "swig_r");
Replaceall(tm, "$input", Getattr(p, "emit:goinput"));
Printv(f_go_wrappers, tm, NULL);
Delete(tm);
p = Getattr(p, "tmap:goargout:next");
}
}
}
/* -----------------------------------------------------------------------
* freearg()
*
@ -3170,6 +3206,7 @@ private:
Swig_typemap_attach_parms("directorargout", parms, w);
Swig_typemap_attach_parms("godirectorin", parms, w);
Swig_typemap_attach_parms("goin", parms, dummy);
Swig_typemap_attach_parms("goargout", parms, dummy);
if (!is_ignored) {
// We use an interface to see if this method is defined in Go.
@ -3282,16 +3319,29 @@ private:
if (GetFlag(n, "abstract")) {
Printv(f_go_wrappers, "\tpanic(\"call to pure virtual method\")\n", NULL);
} else {
bool has_goout = false;
String *goout = NULL;
if (SwigType_type(result) != T_VOID) {
Printv(f_go_wrappers, "\tvar swig_r ", goImType(n, result), "\n", NULL);
goout = Swig_typemap_lookup("goout", n, "swig_r", NULL);
if (goout) {
has_goout = true;
}
}
p = parms;
for (int i = 0; i < parm_count; ++i) {
p = getParm(p);
if (Getattr(p, "tmap:goargout")) {
has_goout = true;
}
p = nextParm(p);
}
String *call = NewString("");
if (gccgo_flag) {
if (goout != NULL) {
if (has_goout) {
Printv(call, "\tfunc() {\n", NULL);
}
Printv(call, "\tdefer SwigCgocallDone()\n", NULL);
@ -3320,6 +3370,7 @@ private:
if (goTypeIsInterface(p, pt)) {
Printv(call, ".Swigcptr()", NULL);
}
Setattr(p, "emit:goinput", ln);
} else {
String *ivar = NewString("");
Printf(ivar, "_swig_i_%d", i);
@ -3331,7 +3382,7 @@ private:
Printv(f_go_wrappers, goin, NULL);
Delete(goin);
Printv(call, ivar, NULL);
Delete(ivar);
Setattr(p, "emit:goinput", ivar);
}
p = nextParm(p);
@ -3339,13 +3390,15 @@ private:
Printv(call, ")\n", NULL);
if (gccgo_flag && goout != NULL) {
if (gccgo_flag && has_goout) {
Printv(call, "\t}()\n", NULL);
}
Printv(f_go_wrappers, call, NULL);
Delete(call);
goargout(parms, parm_count);
if (SwigType_type(result) != T_VOID) {
if (goout == NULL) {
Printv(f_go_wrappers, "\treturn swig_r\n", NULL);
@ -3526,6 +3579,7 @@ private:
String *goin = Getattr(p, "tmap:goin");
if (goin == NULL) {
Printv(call, ln, NULL);
Setattr(p, "emit:goinput", ln);
} else {
String *ivar = NewString("");
Printf(ivar, "_swig_i_%d", i);
@ -3537,7 +3591,7 @@ private:
Printv(f_go_wrappers, goin, NULL);
Delete(goin);
Printv(call, ivar, NULL);
Delete(ivar);
Setattr(p, "emit:goinput", ivar);
}
Delete(ln);
@ -3554,6 +3608,8 @@ private:
Printv(f_go_wrappers, call, NULL);
Delete(call);
goargout(parms, parm_count);
if (SwigType_type(result) != T_VOID) {
if (goout == NULL) {
Printv(f_go_wrappers, "\treturn swig_r\n", NULL);
@ -3838,6 +3894,21 @@ private:
Printv(w->code, " crosscall2(", callback_wname, ", &swig_a, (int) sizeof swig_a);\n", NULL);
/* Marshal outputs */
for (p = parms; p;) {
String *tm;
if ((tm = Getattr(p, "tmap:directorargout"))) {
tm = Copy(tm);
Replaceall(tm, "$result", "jresult");
Replaceall(tm, "$input", Getattr(p, "emit:directorinput"));
Printv(w->code, tm, "\n", NIL);
Delete(tm);
p = Getattr(p, "tmap:directorargout:next");
} else {
p = nextSibling(p);
}
}
if (SwigType_type(result) != T_VOID) {
String *result_str = NewString("c_result");
String *tm = Swig_typemap_lookup("directorout", n, result_str, NULL);
@ -3886,7 +3957,7 @@ private:
String *pn = NewString("g");
Append(pn, Getattr(p, "lname"));
Setattr(p, "emit:input", pn);
Setattr(p, "emit:directorinput", pn);
String *tm = gccgoCTypeForGoValue(n, Getattr(p, "type"), pn);
Wrapper_add_local(w, pn, tm);
@ -3915,6 +3986,21 @@ private:
}
Printv(w->code, callback_wname, "(go_val", args, ");\n", NULL);
/* Marshal outputs */
for (p = parms; p;) {
String *tm;
if ((tm = Getattr(p, "tmap:directorargout"))) {
tm = Copy(tm);
Replaceall(tm, "$result", "jresult");
Replaceall(tm, "$input", Getattr(p, "emit:directorinput"));
Printv(w->code, tm, "\n", NIL);
Delete(tm);
p = Getattr(p, "tmap:directorargout:next");
} else {
p = nextSibling(p);
}
}
if (SwigType_type(result) != T_VOID) {
String *result_str = NewString("c_result");
String *tm = Swig_typemap_lookup("directorout", n, result_str, NULL);
@ -3933,21 +4019,6 @@ private:
Delete(result_str);
}
}
/* Marshal outputs */
for (p = parms; p;) {
String *tm;
if ((tm = Getattr(p, "tmap:directorargout"))) {
tm = Copy(tm);
Replaceall(tm, "$result", "jresult");
Replaceall(tm, "$input", Getattr(p, "emit:directorinput"));
Printv(w->code, tm, "\n", NIL);
Delete(tm);
p = Getattr(p, "tmap:directorargout:next");
} else {
p = nextSibling(p);
}
}
} else {
assert(is_pure_virtual);
Printv(w->code, " _swig_gopanic(\"call to pure virtual function ", Getattr(parent, "sym:name"), name, "\");\n", NULL);
@ -4990,6 +5061,17 @@ private:
String *gcCTypeForGoValue(Node *n, SwigType *type, String *name) {
bool is_interface;
String *gt = goTypeWithInfo(n, type, true, &is_interface);
String *tail = NewString("");
SwigType *t = SwigType_typedef_resolve_all(type);
if (!SwigType_isreference(t)) {
while (Strncmp(gt, "*", 1) == 0) {
Replace(gt, "*", "", DOH_REPLACE_FIRST);
Printv(tail, "*", NULL);
}
}
Delete(t);
bool is_string = Strcmp(gt, "string") == 0;
bool is_slice = Strncmp(gt, "[]", 2) == 0;
bool is_function = Strcmp(gt, "_swig_fnptr") == 0;
@ -5017,18 +5099,21 @@ private:
if (is_string) {
// Note that we don't turn a reference to a string into a
// pointer to a string. Strings are immutable anyhow.
ret = NewString("_gostring_ ");
Append(ret, name);
ret = NewString("");
Printv(ret, "_gostring_", tail, " ", name, NULL);
Delete(tail);
return ret;
} else if (is_slice) {
// Slices are always passed as a _goslice_, whether or not references
// are involved.
ret = NewString("_goslice_ ");
Append(ret, name);
ret = NewString("");
Printv(ret, "_goslice_", tail, " ", name, NULL);
Delete(tail);
return ret;
} else if (is_function || is_member) {
ret = NewString("void *");
Append(ret, name);
ret = NewString("");
Printv(ret, "void*", tail, " ", name, NULL);
Delete(tail);
return ret;
} else if (is_complex64) {
ret = NewString("_Complex float ");
@ -5045,6 +5130,7 @@ private:
SwigType_add_pointer(t);
ret = SwigType_lstr(t, name);
Delete(t);
Delete(tail);
return ret;
} else {
SwigType *t = SwigType_typedef_resolve_all(type);
@ -5068,6 +5154,7 @@ private:
}
Delete(q);
Delete(t);
Delete(tail);
return ret;
}
}
@ -5100,14 +5187,17 @@ private:
} else if (is_float64) {
ret = NewString("double ");
} else {
Delete(tail);
return SwigType_lstr(type, name);
}
}
Append(ret, tail);
if (SwigType_isreference(type)) {
Append(ret, "* ");
}
Append(ret, name);
Delete(tail);
return ret;
}