From 8ba93b96cb15de4ddd1824dde607d55699576194 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Sun, 5 May 2024 14:31:36 -0400 Subject: [PATCH 1/2] x/mobile/bind: support slices of structs Adds code generation for supporting slices of structs in gobind. Fixes https://github.com/golang/go/issues/13445 NOTE: This PR isn't quite done yet, I'm having some trouble getting the testing environment set up. It seems like the default test environment is a bit outdated? In any case, could I use this PR's CI to iterate on test failures? Open to other suggestions as well. --- bind/gen.go | 10 ++++++++ bind/gengo.go | 14 +++++++++++ bind/genjava.go | 35 ++++++++++++++++++++++++-- bind/genobjc.go | 34 ++++++++++++++++++++++--- bind/java/seq_android.c.support | 42 +++++++++++++++++++++++++++++++ bind/java/seq_android.h | 6 +++++ bind/objc/seq_darwin.h | 6 +++++ bind/testdata/structs.go | 32 +++++++++++++++++++++++ bind/testdata/structs.java.golden | 9 +++++-- 9 files changed, 180 insertions(+), 8 deletions(-) diff --git a/bind/gen.go b/bind/gen.go index 4fedfbeb5..64dad01c2 100644 --- a/bind/gen.go +++ b/bind/gen.go @@ -402,6 +402,11 @@ func (g *Generator) cgoType(t types.Type) string { default: g.errorf("unsupported slice type: %s", t) } + case *types.Pointer: + switch e.Elem().(type) { + case *types.Named: + return "nrefnumslice" + } default: g.errorf("unsupported slice type: %s", t) } @@ -507,6 +512,11 @@ func (g *Generator) isSupported(t types.Type) bool { switch e := t.Elem().(type) { case *types.Basic: return e.Kind() == types.Uint8 + case *types.Pointer: + switch f := e.Elem().(type) { + case *types.Named: + return g.validPkg(f.Obj().Pkg()) + } } case *types.Pointer: switch t := t.Elem().(type) { diff --git a/bind/gengo.go b/bind/gengo.go index 8087c7ad2..9a017e5ec 100644 --- a/bind/gengo.go +++ b/bind/gengo.go @@ -119,6 +119,13 @@ func (g *goGen) genWrite(toVar, fromVar string, t types.Type, mode varMode) { default: g.errorf("unsupported type: %s", t) } + case *types.Pointer: + switch e.Elem().(type) { + case *types.Named: + g.Printf("%s := toRefNumSlice(%s)\n", toVar, fromVar) + default: + g.errorf("unsupported type: %s", t) + } default: g.errorf("unsupported type: %s", t) } @@ -403,6 +410,13 @@ func (g *goGen) genRead(toVar, fromVar string, typ types.Type, mode varMode) { default: g.errorf("unsupported type: %s", t) } + case *types.Pointer: + switch e.Elem().(type) { + case *types.Named: + g.Printf("%s := fromRefNumSlice(%s)\n", toVar, fromVar) + default: + g.errorf("unsupported type: %s", t) + } default: g.errorf("unsupported type: %s", t) } diff --git a/bind/genjava.go b/bind/genjava.go index b197640aa..fecfb191c 100644 --- a/bind/genjava.go +++ b/bind/genjava.go @@ -135,6 +135,13 @@ func (j *javaClassInfo) toJavaType(T types.Type) *java.Type { case types.Uint8: // Byte. return &java.Type{Kind: java.Array, Elem: &java.Type{Kind: java.Byte}} } + case *types.Pointer: + switch e.Elem().(type) { + case *types.Named: + if isJavaType(e) { + return &java.Type{Kind: java.Array, Elem: &java.Type{Kind: java.Object, Class: classNameFor(e)}} + } + } } return nil case *types.Named: @@ -641,8 +648,18 @@ func (g *JavaGen) jniType(T types.Type) string { return "TODO" } case *types.Slice: - return "jbyteArray" - + switch e := T.Elem().(type) { + case *types.Basic: + switch e.Kind() { + case types.Uint8: // Byte. + return "jbyteArray" + } + case *types.Pointer: + switch e.Elem().(type) { + case *types.Named: + return "jobjectArray" + } + } case *types.Pointer: if _, ok := T.Elem().(*types.Named); ok { return g.jniType(T.Elem()) @@ -915,6 +932,13 @@ func (g *JavaGen) genJavaToC(varName string, t types.Type, mode varMode) { default: g.errorf("unsupported type: %s", t) } + case *types.Pointer: + switch e.Elem().(type) { + case *types.Named: + g.Printf("nobjectarray _%s = go_seq_from_java_objectarray(env, %s, %d);\n", varName, varName, toCFlag(mode == modeRetained)) + default: + g.errorf("unsupported type: %s", t) + } default: g.errorf("unsupported type: %s", t) } @@ -952,6 +976,13 @@ func (g *JavaGen) genCToJava(toName, fromName string, t types.Type, mode varMode default: g.errorf("unsupported type: %s", t) } + case *types.Pointer: + switch e.Elem().(type) { + case *types.Named: + g.Printf("jobjectArray %s = go_seq_to_java_objectarray(env, %s, %d);\n", toName, fromName, toCFlag(mode == modeRetained)) + default: + g.errorf("unsupported type: %s", t) + } default: g.errorf("unsupported type: %s", t) } diff --git a/bind/genobjc.go b/bind/genobjc.go index d5914138a..28df8b9c7 100644 --- a/bind/genobjc.go +++ b/bind/genobjc.go @@ -696,6 +696,13 @@ func (g *ObjcGen) genWrite(varName string, t types.Type, mode varMode) { default: g.errorf("unsupported type: %s", t) } + case *types.Pointer: + switch e.Elem().(type) { + case *types.Named: + g.Printf("nrefnumslice _%s = go_seq_from_objc_refnumarray(%s);\n", varName, varName) + default: + g.errorf("unsupported type: %s", t) + } default: g.errorf("unsupported type: %s", t) } @@ -763,6 +770,13 @@ func (g *ObjcGen) genRead(toName, fromName string, t types.Type, mode varMode) { default: g.errorf("unsupported type: %s", t) } + case *types.Pointer: + switch e.Elem().(type) { + case *types.Named: + g.Printf("NSArray *%s = go_seq_to_objc_refnumarray(%s);\n", toName, fromName) + default: + g.errorf("unsupported type: %s", t) + } default: g.errorf("unsupported type: %s", t) } @@ -1047,6 +1061,11 @@ func (g *ObjcGen) genRelease(varName string, t types.Type, mode varMode) { g.Printf("}\n") } } + case *types.Pointer: + switch e.Elem().(type) { + case *types.Named: + g.Printf("free(_%s.ptr);\n", varName) + } } } } @@ -1344,10 +1363,17 @@ func (g *ObjcGen) objcType(typ types.Type) string { return "TODO" } case *types.Slice: - elem := g.objcType(typ.Elem()) - // Special case: NSData seems to be a better option for byte slice. - if elem == "byte" { - return "NSData* _Nullable" + switch e := typ.Elem().(type) { + case *types.Basic: + switch e.Kind() { + case types.Uint8: + return "NSData* _Nullable" + } + case *types.Pointer: + switch e.Elem().(type) { + case *types.Named: + return "NSArray* _Nullable" + } } // TODO(hyangah): support other slice types: NSArray or CFArrayRef. // Investigate the performance implication. diff --git a/bind/java/seq_android.c.support b/bind/java/seq_android.c.support index 77ec5f4ae..e8307e0c7 100644 --- a/bind/java/seq_android.c.support +++ b/bind/java/seq_android.c.support @@ -93,6 +93,20 @@ jbyteArray go_seq_to_java_bytearray(JNIEnv *env, nbyteslice s, int copy) { return res; } +jobjectArray go_seq_to_java_objectarray(JNIEnv *env, nrefnumslice arr) { + if (arr.ptr == NULL) { + return NULL; + } + jobjectArray res = (*env)->NewObjectArray(env, arr.len, (*env)->FindClass(env, "java/lang/Object"), NULL); + if (res == NULL) { + LOG_FATAL("NewObjectArray failed"); + } + for (int i = 0; i < arr.len; i++) { + (*env)->SetObjectArrayElement(env, res, i, go_seq_from_refnum(env, arr.ptr[i])); + } + return res; +} + #define surr1 0xd800 #define surr2 0xdc00 #define surr3 0xe000 @@ -224,6 +238,34 @@ nbyteslice go_seq_from_java_bytearray(JNIEnv *env, jbyteArray arr, int copy) { return res; } +nrefnumslice go_seq_from_java_objectarray(JNIEnv *env, jobjectArray arr) { + struct nrefnumslice res = {NULL, 0}; + if (arr == NULL) { + return res; + } + + jsize len = (*env)->GetArrayLength(env, arr); + if (len == 0) { + return res; + } + jint *ptr = (jint *)(*env)->GetPrimitiveArrayCritical(env, arr, NULL); + if (ptr == NULL) { + LOG_FATAL("GetPrimitiveArrayCritical failed"); + } + void *refnums = (void *)malloc(len * sizeof(jint)); + if (refnums == NULL) { + LOG_FATAL("malloc failed"); + } + // convert to refnums + for (int i = 0; i < len; i++) { + refnums[i] = go_seq_to_refnum(env, (*env)->GetObjectArrayElement(env, arr, i)); + } + res.ptr = refnums; + res.len = len; + (*env)->ReleasePrimitiveArrayCritical(env, arr, ptr, JNI_ABORT); + return res; +} + int32_t go_seq_to_refnum_go(JNIEnv *env, jobject o) { if (o == NULL) { return NULL_REFNUM; diff --git a/bind/java/seq_android.h b/bind/java/seq_android.h index 26e90251e..7b8d3e144 100644 --- a/bind/java/seq_android.h +++ b/bind/java/seq_android.h @@ -30,6 +30,10 @@ typedef struct nbyteslice { void *ptr; jsize len; } nbyteslice; +typedef struct nrefnumslice { + void *ptr; + jsize len; +} nrefnumslice; typedef jlong nint; extern void go_seq_dec_ref(int32_t ref); @@ -47,6 +51,8 @@ extern jobject go_seq_get_exception(JNIEnv *env); extern jbyteArray go_seq_to_java_bytearray(JNIEnv *env, nbyteslice s, int copy); extern nbyteslice go_seq_from_java_bytearray(JNIEnv *env, jbyteArray s, int copy); +extern jobjectArray go_seq_to_java_objectarray(JNIEnv *env, nrefnumslice arr); +extern nrefnumslice go_seq_from_java_objectarray(JNIEnv *env, jobjectArray arr); extern void go_seq_release_byte_array(JNIEnv *env, jbyteArray arr, jbyte* ptr); extern jstring go_seq_to_java_string(JNIEnv *env, nstring str); diff --git a/bind/objc/seq_darwin.h b/bind/objc/seq_darwin.h index 1aeec4ada..1b53ffbf2 100644 --- a/bind/objc/seq_darwin.h +++ b/bind/objc/seq_darwin.h @@ -34,6 +34,10 @@ typedef struct nbyteslice { void *ptr; int len; } nbyteslice; +typedef struct nrefnumslice { + void *ptr; + int len; +} nrefnumslice; typedef int nint; extern void init_seq(); @@ -55,9 +59,11 @@ extern GoSeqRef *go_seq_from_refnum(int32_t refnum); extern id go_seq_objc_from_refnum(int32_t refnum); extern nbyteslice go_seq_from_objc_bytearray(NSData *data, int copy); +extern nrefnumslice go_seq_from_objc_objectarray(NSArray *arr); extern nstring go_seq_from_objc_string(NSString *s); extern NSData *go_seq_to_objc_bytearray(nbyteslice, int copy); +extern NSArray *go_seq_to_objc_objectarray(nrefnumslice arr); extern NSString *go_seq_to_objc_string(nstring str); #endif // __GO_SEQ_DARWIN_HDR__ diff --git a/bind/testdata/structs.go b/bind/testdata/structs.go index 5974bdc36..450bdd9b9 100644 --- a/bind/testdata/structs.go +++ b/bind/testdata/structs.go @@ -25,6 +25,38 @@ func IdentityWithError(s *S) (*S, error) { return s, nil } +func (s *S) Repeat(n int) []*S { + t := make([]*S, n) + for i := range t { + t[i] = s + } + return t +} + +func (s *S) RepeatWithError(n int) ([]*S, error) { + return Repeat(s, n), nil +} + +func Repeat(s *S, n int) []*S { + t := make([]*S, n) + for i := range t { + t[i] = s + } + return t +} + +func RepeatWithError(s *S, n int) ([]*S, error) { + return Repeat(s, n), nil +} + +func FirstSum(s []*S) float64 { + return s[0].Sum() +} + +func FirstSumWithError(s []*S) (float64, error) { + return s[0].Sum(), nil +} + type ( S2 struct{} I interface { diff --git a/bind/testdata/structs.java.golden b/bind/testdata/structs.java.golden index 5a140fe00..9b8d0743d 100644 --- a/bind/testdata/structs.java.golden +++ b/bind/testdata/structs.java.golden @@ -30,6 +30,8 @@ public final class S implements Seq.Proxy { public final native void setY(double v); public native S identity() throws Exception; + public native S[] repeat(long n); + public native S[] repeatWithError(long n) throws Exception; public native double sum(); @Override public boolean equals(Object o) { if (o == null || !(o instanceof S)) { @@ -176,7 +178,7 @@ import go.Seq; public abstract class Structs { static { - Seq.touch(); // for loading the native library + Seq.touch(); // for loading the native library _init(); } @@ -200,7 +202,10 @@ public abstract class Structs { public native void m(); } - + public static native double firstSum(S[] s); + public static native double firstSumWithError(S[] s) throws Exception; public static native S identity(S s); public static native S identityWithError(S s) throws Exception; + public static native S[] repeat(S s, long n); + public static native S[] repeatWithError(S s, long n) throws Exception; } From 19dd1fc3c052d0490e2094325e2d54c0e24ffc2d Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Wed, 5 Aug 2026 22:28:11 -0400 Subject: [PATCH 2/2] bind: support slices of pointers to bound structs --- bind/gen.go | 12 +- bind/gengo.go | 60 +++++-- bind/genjava.go | 50 +++--- bind/genobjc.go | 49 +++--- bind/java/SeqTest.java | 48 ++++++ bind/java/seq_android.c.support | 37 ++-- bind/java/seq_android.go.support | 33 ++++ bind/java/seq_android.h | 7 +- bind/objc/SeqTest.m | 58 +++++++ bind/objc/seq_darwin.go.support | 33 ++++ bind/objc/seq_darwin.h | 9 +- bind/objc/seq_darwin.m.support | 54 ++++++ bind/testdata/structs.go | 27 +-- bind/testdata/structs.go.golden | 229 ++++++++++++++++++++++++- bind/testdata/structs.java.c.golden | 101 +++++++++++ bind/testdata/structs.java.golden | 52 +++++- bind/testdata/structs.java.h.golden | 5 + bind/testdata/structs.objc.go.h.golden | 2 + bind/testdata/structs.objc.h.golden | 31 ++++ bind/testdata/structs.objc.m.golden | 142 +++++++++++++++ bind/testdata/testpkg/testpkg.go | 37 ++++ bind/types.go | 12 ++ cmd/gobind/doc.go | 4 + 23 files changed, 983 insertions(+), 109 deletions(-) diff --git a/bind/gen.go b/bind/gen.go index ccd9f03fa..b39f89e96 100644 --- a/bind/gen.go +++ b/bind/gen.go @@ -397,10 +397,8 @@ func (g *Generator) cgoType(t types.Type) string { if isBytesSlice(t) { return "nbyteslice" } - if p, ok := types.Unalias(t.Elem()).(*types.Pointer); ok { - if _, ok := types.Unalias(p.Elem()).(*types.Named); ok { - return "nrefnumslice" - } + if _, ok := refSliceElem(t); ok { + return "nrefnumslice" } g.errorf("unsupported slice type: %s", t) case *types.Pointer: @@ -505,10 +503,8 @@ func (g *Generator) isSupported(t types.Type) bool { if isBytesSlice(t) { return true } - if p, ok := types.Unalias(t.Elem()).(*types.Pointer); ok { - if n, ok := types.Unalias(p.Elem()).(*types.Named); ok { - return g.validPkg(n.Obj().Pkg()) - } + if n, ok := refSliceElem(t); ok { + return g.validPkg(n.Obj().Pkg()) } return false case *types.Pointer: diff --git a/bind/gengo.go b/bind/gengo.go index f69532d59..4fdf4fcc9 100644 --- a/bind/gengo.go +++ b/bind/gengo.go @@ -115,11 +115,9 @@ func (g *goGen) genWrite(toVar, fromVar string, t types.Type, mode varMode) { g.Printf("%s := fromSlice(%s, %v)\n", toVar, fromVar, mode == modeRetained) return } - if p, ok := types.Unalias(t.Elem()).(*types.Pointer); ok { - if _, ok := types.Unalias(p.Elem()).(*types.Named); ok { - g.Printf("%s := toRefNumSlice(%s)\n", toVar, fromVar) - return - } + if _, ok := refSliceElem(t); ok { + g.genToRefNumSlice(toVar, fromVar) + return } g.errorf("unsupported type: %s", t) case *types.Pointer: @@ -153,6 +151,45 @@ func (g *goGen) genToRefNum(toVar, fromVar string) { g.Printf("}\n") } +// genToRefNumSlice generates Go code for converting a slice of pointers to +// bound types into a slice of refnums, passed on as C memory owned by the +// receiving foreign code. +func (g *goGen) genToRefNumSlice(toVar, fromVar string) { + g.Printf("var %s_refs []int32\n", toVar) + g.Printf("if %s != nil {\n", fromVar) + g.Printf(" %s_refs = make([]int32, len(%s))\n", toVar, fromVar) + g.Printf(" for i, e := range %s {\n", fromVar) + g.Printf(" %s_refs[i] = _seq.NullRefNum\n", toVar) + g.Printf(" if e != nil {\n") + g.Printf(" %s_refs[i] = _seq.ToRefNum(e)\n", toVar) + g.Printf(" }\n") + g.Printf(" }\n") + g.Printf("}\n") + g.Printf("%s := fromRefSlice(%s_refs)\n", toVar, toVar) +} + +// genFromRefNumSlice generates Go code for converting a slice of refnums, +// held in C memory owned by this side, into a slice of pointers to the bound +// type n. +func (g *goGen) genFromRefNumSlice(toVar, fromVar string, n *types.Named) { + o := n.Obj() + oPkg := o.Pkg() + if !g.validPkg(oPkg) { + g.errorf("type %s is defined in %s, which is not bound", n, oPkg) + return + } + g.Printf("var %s []*%s%s\n", toVar, g.pkgName(oPkg), o.Name()) + g.Printf("if %s_refs := toRefSlice(%s); %s_refs != nil {\n", toVar, fromVar, toVar) + g.Printf(" %s = make([]*%s%s, len(%s_refs))\n", toVar, g.pkgName(oPkg), o.Name(), toVar) + g.Printf(" for i, refnum := range %s_refs {\n", toVar) + g.Printf(" // Must be a Go object\n") + g.Printf(" if ref := _seq.FromRefNum(refnum); ref != nil {\n") + g.Printf(" %s[i] = ref.Get().(*%s%s)\n", toVar, g.pkgName(oPkg), o.Name()) + g.Printf(" }\n") + g.Printf(" }\n") + g.Printf("}\n") +} + func (g *goGen) genFuncSignature(o *types.Func, objName string) { g.Printf("//export proxy%s_%s_%s\n", g.pkgPrefix, objName, o.Name()) g.Printf("func proxy%s_%s_%s(", g.pkgPrefix, objName, o.Name()) @@ -399,11 +436,9 @@ func (g *goGen) genRead(toVar, fromVar string, typ types.Type, mode varMode) { g.Printf("%s := toSlice(%s, %v)\n", toVar, fromVar, mode == modeRetained) return } - if p, ok := types.Unalias(t.Elem()).(*types.Pointer); ok { - if _, ok := types.Unalias(p.Elem()).(*types.Named); ok { - g.Printf("%s := fromRefNumSlice(%s)\n", toVar, fromVar) - return - } + if n, ok := refSliceElem(t); ok { + g.genFromRefNumSlice(toVar, fromVar, n) + return } g.errorf("unsupported type: %s", t) case *types.Pointer: @@ -496,6 +531,11 @@ func (g *goGen) typeString(typ types.Type) string { default: g.errorf("not yet supported, pointer type %s / %T", t, t) } + case *types.Slice: + if _, ok := refSliceElem(t); ok { + return fmt.Sprintf("[]%s", g.typeString(t.Elem())) + } + return types.TypeString(typ, types.RelativeTo(pkg)) default: return types.TypeString(typ, types.RelativeTo(pkg)) } diff --git a/bind/genjava.go b/bind/genjava.go index 4a15f5953..655d1e7a7 100644 --- a/bind/genjava.go +++ b/bind/genjava.go @@ -132,11 +132,6 @@ func (j *javaClassInfo) toJavaType(T types.Type) *java.Type { if isBytesSlice(T) { return &java.Type{Kind: java.Array, Elem: &java.Type{Kind: java.Byte}} } - if p, ok := types.Unalias(T.Elem()).(*types.Pointer); ok { - if _, ok := types.Unalias(p.Elem()).(*types.Named); ok && isJavaType(p) { - return &java.Type{Kind: java.Array, Elem: &java.Type{Kind: java.Object, Class: classNameFor(p)}} - } - } return nil case *types.Named: if isJavaType(T) { @@ -642,18 +637,13 @@ func (g *JavaGen) jniType(T types.Type) string { return "TODO" } case *types.Slice: - switch e := T.Elem().(type) { - case *types.Basic: - switch e.Kind() { - case types.Uint8: // Byte. - return "jbyteArray" - } - case *types.Pointer: - switch e.Elem().(type) { - case *types.Named: - return "jobjectArray" - } + if isBytesSlice(T) { + return "jbyteArray" } + if _, ok := refSliceElem(T); ok { + return "jobjectArray" + } + g.errorf("unsupported slice type: %s", T) case *types.Pointer: if _, ok := types.Unalias(T.Elem()).(*types.Named); ok { return g.jniType(T.Elem()) @@ -922,11 +912,9 @@ func (g *JavaGen) genJavaToC(varName string, t types.Type, mode varMode) { g.Printf("nbyteslice _%s = go_seq_from_java_bytearray(env, %s, %d);\n", varName, varName, toCFlag(mode == modeRetained)) return } - if p, ok := types.Unalias(t.Elem()).(*types.Pointer); ok { - if _, ok := types.Unalias(p.Elem()).(*types.Named); ok { - g.Printf("nobjectarray _%s = go_seq_from_java_objectarray(env, %s, %d);\n", varName, varName, toCFlag(mode == modeRetained)) - return - } + if _, ok := refSliceElem(t); ok { + g.Printf("nrefnumslice _%s = go_seq_from_java_objectarray(env, %s);\n", varName, varName) + return } g.errorf("unsupported type: %s", t) case *types.Named: @@ -959,11 +947,9 @@ func (g *JavaGen) genCToJava(toName, fromName string, t types.Type, mode varMode g.Printf("jbyteArray %s = go_seq_to_java_bytearray(env, %s, %d);\n", toName, fromName, toCFlag(mode == modeRetained)) return } - if p, ok := types.Unalias(t.Elem()).(*types.Pointer); ok { - if _, ok := types.Unalias(p.Elem()).(*types.Named); ok { - g.Printf("jobjectArray %s = go_seq_to_java_objectarray(env, %s, %d);\n", toName, fromName, toCFlag(mode == modeRetained)) - return - } + if n, ok := refSliceElem(t); ok { + g.genFromRefnumArray(toName, fromName, t, n.Obj()) + return } g.errorf("unsupported type: %s", t) case *types.Pointer: @@ -1004,6 +990,18 @@ func (g *JavaGen) genFromRefnum(toName, fromName string, t types.Type, o *types. g.Printf(");\n") } +// genFromRefnumArray generates the conversion of a slice of reference numbers +// to a Java array of proxies for the type named by o. +func (g *JavaGen) genFromRefnumArray(toName, fromName string, t types.Type, o *types.TypeName) { + oPkg := o.Pkg() + if !g.validPkg(oPkg) { + g.errorf("type %s is defined in package %s, which is not bound", t, oPkg) + return + } + p := pkgPrefix(oPkg) + g.Printf("jobjectArray %s = go_seq_to_java_objectarray(env, %s, proxy_class_%s_%s, proxy_class_%s_%s_cons);\n", toName, fromName, p, o.Name(), p, o.Name()) +} + func (g *JavaGen) gobindOpts() string { opts := []string{"-lang=java"} if g.JavaPkg != "" { diff --git a/bind/genobjc.go b/bind/genobjc.go index b0e2b6020..6dc508c9d 100644 --- a/bind/genobjc.go +++ b/bind/genobjc.go @@ -694,11 +694,9 @@ func (g *ObjcGen) genWrite(varName string, t types.Type, mode varMode) { g.Printf("nbyteslice _%s = go_seq_from_objc_bytearray(%s, %d);\n", varName, varName, toCFlag(mode == modeRetained)) return } - if p, ok := types.Unalias(t.Elem()).(*types.Pointer); ok { - if _, ok := types.Unalias(p.Elem()).(*types.Named); ok { - g.Printf("nrefnumslice _%s = go_seq_from_objc_refnumarray(%s);\n", varName, varName) - return - } + if _, ok := refSliceElem(t); ok { + g.Printf("nrefnumslice _%s = go_seq_from_objc_objectarray(%s);\n", varName, varName) + return } g.errorf("unsupported type: %s", t) case *types.Named: @@ -745,6 +743,13 @@ func (g *ObjcGen) genRefRead(toName, fromName string, t types.Type) { g.Printf("}\n") } +// genRefReadArray generates the conversion of a slice of reference numbers to +// an NSArray of proxies for the type t. +func (g *ObjcGen) genRefReadArray(toName, fromName string, t types.Type) { + ptype := g.refTypeBase(t) + g.Printf("NSArray<%s*>* %s = go_seq_to_objc_objectarray(%s, [%s class]);\n", ptype, toName, fromName, ptype) +} + func (g *ObjcGen) genRead(toName, fromName string, t types.Type, mode varMode) { switch t := types.Unalias(t).(type) { case *types.Basic: @@ -761,11 +766,9 @@ func (g *ObjcGen) genRead(toName, fromName string, t types.Type, mode varMode) { g.Printf("NSData *%s = go_seq_to_objc_bytearray(%s, %d);\n", toName, fromName, toCFlag(mode == modeRetained)) return } - if p, ok := types.Unalias(t.Elem()).(*types.Pointer); ok { - if _, ok := types.Unalias(p.Elem()).(*types.Named); ok { - g.Printf("NSArray *%s = go_seq_to_objc_refnumarray(%s);\n", toName, fromName) - return - } + if n, ok := refSliceElem(t); ok { + g.genRefReadArray(toName, fromName, types.NewPointer(n)) + return } g.errorf("unsupported type: %s", t) case *types.Pointer: @@ -1043,12 +1046,6 @@ func (g *ObjcGen) genRelease(varName string, t types.Type, mode varMode) { g.Printf("if (![%s isKindOfClass:[NSMutableData class]]) {\n", varName) g.Printf(" free(_%s.ptr);\n", varName) g.Printf("}\n") - return - } - if p, ok := types.Unalias(t.Elem()).(*types.Pointer); ok { - if _, ok := types.Unalias(p.Elem()).(*types.Named); ok { - g.Printf("free(_%s.ptr);\n", varName) - } } } } @@ -1344,19 +1341,15 @@ func (g *ObjcGen) objcType(typ types.Type) string { return "TODO" } case *types.Slice: - switch e := typ.Elem().(type) { - case *types.Basic: - switch e.Kind() { - case types.Uint8: - return "NSData* _Nullable" - } - case *types.Pointer: - switch e.Elem().(type) { - case *types.Named: - return "NSArray* _Nullable" - } + elem := g.objcType(typ.Elem()) + // Special case: NSData seems to be a better option for byte slice. + if elem == "byte" { + return "NSData* _Nullable" + } + if n, ok := refSliceElem(typ); ok { + return "NSArray<" + g.refTypeBase(types.NewPointer(n)) + "*>* _Nullable" } - // TODO(hyangah): support other slice types: NSArray or CFArrayRef. + // TODO(hyangah): support other slice types: CFArrayRef. // Investigate the performance implication. g.errorf("unsupported type: %s", typ) return "TODO" diff --git a/bind/java/SeqTest.java b/bind/java/SeqTest.java index f89693c97..530d760a6 100644 --- a/bind/java/SeqTest.java +++ b/bind/java/SeqTest.java @@ -457,6 +457,54 @@ public NullTest null_() { assertTrue(nullArger.callWithNull(null)); } + public void testStructSlice() { + Node a = Testpkg.newNode("A"); + Node[] nodes = Testpkg.repeatNode(a, 3); + assertEquals("want three nodes", 3, nodes.length); + for (Node n : nodes) { + assertEquals("want the node we passed in", a, n); + } + assertEquals("want the names of the nodes", "A,A,A", Testpkg.nodeNames(nodes)); + assertEquals("want no names for an empty slice", "", Testpkg.nodeNames(new Node[0])); + assertEquals("want no names for a nil slice", "", Testpkg.nodeNames(null)); + assertEquals("want a nil element to be reported", "", Testpkg.nodeNames(new Node[]{null})); + assertEquals("want an empty slice back", 0, Testpkg.repeatNode(a, 0).length); + + // Slices long enough to overflow the JNI local reference table if + // references were leaked. + Node[] many = Testpkg.repeatNode(a, 1000); + assertEquals("want a thousand nodes", 1000, many.length); + assertEquals("want a thousand names", 2*many.length - 1, Testpkg.nodeNames(many).length()); + } + + public void testStructSliceCallback() { + String names = Testpkg.callNodeSlicer(new NodeSlicer() { + @Override public Node[] slice(Node[] nodes) { + Node[] res = Arrays.copyOf(nodes, nodes.length + 1); + res[nodes.length] = Testpkg.newNode("B"); + return res; + } + }, Testpkg.newNode("A")); + assertEquals("want the nodes to survive the round trip", "A,A,A,B", names); + + Node a = Testpkg.newNode("A"); + assertEquals("want a null element to be passed on", ",A", Testpkg.callNodeSlicer(new NodeSlicer() { + @Override public Node[] slice(Node[] nodes) { + return new Node[]{null, nodes[0]}; + } + }, a)); + assertEquals("want an empty array to be passed on", "", Testpkg.callNodeSlicer(new NodeSlicer() { + @Override public Node[] slice(Node[] nodes) { + return new Node[0]; + } + }, a)); + assertEquals("want a null array to be passed on", "", Testpkg.callNodeSlicer(new NodeSlicer() { + @Override public Node[] slice(Node[] nodes) { + return null; + } + }, a)); + } + public void testPassByteArray() { Testpkg.passByteArray(new B() { @Override public void b(byte[] b) { diff --git a/bind/java/seq_android.c.support b/bind/java/seq_android.c.support index e8307e0c7..eadee1e4f 100644 --- a/bind/java/seq_android.c.support +++ b/bind/java/seq_android.c.support @@ -93,17 +93,24 @@ jbyteArray go_seq_to_java_bytearray(JNIEnv *env, nbyteslice s, int copy) { return res; } -jobjectArray go_seq_to_java_objectarray(JNIEnv *env, nrefnumslice arr) { +// go_seq_to_java_objectarray converts a slice of refnums to an array of +// proxies of the class proxy_class. The refnums are freed. +jobjectArray go_seq_to_java_objectarray(JNIEnv *env, nrefnumslice arr, jclass proxy_class, jmethodID proxy_cons) { if (arr.ptr == NULL) { return NULL; } - jobjectArray res = (*env)->NewObjectArray(env, arr.len, (*env)->FindClass(env, "java/lang/Object"), NULL); + jobjectArray res = (*env)->NewObjectArray(env, arr.len, proxy_class, NULL); if (res == NULL) { LOG_FATAL("NewObjectArray failed"); } - for (int i = 0; i < arr.len; i++) { - (*env)->SetObjectArrayElement(env, res, i, go_seq_from_refnum(env, arr.ptr[i])); + for (jsize i = 0; i < arr.len; i++) { + jobject o = go_seq_from_refnum(env, arr.ptr[i], proxy_class, proxy_cons); + (*env)->SetObjectArrayElement(env, res, i, o); + // The array holds the only reference we need; drop the local one to + // avoid overflowing the local reference table for large arrays. + (*env)->DeleteLocalRef(env, o); } + free(arr.ptr); return res; } @@ -238,31 +245,27 @@ nbyteslice go_seq_from_java_bytearray(JNIEnv *env, jbyteArray arr, int copy) { return res; } +// go_seq_from_java_objectarray converts an array of proxies to a slice of +// refnums. The returned slice is freed by the Go side. nrefnumslice go_seq_from_java_objectarray(JNIEnv *env, jobjectArray arr) { struct nrefnumslice res = {NULL, 0}; if (arr == NULL) { return res; } - jsize len = (*env)->GetArrayLength(env, arr); - if (len == 0) { - return res; - } - jint *ptr = (jint *)(*env)->GetPrimitiveArrayCritical(env, arr, NULL); - if (ptr == NULL) { - LOG_FATAL("GetPrimitiveArrayCritical failed"); - } - void *refnums = (void *)malloc(len * sizeof(jint)); + // Allocate at least one element so that an empty array is distinguishable + // from a null array. + int32_t *refnums = (int32_t *)malloc(len == 0 ? 1 : len * sizeof(int32_t)); if (refnums == NULL) { LOG_FATAL("malloc failed"); } - // convert to refnums - for (int i = 0; i < len; i++) { - refnums[i] = go_seq_to_refnum(env, (*env)->GetObjectArrayElement(env, arr, i)); + for (jsize i = 0; i < len; i++) { + jobject o = (*env)->GetObjectArrayElement(env, arr, i); + refnums[i] = go_seq_to_refnum(env, o); + (*env)->DeleteLocalRef(env, o); } res.ptr = refnums; res.len = len; - (*env)->ReleasePrimitiveArrayCritical(env, arr, ptr, JNI_ABORT); return res; } diff --git a/bind/java/seq_android.go.support b/bind/java/seq_android.go.support index a83229268..103014d8f 100644 --- a/bind/java/seq_android.go.support +++ b/bind/java/seq_android.go.support @@ -80,6 +80,39 @@ func fromSlice(s []byte, cpy bool) C.nbyteslice { return C.nbyteslice{ptr: unsafe.Pointer(ptr), len: n} } +// fromRefSlice converts a slice of refnums to a nrefnumslice. The returned +// nrefnumslice points to C memory freed by go_seq_to_java_objectarray. A nil +// slice is converted to a nil pointer, an empty slice is not. +func fromRefSlice(refs []int32) C.nrefnumslice { + if refs == nil { + return C.nrefnumslice{} + } + // Allocate at least one element so that an empty slice is distinguishable + // from a nil slice. + sz := C.size_t(len(refs)) * C.size_t(unsafe.Sizeof(C.int32_t(0))) + if sz == 0 { + sz = 1 + } + ptr := (*C.int32_t)(C.malloc(sz)) + if ptr == nil { + panic("fromRefSlice: malloc failed") + } + copy(unsafe.Slice((*int32)(unsafe.Pointer(ptr)), len(refs)), refs) + return C.nrefnumslice{ptr: ptr, len: C.jsize(len(refs))} +} + +// toRefSlice takes a nrefnumslice created by go_seq_from_java_objectarray +// and returns its refnums as a Go slice. The C memory is freed. +func toRefSlice(s C.nrefnumslice) []int32 { + if s.ptr == nil { + return nil + } + refs := make([]int32, s.len) + copy(refs, unsafe.Slice((*int32)(unsafe.Pointer(s.ptr)), s.len)) + C.free(unsafe.Pointer(s.ptr)) + return refs +} + // toSlice takes a nbyteslice (jbyteArray) and returns a byte slice // with the data. If cpy is set, the slice contains a copy of the data and is // freed. diff --git a/bind/java/seq_android.h b/bind/java/seq_android.h index 7b8d3e144..3865d6771 100644 --- a/bind/java/seq_android.h +++ b/bind/java/seq_android.h @@ -30,8 +30,11 @@ typedef struct nbyteslice { void *ptr; jsize len; } nbyteslice; +// nrefnumslice is a slice of reference numbers, used to pass slices of +// bound types across the language barrier. The memory pointed to by ptr is +// always owned by the receiver, which frees it after conversion. typedef struct nrefnumslice { - void *ptr; + int32_t *ptr; jsize len; } nrefnumslice; typedef jlong nint; @@ -51,7 +54,7 @@ extern jobject go_seq_get_exception(JNIEnv *env); extern jbyteArray go_seq_to_java_bytearray(JNIEnv *env, nbyteslice s, int copy); extern nbyteslice go_seq_from_java_bytearray(JNIEnv *env, jbyteArray s, int copy); -extern jobjectArray go_seq_to_java_objectarray(JNIEnv *env, nrefnumslice arr); +extern jobjectArray go_seq_to_java_objectarray(JNIEnv *env, nrefnumslice arr, jclass proxy_class, jmethodID proxy_cons); extern nrefnumslice go_seq_from_java_objectarray(JNIEnv *env, jobjectArray arr); extern void go_seq_release_byte_array(JNIEnv *env, jbyteArray arr, jbyte* ptr); diff --git a/bind/objc/SeqTest.m b/bind/objc/SeqTest.m index 257d8b1fa..b5967cdf8 100644 --- a/bind/objc/SeqTest.m +++ b/bind/objc/SeqTest.m @@ -115,6 +115,33 @@ - (BOOL)emptyError:(NSError **)error { } @end +// Objective-C implementation of testpkg.NodeSlicer. The result of slice: +// depends on mode, to cover the ways an array can be returned to Go. +@interface NodeSlicer: NSObject { +} +@property int32_t mode; + +@end + +@implementation NodeSlicer { +} +@synthesize mode; + +- (NSArray *)slice:(NSArray *)nodes { + switch (self.mode) { + case 1: + return @[[NSNull null], nodes[0]]; + case 2: + return @[]; + case 3: + return NULL; + } + NSMutableArray *res = [nodes mutableCopy]; + [res addObject:TestpkgNewNode(@"B")]; + return res; +} +@end + @interface tests : XCTestCase @end @@ -215,6 +242,37 @@ - (void)testBytesAppend { XCTAssertEqualObjects(got, want, @"want %@\nTestpkgBytesAppend(%@, %@) = %@", want, a, b, got); } +- (void)testStructSlice { + TestpkgNode *a = TestpkgNewNode(@"A"); + NSArray *nodes = TestpkgRepeatNode(a, 3); + XCTAssertEqual(nodes.count, 3, @"TestpkgRepeatNode(a, 3).count = %lu; want 3", (unsigned long)nodes.count); + for (TestpkgNode *n in nodes) { + XCTAssertEqualObjects(n, a, @"want the node passed to TestpkgRepeatNode back"); + } + NSString *got = TestpkgNodeNames(nodes); + XCTAssertEqualObjects(got, @"A,A,A", @"TestpkgNodeNames(nodes) = %@; want A,A,A", got); + got = TestpkgNodeNames(@[]); + XCTAssertEqualObjects(got, @"", @"TestpkgNodeNames(@[]) = %@; want the empty string", got); + got = TestpkgNodeNames(NULL); + XCTAssertEqualObjects(got, @"", @"TestpkgNodeNames(NULL) = %@; want the empty string", got); + got = TestpkgNodeNames(@[[NSNull null]]); + XCTAssertEqualObjects(got, @"", @"TestpkgNodeNames(@[NSNull]) = %@; want ", got); + XCTAssertEqual(TestpkgRepeatNode(a, 0).count, 0, @"want an empty slice back"); + NSArray *many = TestpkgRepeatNode(a, 1000); + XCTAssertEqual(many.count, 1000, @"TestpkgRepeatNode(a, 1000).count = %lu; want 1000", (unsigned long)many.count); + XCTAssertEqual(TestpkgNodeNames(many).length, 2*many.count-1, @"want a name for every node"); +} + +- (void)testStructSliceCallback { + NSArray *want = @[@"A,A,A,B", @",A", @"", @""]; + for (int32_t mode = 0; mode < want.count; mode++) { + NodeSlicer *slicer = [[NodeSlicer alloc] init]; + slicer.mode = mode; + NSString *got = TestpkgCallNodeSlicer(slicer, TestpkgNewNode(@"A")); + XCTAssertEqualObjects(got, want[mode], @"TestpkgCallNodeSlicer(mode %d) = %@; want %@", mode, got, want[mode]); + } +} + - (void)testInterface { // Test Go object implementing testpkg.I is handled correctly. id goObj = TestpkgNewI(); diff --git a/bind/objc/seq_darwin.go.support b/bind/objc/seq_darwin.go.support index 0b4e5de7b..f1c704062 100644 --- a/bind/objc/seq_darwin.go.support +++ b/bind/objc/seq_darwin.go.support @@ -73,6 +73,39 @@ func fromSlice(s []byte, cpy bool) C.nbyteslice { return C.nbyteslice{ptr: ptr, len: n} } +// fromRefSlice converts a slice of refnums to a nrefnumslice. The returned +// nrefnumslice points to C memory freed by go_seq_to_objc_objectarray. A nil +// slice is converted to a nil pointer, an empty slice is not. +func fromRefSlice(refs []int32) C.nrefnumslice { + if refs == nil { + return C.nrefnumslice{} + } + // Allocate at least one element so that an empty slice is distinguishable + // from a nil slice. + sz := C.size_t(len(refs)) * C.size_t(unsafe.Sizeof(C.int32_t(0))) + if sz == 0 { + sz = 1 + } + ptr := (*C.int32_t)(C.malloc(sz)) + if ptr == nil { + panic("fromRefSlice: malloc failed") + } + copy(unsafe.Slice((*int32)(unsafe.Pointer(ptr)), len(refs)), refs) + return C.nrefnumslice{ptr: ptr, len: C.int(len(refs))} +} + +// toRefSlice takes a nrefnumslice created by go_seq_from_objc_objectarray +// and returns its refnums as a Go slice. The C memory is freed. +func toRefSlice(s C.nrefnumslice) []int32 { + if s.ptr == nil { + return nil + } + refs := make([]int32, s.len) + copy(refs, unsafe.Slice((*int32)(unsafe.Pointer(s.ptr)), s.len)) + C.free(unsafe.Pointer(s.ptr)) + return refs +} + // toSlice takes a nbyteslice and returns a byte slice with the data. If cpy is // set, the slice contains a copy of the data. If not, the generated Go code // calls releaseByteSlice after use. diff --git a/bind/objc/seq_darwin.h b/bind/objc/seq_darwin.h index 1b53ffbf2..e7bd48fe5 100644 --- a/bind/objc/seq_darwin.h +++ b/bind/objc/seq_darwin.h @@ -34,9 +34,12 @@ typedef struct nbyteslice { void *ptr; int len; } nbyteslice; +// nrefnumslice is a slice of reference numbers, used to pass slices of +// bound types across the language barrier. The memory pointed to by ptr is +// always owned by the receiver, which frees it after conversion. typedef struct nrefnumslice { - void *ptr; - int len; + int32_t *ptr; + int len; } nrefnumslice; typedef int nint; @@ -63,7 +66,7 @@ extern nrefnumslice go_seq_from_objc_objectarray(NSArray *arr); extern nstring go_seq_from_objc_string(NSString *s); extern NSData *go_seq_to_objc_bytearray(nbyteslice, int copy); -extern NSArray *go_seq_to_objc_objectarray(nrefnumslice arr); +extern NSArray *go_seq_to_objc_objectarray(nrefnumslice arr, Class proxy_class); extern NSString *go_seq_to_objc_string(nstring str); #endif // __GO_SEQ_DARWIN_HDR__ diff --git a/bind/objc/seq_darwin.m.support b/bind/objc/seq_darwin.m.support index 917a4efab..ff302fec3 100644 --- a/bind/objc/seq_darwin.m.support +++ b/bind/objc/seq_darwin.m.support @@ -136,6 +136,30 @@ NSData *go_seq_to_objc_bytearray(nbyteslice s, int copy) { return [NSData dataWithBytesNoCopy:s.ptr length:s.len freeWhenDone:freeWhenDone]; } +// go_seq_to_objc_objectarray converts a slice of refnums to an array of +// proxies of the class proxy_class. Nil elements are represented by NSNull. +// The refnums are freed. +NSArray *go_seq_to_objc_objectarray(nrefnumslice arr, Class proxy_class) { + if (arr.ptr == NULL) { + return NULL; + } + NSMutableArray *res = [NSMutableArray arrayWithCapacity:arr.len]; + for (int i = 0; i < arr.len; i++) { + GoSeqRef *ref = go_seq_from_refnum(arr.ptr[i]); + if (ref == nil) { + [res addObject:[NSNull null]]; + continue; + } + id obj = ref.obj; + if (obj == nil) { + obj = [[proxy_class alloc] initWithRef:ref]; + } + [res addObject:obj]; + } + free(arr.ptr); + return res; +} + NSString *go_seq_to_objc_string(nstring str) { if (str.len == 0) { // empty string. return @""; @@ -206,6 +230,36 @@ nbyteslice go_seq_from_objc_bytearray(NSData *data, int copy) { return res; } +// go_seq_from_objc_objectarray converts an array of proxies to a slice of +// refnums. The returned slice is freed by the Go side. +nrefnumslice go_seq_from_objc_objectarray(NSArray *arr) { + struct nrefnumslice res = {NULL, 0}; + if (arr == nil) { + return res; + } + int len = arr.count; + // Allocate at least one element so that an empty array is distinguishable + // from a nil array. + int32_t *refnums = (int32_t *)malloc(len == 0 ? 1 : len * sizeof(int32_t)); + if (refnums == NULL) { + LOG_FATAL(@"malloc failed"); + } + for (int i = 0; i < len; i++) { + id obj = arr[i]; + if ([obj isKindOfClass:[NSNull class]]) { + refnums[i] = go_seq_to_refnum(nil); + } else if ([obj conformsToProtocol:@protocol(goSeqRefInterface)]) { + id obj_proxy = (id)obj; + refnums[i] = go_seq_go_to_refnum(obj_proxy._ref); + } else { + refnums[i] = go_seq_to_refnum(obj); + } + } + res.ptr = refnums; + res.len = len; + return res; +} + nstring go_seq_from_objc_string(NSString *s) { nstring res = {NULL, 0}; int len = [s lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; diff --git a/bind/testdata/structs.go b/bind/testdata/structs.go index 27f11ca04..457fb9b23 100644 --- a/bind/testdata/structs.go +++ b/bind/testdata/structs.go @@ -26,11 +26,7 @@ func IdentityWithError(s *S) (*S, error) { } func (s *S) Repeat(n int) []*S { - t := make([]*S, n) - for i := range t { - t[i] = s - } - return t + return Repeat(s, n) } func (s *S) RepeatWithError(n int) ([]*S, error) { @@ -49,12 +45,16 @@ func RepeatWithError(s *S, n int) ([]*S, error) { return Repeat(s, n), nil } -func FirstSum(s []*S) float64 { - return s[0].Sum() +func SumAll(s []*S) float64 { + var sum float64 + for _, e := range s { + sum += e.Sum() + } + return sum } -func FirstSumWithError(s []*S) (float64, error) { - return s[0].Sum(), nil +func SumAllWithError(s []*S) (float64, error) { + return SumAll(s), nil } type ( @@ -62,6 +62,11 @@ type ( I interface { M() } + // Slicer is implemented by both Go and foreign code, exercising + // slices of structs in both directions. + Slicer interface { + Slice(s []*S) []*S + } ) func (s *S2) M() { @@ -72,7 +77,9 @@ func (_ *S2) String() string { } // Structs is a struct with the same name as its package. -type Structs struct{} +type Structs struct { + Elems []*S +} func (_ *Structs) M() { } diff --git a/bind/testdata/structs.go.golden b/bind/testdata/structs.go.golden index fb50778ae..a9b7ba6c8 100644 --- a/bind/testdata/structs.go.golden +++ b/bind/testdata/structs.go.golden @@ -2,7 +2,7 @@ // Package main is an autogenerated binder stub for package structs. // -// autogenerated by gobind -lang=go structs +// autogenerated by gobind -lang=go structs package main /* @@ -68,6 +68,50 @@ func proxystructs_S_Identity(refnum C.int32_t) (C.int32_t, C.int32_t) { return _res_0, _res_1 } +//export proxystructs_S_Repeat +func proxystructs_S_Repeat(refnum C.int32_t, param_n C.nint) C.nrefnumslice { + ref := _seq.FromRefNum(int32(refnum)) + v := ref.Get().(*structs.S) + _param_n := int(param_n) + res_0 := v.Repeat(_param_n) + var _res_0_refs []int32 + if res_0 != nil { + _res_0_refs = make([]int32, len(res_0)) + for i, e := range res_0 { + _res_0_refs[i] = _seq.NullRefNum + if e != nil { + _res_0_refs[i] = _seq.ToRefNum(e) + } + } + } + _res_0 := fromRefSlice(_res_0_refs) + return _res_0 +} + +//export proxystructs_S_RepeatWithError +func proxystructs_S_RepeatWithError(refnum C.int32_t, param_n C.nint) (C.nrefnumslice, C.int32_t) { + ref := _seq.FromRefNum(int32(refnum)) + v := ref.Get().(*structs.S) + _param_n := int(param_n) + res_0, res_1 := v.RepeatWithError(_param_n) + var _res_0_refs []int32 + if res_0 != nil { + _res_0_refs = make([]int32, len(res_0)) + for i, e := range res_0 { + _res_0_refs[i] = _seq.NullRefNum + if e != nil { + _res_0_refs[i] = _seq.ToRefNum(e) + } + } + } + _res_0 := fromRefSlice(_res_0_refs) + var _res_1 C.int32_t = _seq.NullRefNum + if res_1 != nil { + _res_1 = C.int32_t(_seq.ToRefNum(res_1)) + } + return _res_0, _res_1 +} + //export proxystructs_S_Sum func proxystructs_S_Sum(refnum C.int32_t) C.double { ref := _seq.FromRefNum(int32(refnum)) @@ -103,6 +147,40 @@ func new_structs_S2() C.int32_t { return C.int32_t(_seq.ToRefNum(new(structs.S2))) } +//export proxystructs_Structs_Elems_Set +func proxystructs_Structs_Elems_Set(refnum C.int32_t, v C.nrefnumslice) { + ref := _seq.FromRefNum(int32(refnum)) + var _v []*structs.S + if _v_refs := toRefSlice(v); _v_refs != nil { + _v = make([]*structs.S, len(_v_refs)) + for i, refnum := range _v_refs { + // Must be a Go object + if ref := _seq.FromRefNum(refnum); ref != nil { + _v[i] = ref.Get().(*structs.S) + } + } + } + ref.Get().(*structs.Structs).Elems = _v +} + +//export proxystructs_Structs_Elems_Get +func proxystructs_Structs_Elems_Get(refnum C.int32_t) C.nrefnumslice { + ref := _seq.FromRefNum(int32(refnum)) + v := ref.Get().(*structs.Structs).Elems + var _v_refs []int32 + if v != nil { + _v_refs = make([]int32, len(v)) + for i, e := range v { + _v_refs[i] = _seq.NullRefNum + if e != nil { + _v_refs[i] = _seq.ToRefNum(e) + } + } + } + _v := fromRefSlice(_v_refs) + return _v +} + //export proxystructs_Structs_M func proxystructs_Structs_M(refnum C.int32_t) { ref := _seq.FromRefNum(int32(refnum)) @@ -132,6 +210,67 @@ func (p *proxystructs_I) M() { C.cproxystructs_I_M(C.int32_t(p.Bind_proxy_refnum__())) } +//export proxystructs_Slicer_Slice +func proxystructs_Slicer_Slice(refnum C.int32_t, param_s C.nrefnumslice) C.nrefnumslice { + ref := _seq.FromRefNum(int32(refnum)) + v := ref.Get().(structs.Slicer) + var _param_s []*structs.S + if _param_s_refs := toRefSlice(param_s); _param_s_refs != nil { + _param_s = make([]*structs.S, len(_param_s_refs)) + for i, refnum := range _param_s_refs { + // Must be a Go object + if ref := _seq.FromRefNum(refnum); ref != nil { + _param_s[i] = ref.Get().(*structs.S) + } + } + } + res_0 := v.Slice(_param_s) + var _res_0_refs []int32 + if res_0 != nil { + _res_0_refs = make([]int32, len(res_0)) + for i, e := range res_0 { + _res_0_refs[i] = _seq.NullRefNum + if e != nil { + _res_0_refs[i] = _seq.ToRefNum(e) + } + } + } + _res_0 := fromRefSlice(_res_0_refs) + return _res_0 +} + +type proxystructs_Slicer _seq.Ref + +func (p *proxystructs_Slicer) Bind_proxy_refnum__() int32 { + return (*_seq.Ref)(p).Bind_IncNum() +} + +func (p *proxystructs_Slicer) Slice(param_s []*structs.S) []*structs.S { + var _param_s_refs []int32 + if param_s != nil { + _param_s_refs = make([]int32, len(param_s)) + for i, e := range param_s { + _param_s_refs[i] = _seq.NullRefNum + if e != nil { + _param_s_refs[i] = _seq.ToRefNum(e) + } + } + } + _param_s := fromRefSlice(_param_s_refs) + res := C.cproxystructs_Slicer_Slice(C.int32_t(p.Bind_proxy_refnum__()), _param_s) + var _res []*structs.S + if _res_refs := toRefSlice(res); _res_refs != nil { + _res = make([]*structs.S, len(_res_refs)) + for i, refnum := range _res_refs { + // Must be a Go object + if ref := _seq.FromRefNum(refnum); ref != nil { + _res[i] = ref.Get().(*structs.S) + } + } + } + return _res +} + //export proxystructs__Identity func proxystructs__Identity(param_s C.int32_t) C.int32_t { // Must be a Go object @@ -165,3 +304,91 @@ func proxystructs__IdentityWithError(param_s C.int32_t) (C.int32_t, C.int32_t) { } return _res_0, _res_1 } + +//export proxystructs__Repeat +func proxystructs__Repeat(param_s C.int32_t, param_n C.nint) C.nrefnumslice { + // Must be a Go object + var _param_s *structs.S + if _param_s_ref := _seq.FromRefNum(int32(param_s)); _param_s_ref != nil { + _param_s = _param_s_ref.Get().(*structs.S) + } + _param_n := int(param_n) + res_0 := structs.Repeat(_param_s, _param_n) + var _res_0_refs []int32 + if res_0 != nil { + _res_0_refs = make([]int32, len(res_0)) + for i, e := range res_0 { + _res_0_refs[i] = _seq.NullRefNum + if e != nil { + _res_0_refs[i] = _seq.ToRefNum(e) + } + } + } + _res_0 := fromRefSlice(_res_0_refs) + return _res_0 +} + +//export proxystructs__RepeatWithError +func proxystructs__RepeatWithError(param_s C.int32_t, param_n C.nint) (C.nrefnumslice, C.int32_t) { + // Must be a Go object + var _param_s *structs.S + if _param_s_ref := _seq.FromRefNum(int32(param_s)); _param_s_ref != nil { + _param_s = _param_s_ref.Get().(*structs.S) + } + _param_n := int(param_n) + res_0, res_1 := structs.RepeatWithError(_param_s, _param_n) + var _res_0_refs []int32 + if res_0 != nil { + _res_0_refs = make([]int32, len(res_0)) + for i, e := range res_0 { + _res_0_refs[i] = _seq.NullRefNum + if e != nil { + _res_0_refs[i] = _seq.ToRefNum(e) + } + } + } + _res_0 := fromRefSlice(_res_0_refs) + var _res_1 C.int32_t = _seq.NullRefNum + if res_1 != nil { + _res_1 = C.int32_t(_seq.ToRefNum(res_1)) + } + return _res_0, _res_1 +} + +//export proxystructs__SumAll +func proxystructs__SumAll(param_s C.nrefnumslice) C.double { + var _param_s []*structs.S + if _param_s_refs := toRefSlice(param_s); _param_s_refs != nil { + _param_s = make([]*structs.S, len(_param_s_refs)) + for i, refnum := range _param_s_refs { + // Must be a Go object + if ref := _seq.FromRefNum(refnum); ref != nil { + _param_s[i] = ref.Get().(*structs.S) + } + } + } + res_0 := structs.SumAll(_param_s) + _res_0 := C.double(res_0) + return _res_0 +} + +//export proxystructs__SumAllWithError +func proxystructs__SumAllWithError(param_s C.nrefnumslice) (C.double, C.int32_t) { + var _param_s []*structs.S + if _param_s_refs := toRefSlice(param_s); _param_s_refs != nil { + _param_s = make([]*structs.S, len(_param_s_refs)) + for i, refnum := range _param_s_refs { + // Must be a Go object + if ref := _seq.FromRefNum(refnum); ref != nil { + _param_s[i] = ref.Get().(*structs.S) + } + } + } + res_0, res_1 := structs.SumAllWithError(_param_s) + _res_0 := C.double(res_0) + var _res_1 C.int32_t = _seq.NullRefNum + if res_1 != nil { + _res_1 = C.int32_t(_seq.ToRefNum(res_1)) + } + return _res_0, _res_1 +} diff --git a/bind/testdata/structs.java.c.golden b/bind/testdata/structs.java.c.golden index 62b8f5af8..a0da796ed 100644 --- a/bind/testdata/structs.java.c.golden +++ b/bind/testdata/structs.java.c.golden @@ -13,6 +13,9 @@ jclass proxy_class_structs_I; jmethodID proxy_class_structs_I_cons; static jmethodID mid_I_M; +jclass proxy_class_structs_Slicer; +jmethodID proxy_class_structs_Slicer_cons; +static jmethodID mid_Slicer_Slice; jclass proxy_class_structs_S; jmethodID proxy_class_structs_S_cons; jclass proxy_class_structs_S2; @@ -38,6 +41,12 @@ Java_structs_Structs__1init(JNIEnv *env, jclass _unused) { clazz = (*env)->FindClass(env, "structs/I"); mid_I_M = (*env)->GetMethodID(env, clazz, "m", "()V"); + clazz = (*env)->FindClass(env, "structs/Structs$proxySlicer"); + proxy_class_structs_Slicer = (*env)->NewGlobalRef(env, clazz); + proxy_class_structs_Slicer_cons = (*env)->GetMethodID(env, clazz, "", "(I)V"); + clazz = (*env)->FindClass(env, "structs/Slicer"); + mid_Slicer_Slice = (*env)->GetMethodID(env, clazz, "slice", "([Lstructs/S;)[Lstructs/S;"); + } JNIEXPORT jobject JNICALL @@ -58,6 +67,44 @@ Java_structs_Structs_identityWithError(JNIEnv* env, jclass _clazz, jobject s) { return _r0; } +JNIEXPORT jobjectArray JNICALL +Java_structs_Structs_repeat(JNIEnv* env, jclass _clazz, jobject s, jlong n) { + int32_t _s = go_seq_to_refnum(env, s); + nint _n = (nint)n; + nrefnumslice r0 = proxystructs__Repeat(_s, _n); + jobjectArray _r0 = go_seq_to_java_objectarray(env, r0, proxy_class_structs_S, proxy_class_structs_S_cons); + return _r0; +} + +JNIEXPORT jobjectArray JNICALL +Java_structs_Structs_repeatWithError(JNIEnv* env, jclass _clazz, jobject s, jlong n) { + int32_t _s = go_seq_to_refnum(env, s); + nint _n = (nint)n; + struct proxystructs__RepeatWithError_return res = proxystructs__RepeatWithError(_s, _n); + jobjectArray _r0 = go_seq_to_java_objectarray(env, res.r0, proxy_class_structs_S, proxy_class_structs_S_cons); + jobject _r1 = go_seq_from_refnum(env, res.r1, proxy_class__error, proxy_class__error_cons); + go_seq_maybe_throw_exception(env, _r1); + return _r0; +} + +JNIEXPORT jdouble JNICALL +Java_structs_Structs_sumAll(JNIEnv* env, jclass _clazz, jobjectArray s) { + nrefnumslice _s = go_seq_from_java_objectarray(env, s); + double r0 = proxystructs__SumAll(_s); + jdouble _r0 = (jdouble)r0; + return _r0; +} + +JNIEXPORT jdouble JNICALL +Java_structs_Structs_sumAllWithError(JNIEnv* env, jclass _clazz, jobjectArray s) { + nrefnumslice _s = go_seq_from_java_objectarray(env, s); + struct proxystructs__SumAllWithError_return res = proxystructs__SumAllWithError(_s); + jdouble _r0 = (jdouble)res.r0; + jobject _r1 = go_seq_from_refnum(env, res.r1, proxy_class__error, proxy_class__error_cons); + go_seq_maybe_throw_exception(env, _r1); + return _r0; +} + JNIEXPORT jint JNICALL Java_structs_S__1_1New(JNIEnv *env, jclass clazz) { return new_structs_S(); @@ -73,6 +120,26 @@ Java_structs_S_identity(JNIEnv* env, jobject __this__) { return _r0; } +JNIEXPORT jobjectArray JNICALL +Java_structs_S_repeat(JNIEnv* env, jobject __this__, jlong n) { + int32_t o = go_seq_to_refnum_go(env, __this__); + nint _n = (nint)n; + nrefnumslice r0 = proxystructs_S_Repeat(o, _n); + jobjectArray _r0 = go_seq_to_java_objectarray(env, r0, proxy_class_structs_S, proxy_class_structs_S_cons); + return _r0; +} + +JNIEXPORT jobjectArray JNICALL +Java_structs_S_repeatWithError(JNIEnv* env, jobject __this__, jlong n) { + int32_t o = go_seq_to_refnum_go(env, __this__); + nint _n = (nint)n; + struct proxystructs_S_RepeatWithError_return res = proxystructs_S_RepeatWithError(o, _n); + jobjectArray _r0 = go_seq_to_java_objectarray(env, res.r0, proxy_class_structs_S, proxy_class_structs_S_cons); + jobject _r1 = go_seq_from_refnum(env, res.r1, proxy_class__error, proxy_class__error_cons); + go_seq_maybe_throw_exception(env, _r1); + return _r0; +} + JNIEXPORT jdouble JNICALL Java_structs_S_sum(JNIEnv* env, jobject __this__) { int32_t o = go_seq_to_refnum_go(env, __this__); @@ -141,6 +208,21 @@ Java_structs_Structs_1_m(JNIEnv* env, jobject __this__) { proxystructs_Structs_M(o); } +JNIEXPORT void JNICALL +Java_structs_Structs_1_setElems(JNIEnv *env, jobject this, jobjectArray v) { + int32_t o = go_seq_to_refnum_go(env, this); + nrefnumslice _v = go_seq_from_java_objectarray(env, v); + proxystructs_Structs_Elems_Set(o, _v); +} + +JNIEXPORT jobjectArray JNICALL +Java_structs_Structs_1_getElems(JNIEnv *env, jobject this) { + int32_t o = go_seq_to_refnum_go(env, this); + nrefnumslice r0 = proxystructs_Structs_Elems_Get(o); + jobjectArray _r0 = go_seq_to_java_objectarray(env, r0, proxy_class_structs_S, proxy_class_structs_S_cons); + return _r0; +} + JNIEXPORT void JNICALL Java_structs_Structs_00024proxyI_m(JNIEnv* env, jobject __this__) { int32_t o = go_seq_to_refnum_go(env, __this__); @@ -154,3 +236,22 @@ void cproxystructs_I_M(int32_t refnum) { go_seq_pop_local_frame(env); } +JNIEXPORT jobjectArray JNICALL +Java_structs_Structs_00024proxySlicer_slice(JNIEnv* env, jobject __this__, jobjectArray s) { + int32_t o = go_seq_to_refnum_go(env, __this__); + nrefnumslice _s = go_seq_from_java_objectarray(env, s); + nrefnumslice r0 = proxystructs_Slicer_Slice(o, _s); + jobjectArray _r0 = go_seq_to_java_objectarray(env, r0, proxy_class_structs_S, proxy_class_structs_S_cons); + return _r0; +} + +nrefnumslice cproxystructs_Slicer_Slice(int32_t refnum, nrefnumslice s) { + JNIEnv *env = go_seq_push_local_frame(1); + jobject o = go_seq_from_refnum(env, refnum, proxy_class_structs_Slicer, proxy_class_structs_Slicer_cons); + jobjectArray _s = go_seq_to_java_objectarray(env, s, proxy_class_structs_S, proxy_class_structs_S_cons); + jobjectArray res = (*env)->CallObjectMethod(env, o, mid_Slicer_Slice, _s); + nrefnumslice _res = go_seq_from_java_objectarray(env, res); + go_seq_pop_local_frame(env); + return _res; +} + diff --git a/bind/testdata/structs.java.golden b/bind/testdata/structs.java.golden index 9b8d0743d..4cffae0cd 100644 --- a/bind/testdata/structs.java.golden +++ b/bind/testdata/structs.java.golden @@ -133,22 +133,35 @@ public final class Structs_ implements Seq.Proxy, I { private static native int __New(); + public final native S[] getElems(); + public final native void setElems(S[] v); + public native void m(); @Override public boolean equals(Object o) { if (o == null || !(o instanceof Structs_)) { return false; } Structs_ that = (Structs_)o; + S[] thisElems = getElems(); + S[] thatElems = that.getElems(); + if (thisElems == null) { + if (thatElems != null) { + return false; + } + } else if (!thisElems.equals(thatElems)) { + return false; + } return true; } @Override public int hashCode() { - return java.util.Arrays.hashCode(new Object[] {}); + return java.util.Arrays.hashCode(new Object[] {getElems()}); } @Override public String toString() { StringBuilder b = new StringBuilder(); b.append("Structs_").append("{"); + b.append("Elems:").append(getElems()).append(","); return b.append("}").toString(); } } @@ -169,6 +182,24 @@ public interface I { // Code generated by gobind. DO NOT EDIT. +// Java class structs.Slicer is a proxy for talking to a Go program. +// +// autogenerated by gobind -lang=java structs +package structs; + +import go.Seq; + +/** + * Slicer is implemented by both Go and foreign code, exercising +slices of structs in both directions. + */ +public interface Slicer { + public S[] slice(S[] s); + +} + +// Code generated by gobind. DO NOT EDIT. + // Java class structs.Structs is a proxy for talking to a Go program. // // autogenerated by gobind -lang=java structs @@ -178,7 +209,7 @@ import go.Seq; public abstract class Structs { static { - Seq.touch(); // for loading the native library + Seq.touch(); // for loading the native library _init(); } @@ -201,11 +232,24 @@ public abstract class Structs { public native void m(); } + private static final class proxySlicer implements Seq.Proxy, Slicer { + private final int refnum; + + @Override public final int incRefnum() { + Seq.incGoRef(refnum, this); + return refnum; + } + + proxySlicer(int refnum) { this.refnum = refnum; Seq.trackGoRef(refnum, this); } + + public native S[] slice(S[] s); + } + - public static native double firstSum(S[] s); - public static native double firstSumWithError(S[] s) throws Exception; public static native S identity(S s); public static native S identityWithError(S s) throws Exception; public static native S[] repeat(S s, long n); public static native S[] repeatWithError(S s, long n) throws Exception; + public static native double sumAll(S[] s); + public static native double sumAllWithError(S[] s) throws Exception; } diff --git a/bind/testdata/structs.java.h.golden b/bind/testdata/structs.java.h.golden index 85dc84bb0..da9d19b98 100644 --- a/bind/testdata/structs.java.h.golden +++ b/bind/testdata/structs.java.h.golden @@ -14,6 +14,11 @@ extern jmethodID proxy_class_structs_I_cons; void cproxystructs_I_M(int32_t refnum); +extern jclass proxy_class_structs_Slicer; +extern jmethodID proxy_class_structs_Slicer_cons; + +nrefnumslice cproxystructs_Slicer_Slice(int32_t refnum, nrefnumslice s); + extern jclass proxy_class_structs_S; extern jmethodID proxy_class_structs_S_cons; extern jclass proxy_class_structs_S2; diff --git a/bind/testdata/structs.objc.go.h.golden b/bind/testdata/structs.objc.go.h.golden index 41f67cb90..c8da52d79 100644 --- a/bind/testdata/structs.objc.go.h.golden +++ b/bind/testdata/structs.objc.go.h.golden @@ -10,4 +10,6 @@ #include void cproxystructs_I_M(int32_t refnum); +nrefnumslice cproxystructs_Slicer_Slice(int32_t refnum, nrefnumslice s); + #endif diff --git a/bind/testdata/structs.objc.h.golden b/bind/testdata/structs.objc.h.golden index 87ccbc052..3156a5f43 100644 --- a/bind/testdata/structs.objc.h.golden +++ b/bind/testdata/structs.objc.h.golden @@ -16,11 +16,17 @@ @class StructsStructs; @protocol StructsI; @class StructsI; +@protocol StructsSlicer; +@class StructsSlicer; @protocol StructsI - (void)m; @end +@protocol StructsSlicer +- (NSArray* _Nullable)slice:(NSArray* _Nullable)s; +@end + @interface StructsS : NSObject { } @property(strong, readonly) _Nonnull id _ref; @@ -30,6 +36,8 @@ @property (nonatomic) double x; @property (nonatomic) double y; - (StructsS* _Nullable)identity:(NSError* _Nullable* _Nullable)error; +- (NSArray* _Nullable)repeat:(long)n; +- (NSArray* _Nullable)repeatWithError:(long)n error:(NSError* _Nullable* _Nullable)error; - (double)sum; @end @@ -52,6 +60,7 @@ - (nonnull instancetype)initWithRef:(_Nonnull id)ref; - (nonnull instancetype)init; +@property (nonatomic) NSArray* _Nullable elems; - (void)m; @end @@ -59,8 +68,18 @@ FOUNDATION_EXPORT StructsS* _Nullable StructsIdentity(StructsS* _Nullable s); FOUNDATION_EXPORT StructsS* _Nullable StructsIdentityWithError(StructsS* _Nullable s, NSError* _Nullable* _Nullable error); +FOUNDATION_EXPORT NSArray* _Nullable StructsRepeat(StructsS* _Nullable s, long n); + +FOUNDATION_EXPORT NSArray* _Nullable StructsRepeatWithError(StructsS* _Nullable s, long n, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT double StructsSumAll(NSArray* _Nullable s); + +FOUNDATION_EXPORT BOOL StructsSumAllWithError(NSArray* _Nullable s, double* _Nullable ret0_, NSError* _Nullable* _Nullable error); + @class StructsI; +@class StructsSlicer; + @interface StructsI : NSObject { } @property(strong, readonly) _Nonnull id _ref; @@ -69,4 +88,16 @@ FOUNDATION_EXPORT StructsS* _Nullable StructsIdentityWithError(StructsS* _Nullab - (void)m; @end +/** + * Slicer is implemented by both Go and foreign code, exercising +slices of structs in both directions. + */ +@interface StructsSlicer : NSObject { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (NSArray* _Nullable)slice:(NSArray* _Nullable)s; +@end + #endif diff --git a/bind/testdata/structs.objc.m.golden b/bind/testdata/structs.objc.m.golden index c86517413..4416ca11c 100644 --- a/bind/testdata/structs.objc.m.golden +++ b/bind/testdata/structs.objc.m.golden @@ -80,6 +80,36 @@ return _ret0_; } +- (NSArray* _Nullable)repeat:(long)n { + int32_t refnum = go_seq_go_to_refnum(self._ref); + nint _n = (nint)n; + nrefnumslice r0 = proxystructs_S_Repeat(refnum, _n); + NSArray* _ret0_ = go_seq_to_objc_objectarray(r0, [StructsS class]); + return _ret0_; +} + +- (NSArray* _Nullable)repeatWithError:(long)n error:(NSError* _Nullable* _Nullable)error { + int32_t refnum = go_seq_go_to_refnum(self._ref); + nint _n = (nint)n; + struct proxystructs_S_RepeatWithError_return res = proxystructs_S_RepeatWithError(refnum, _n); + NSArray* _ret0_ = go_seq_to_objc_objectarray(res.r0, [StructsS class]); + Universeerror* _error = nil; + GoSeqRef* _error_ref = go_seq_from_refnum(res.r1); + if (_error_ref != NULL) { + _error = _error_ref.obj; + if (_error == nil) { + _error = [[Universeerror alloc] initWithRef:_error_ref]; + } + } + if (_error != nil && error != nil) { + *error = _error; + } + if (_error != nil) { + return nil; + } + return _ret0_; +} + - (double)sum { int32_t refnum = go_seq_go_to_refnum(self._ref); double r0 = proxystructs_S_Sum(refnum); @@ -141,6 +171,19 @@ return self; } +- (NSArray* _Nullable)elems { + int32_t refnum = go_seq_go_to_refnum(self._ref); + nrefnumslice r0 = proxystructs_Structs_Elems_Get(refnum); + NSArray* _r0 = go_seq_to_objc_objectarray(r0, [StructsS class]); + return _r0; +} + +- (void)setElems:(NSArray* _Nullable)v { + int32_t refnum = go_seq_go_to_refnum(self._ref); + nrefnumslice _v = go_seq_from_objc_objectarray(v); + proxystructs_Structs_Elems_Set(refnum, _v); +} + - (void)m { int32_t refnum = go_seq_go_to_refnum(self._ref); proxystructs_Structs_M(refnum); @@ -166,6 +209,26 @@ @end +@implementation StructsSlicer { +} + +- (nonnull instancetype)initWithRef:(id)ref { + self = [super init]; + if (self) { __ref = ref; } + return self; +} + +- (NSArray* _Nullable)slice:(NSArray* _Nullable)s { + int32_t refnum = go_seq_go_to_refnum(self._ref); + nrefnumslice _s = go_seq_from_objc_objectarray(s); + nrefnumslice r0 = proxystructs_Slicer_Slice(refnum, _s); + NSArray* _ret0_ = go_seq_to_objc_objectarray(r0, [StructsS class]); + return _ret0_; +} + +@end + + StructsS* _Nullable StructsIdentity(StructsS* _Nullable s) { int32_t _s; @@ -221,6 +284,74 @@ StructsS* _Nullable StructsIdentityWithError(StructsS* _Nullable s, NSError* _Nu return _ret0_; } +NSArray* _Nullable StructsRepeat(StructsS* _Nullable s, long n) { + int32_t _s; + if ([s conformsToProtocol:@protocol(goSeqRefInterface)]) { + id s_proxy = (id)(s); + _s = go_seq_go_to_refnum(s_proxy._ref); + } else { + _s = go_seq_to_refnum(s); + } + nint _n = (nint)n; + nrefnumslice r0 = proxystructs__Repeat(_s, _n); + NSArray* _ret0_ = go_seq_to_objc_objectarray(r0, [StructsS class]); + return _ret0_; +} + +NSArray* _Nullable StructsRepeatWithError(StructsS* _Nullable s, long n, NSError* _Nullable* _Nullable error) { + int32_t _s; + if ([s conformsToProtocol:@protocol(goSeqRefInterface)]) { + id s_proxy = (id)(s); + _s = go_seq_go_to_refnum(s_proxy._ref); + } else { + _s = go_seq_to_refnum(s); + } + nint _n = (nint)n; + struct proxystructs__RepeatWithError_return res = proxystructs__RepeatWithError(_s, _n); + NSArray* _ret0_ = go_seq_to_objc_objectarray(res.r0, [StructsS class]); + Universeerror* _error = nil; + GoSeqRef* _error_ref = go_seq_from_refnum(res.r1); + if (_error_ref != NULL) { + _error = _error_ref.obj; + if (_error == nil) { + _error = [[Universeerror alloc] initWithRef:_error_ref]; + } + } + if (_error != nil && error != nil) { + *error = _error; + } + if (_error != nil) { + return nil; + } + return _ret0_; +} + +double StructsSumAll(NSArray* _Nullable s) { + nrefnumslice _s = go_seq_from_objc_objectarray(s); + double r0 = proxystructs__SumAll(_s); + double _ret0_ = (double)r0; + return _ret0_; +} + +BOOL StructsSumAllWithError(NSArray* _Nullable s, double* _Nullable ret0_, NSError* _Nullable* _Nullable error) { + nrefnumslice _s = go_seq_from_objc_objectarray(s); + struct proxystructs__SumAllWithError_return res = proxystructs__SumAllWithError(_s); + double _ret0_ = (double)res.r0; + Universeerror* _error = nil; + GoSeqRef* _error_ref = go_seq_from_refnum(res.r1); + if (_error_ref != NULL) { + _error = _error_ref.obj; + if (_error == nil) { + _error = [[Universeerror alloc] initWithRef:_error_ref]; + } + } + *ret0_ = _ret0_; + if (_error != nil && error != nil) { + *error = _error; + } + return (_error == nil); +} + void cproxystructs_I_M(int32_t refnum) { @autoreleasepool { StructsI* o = go_seq_objc_from_refnum(refnum); @@ -228,6 +359,17 @@ void cproxystructs_I_M(int32_t refnum) { } } +nrefnumslice cproxystructs_Slicer_Slice(int32_t refnum, nrefnumslice s) { + @autoreleasepool { + StructsSlicer* o = go_seq_objc_from_refnum(refnum); + NSArray* _s = go_seq_to_objc_objectarray(s, [StructsS class]); + NSArray* _Nullable ret0_; + ret0_ = [o slice:_s]; + nrefnumslice _ret0_ = go_seq_from_objc_objectarray(ret0_); + return _ret0_; + } +} + __attribute__((constructor)) static void init() { init_seq(); } diff --git a/bind/testdata/testpkg/testpkg.go b/bind/testdata/testpkg/testpkg.go index 2df04bdae..f3a5234d9 100644 --- a/bind/testdata/testpkg/testpkg.go +++ b/bind/testdata/testpkg/testpkg.go @@ -297,6 +297,43 @@ func PassByteArray(b B) { b.B([]byte{1, 2, 3, 4}) } +// RepeatNode returns a slice with n copies of the node. +func RepeatNode(n *Node, count int) []*Node { + nodes := make([]*Node, count) + for i := range nodes { + nodes[i] = n + } + return nodes +} + +// NodeNames joins the names of the nodes, using "" for nil elements. +func NodeNames(nodes []*Node) string { + var names string + for i, n := range nodes { + if i > 0 { + names += "," + } + if n == nil { + names += "" + continue + } + names += n.V + } + return names +} + +// NodeSlicer is implemented by foreign code to test passing slices of +// structs to and from a foreign implementation. +type NodeSlicer interface { + Slice(nodes []*Node) []*Node +} + +// CallNodeSlicer passes a slice of copies of n to s and returns the names +// of the nodes it returns. +func CallNodeSlicer(s NodeSlicer, n *Node) string { + return NodeNames(s.Slice(RepeatNode(n, 3))) +} + func GoroutineCallback(r Receiver) { done := make(chan struct{}) go func() { diff --git a/bind/types.go b/bind/types.go index 4642d741a..b7f5cdef8 100644 --- a/bind/types.go +++ b/bind/types.go @@ -176,3 +176,15 @@ func isBytesSlice(t *types.Slice) bool { e, ok := types.Unalias(t.Elem()).(*types.Basic) return ok && e.Kind() == types.Byte } + +// refSliceElem returns the named type N and true if t is a slice of +// pointers to a named type, such as []*N. Such slices are passed across +// the language barrier as slices of reference numbers. +func refSliceElem(t *types.Slice) (*types.Named, bool) { + p, ok := types.Unalias(t.Elem()).(*types.Pointer) + if !ok { + return nil, false + } + n, ok := types.Unalias(p.Elem()).(*types.Named) + return n, ok +} diff --git a/cmd/gobind/doc.go b/cmd/gobind/doc.go index 07b9df6ea..0581ddc65 100644 --- a/cmd/gobind/doc.go +++ b/cmd/gobind/doc.go @@ -167,6 +167,10 @@ Supported types include: - Byte slice types. Note that byte slices are passed by reference, and support mutation. + - Slices of pointers to supported struct types, such as []*T. Unlike + byte slices, they are passed by value; mutating the slice on one + side of the language barrier does not affect the other. + - Any function type all of whose parameters and results have supported types. Functions must return either no results, one result, or two results where the type of the second is