Skip to content

Commit 8ba93b9

Browse files
committed
x/mobile/bind: support slices of structs
Adds code generation for supporting slices of structs in gobind. Fixes golang/go#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.
1 parent 09dbf07 commit 8ba93b9

File tree

9 files changed

+180
-8
lines changed

9 files changed

+180
-8
lines changed

bind/gen.go

+10
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,11 @@ func (g *Generator) cgoType(t types.Type) string {
402402
default:
403403
g.errorf("unsupported slice type: %s", t)
404404
}
405+
case *types.Pointer:
406+
switch e.Elem().(type) {
407+
case *types.Named:
408+
return "nrefnumslice"
409+
}
405410
default:
406411
g.errorf("unsupported slice type: %s", t)
407412
}
@@ -507,6 +512,11 @@ func (g *Generator) isSupported(t types.Type) bool {
507512
switch e := t.Elem().(type) {
508513
case *types.Basic:
509514
return e.Kind() == types.Uint8
515+
case *types.Pointer:
516+
switch f := e.Elem().(type) {
517+
case *types.Named:
518+
return g.validPkg(f.Obj().Pkg())
519+
}
510520
}
511521
case *types.Pointer:
512522
switch t := t.Elem().(type) {

bind/gengo.go

+14
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,13 @@ func (g *goGen) genWrite(toVar, fromVar string, t types.Type, mode varMode) {
119119
default:
120120
g.errorf("unsupported type: %s", t)
121121
}
122+
case *types.Pointer:
123+
switch e.Elem().(type) {
124+
case *types.Named:
125+
g.Printf("%s := toRefNumSlice(%s)\n", toVar, fromVar)
126+
default:
127+
g.errorf("unsupported type: %s", t)
128+
}
122129
default:
123130
g.errorf("unsupported type: %s", t)
124131
}
@@ -403,6 +410,13 @@ func (g *goGen) genRead(toVar, fromVar string, typ types.Type, mode varMode) {
403410
default:
404411
g.errorf("unsupported type: %s", t)
405412
}
413+
case *types.Pointer:
414+
switch e.Elem().(type) {
415+
case *types.Named:
416+
g.Printf("%s := fromRefNumSlice(%s)\n", toVar, fromVar)
417+
default:
418+
g.errorf("unsupported type: %s", t)
419+
}
406420
default:
407421
g.errorf("unsupported type: %s", t)
408422
}

bind/genjava.go

+33-2
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,13 @@ func (j *javaClassInfo) toJavaType(T types.Type) *java.Type {
135135
case types.Uint8: // Byte.
136136
return &java.Type{Kind: java.Array, Elem: &java.Type{Kind: java.Byte}}
137137
}
138+
case *types.Pointer:
139+
switch e.Elem().(type) {
140+
case *types.Named:
141+
if isJavaType(e) {
142+
return &java.Type{Kind: java.Array, Elem: &java.Type{Kind: java.Object, Class: classNameFor(e)}}
143+
}
144+
}
138145
}
139146
return nil
140147
case *types.Named:
@@ -641,8 +648,18 @@ func (g *JavaGen) jniType(T types.Type) string {
641648
return "TODO"
642649
}
643650
case *types.Slice:
644-
return "jbyteArray"
645-
651+
switch e := T.Elem().(type) {
652+
case *types.Basic:
653+
switch e.Kind() {
654+
case types.Uint8: // Byte.
655+
return "jbyteArray"
656+
}
657+
case *types.Pointer:
658+
switch e.Elem().(type) {
659+
case *types.Named:
660+
return "jobjectArray"
661+
}
662+
}
646663
case *types.Pointer:
647664
if _, ok := T.Elem().(*types.Named); ok {
648665
return g.jniType(T.Elem())
@@ -915,6 +932,13 @@ func (g *JavaGen) genJavaToC(varName string, t types.Type, mode varMode) {
915932
default:
916933
g.errorf("unsupported type: %s", t)
917934
}
935+
case *types.Pointer:
936+
switch e.Elem().(type) {
937+
case *types.Named:
938+
g.Printf("nobjectarray _%s = go_seq_from_java_objectarray(env, %s, %d);\n", varName, varName, toCFlag(mode == modeRetained))
939+
default:
940+
g.errorf("unsupported type: %s", t)
941+
}
918942
default:
919943
g.errorf("unsupported type: %s", t)
920944
}
@@ -952,6 +976,13 @@ func (g *JavaGen) genCToJava(toName, fromName string, t types.Type, mode varMode
952976
default:
953977
g.errorf("unsupported type: %s", t)
954978
}
979+
case *types.Pointer:
980+
switch e.Elem().(type) {
981+
case *types.Named:
982+
g.Printf("jobjectArray %s = go_seq_to_java_objectarray(env, %s, %d);\n", toName, fromName, toCFlag(mode == modeRetained))
983+
default:
984+
g.errorf("unsupported type: %s", t)
985+
}
955986
default:
956987
g.errorf("unsupported type: %s", t)
957988
}

bind/genobjc.go

+30-4
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,13 @@ func (g *ObjcGen) genWrite(varName string, t types.Type, mode varMode) {
696696
default:
697697
g.errorf("unsupported type: %s", t)
698698
}
699+
case *types.Pointer:
700+
switch e.Elem().(type) {
701+
case *types.Named:
702+
g.Printf("nrefnumslice _%s = go_seq_from_objc_refnumarray(%s);\n", varName, varName)
703+
default:
704+
g.errorf("unsupported type: %s", t)
705+
}
699706
default:
700707
g.errorf("unsupported type: %s", t)
701708
}
@@ -763,6 +770,13 @@ func (g *ObjcGen) genRead(toName, fromName string, t types.Type, mode varMode) {
763770
default:
764771
g.errorf("unsupported type: %s", t)
765772
}
773+
case *types.Pointer:
774+
switch e.Elem().(type) {
775+
case *types.Named:
776+
g.Printf("NSArray *%s = go_seq_to_objc_refnumarray(%s);\n", toName, fromName)
777+
default:
778+
g.errorf("unsupported type: %s", t)
779+
}
766780
default:
767781
g.errorf("unsupported type: %s", t)
768782
}
@@ -1047,6 +1061,11 @@ func (g *ObjcGen) genRelease(varName string, t types.Type, mode varMode) {
10471061
g.Printf("}\n")
10481062
}
10491063
}
1064+
case *types.Pointer:
1065+
switch e.Elem().(type) {
1066+
case *types.Named:
1067+
g.Printf("free(_%s.ptr);\n", varName)
1068+
}
10501069
}
10511070
}
10521071
}
@@ -1344,10 +1363,17 @@ func (g *ObjcGen) objcType(typ types.Type) string {
13441363
return "TODO"
13451364
}
13461365
case *types.Slice:
1347-
elem := g.objcType(typ.Elem())
1348-
// Special case: NSData seems to be a better option for byte slice.
1349-
if elem == "byte" {
1350-
return "NSData* _Nullable"
1366+
switch e := typ.Elem().(type) {
1367+
case *types.Basic:
1368+
switch e.Kind() {
1369+
case types.Uint8:
1370+
return "NSData* _Nullable"
1371+
}
1372+
case *types.Pointer:
1373+
switch e.Elem().(type) {
1374+
case *types.Named:
1375+
return "NSArray* _Nullable"
1376+
}
13511377
}
13521378
// TODO(hyangah): support other slice types: NSArray or CFArrayRef.
13531379
// Investigate the performance implication.

bind/java/seq_android.c.support

+42
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,20 @@ jbyteArray go_seq_to_java_bytearray(JNIEnv *env, nbyteslice s, int copy) {
9393
return res;
9494
}
9595

96+
jobjectArray go_seq_to_java_objectarray(JNIEnv *env, nrefnumslice arr) {
97+
if (arr.ptr == NULL) {
98+
return NULL;
99+
}
100+
jobjectArray res = (*env)->NewObjectArray(env, arr.len, (*env)->FindClass(env, "java/lang/Object"), NULL);
101+
if (res == NULL) {
102+
LOG_FATAL("NewObjectArray failed");
103+
}
104+
for (int i = 0; i < arr.len; i++) {
105+
(*env)->SetObjectArrayElement(env, res, i, go_seq_from_refnum(env, arr.ptr[i]));
106+
}
107+
return res;
108+
}
109+
96110
#define surr1 0xd800
97111
#define surr2 0xdc00
98112
#define surr3 0xe000
@@ -224,6 +238,34 @@ nbyteslice go_seq_from_java_bytearray(JNIEnv *env, jbyteArray arr, int copy) {
224238
return res;
225239
}
226240

241+
nrefnumslice go_seq_from_java_objectarray(JNIEnv *env, jobjectArray arr) {
242+
struct nrefnumslice res = {NULL, 0};
243+
if (arr == NULL) {
244+
return res;
245+
}
246+
247+
jsize len = (*env)->GetArrayLength(env, arr);
248+
if (len == 0) {
249+
return res;
250+
}
251+
jint *ptr = (jint *)(*env)->GetPrimitiveArrayCritical(env, arr, NULL);
252+
if (ptr == NULL) {
253+
LOG_FATAL("GetPrimitiveArrayCritical failed");
254+
}
255+
void *refnums = (void *)malloc(len * sizeof(jint));
256+
if (refnums == NULL) {
257+
LOG_FATAL("malloc failed");
258+
}
259+
// convert to refnums
260+
for (int i = 0; i < len; i++) {
261+
refnums[i] = go_seq_to_refnum(env, (*env)->GetObjectArrayElement(env, arr, i));
262+
}
263+
res.ptr = refnums;
264+
res.len = len;
265+
(*env)->ReleasePrimitiveArrayCritical(env, arr, ptr, JNI_ABORT);
266+
return res;
267+
}
268+
227269
int32_t go_seq_to_refnum_go(JNIEnv *env, jobject o) {
228270
if (o == NULL) {
229271
return NULL_REFNUM;

bind/java/seq_android.h

+6
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ typedef struct nbyteslice {
3030
void *ptr;
3131
jsize len;
3232
} nbyteslice;
33+
typedef struct nrefnumslice {
34+
void *ptr;
35+
jsize len;
36+
} nrefnumslice;
3337
typedef jlong nint;
3438

3539
extern void go_seq_dec_ref(int32_t ref);
@@ -47,6 +51,8 @@ extern jobject go_seq_get_exception(JNIEnv *env);
4751

4852
extern jbyteArray go_seq_to_java_bytearray(JNIEnv *env, nbyteslice s, int copy);
4953
extern nbyteslice go_seq_from_java_bytearray(JNIEnv *env, jbyteArray s, int copy);
54+
extern jobjectArray go_seq_to_java_objectarray(JNIEnv *env, nrefnumslice arr);
55+
extern nrefnumslice go_seq_from_java_objectarray(JNIEnv *env, jobjectArray arr);
5056
extern void go_seq_release_byte_array(JNIEnv *env, jbyteArray arr, jbyte* ptr);
5157

5258
extern jstring go_seq_to_java_string(JNIEnv *env, nstring str);

bind/objc/seq_darwin.h

+6
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ typedef struct nbyteslice {
3434
void *ptr;
3535
int len;
3636
} nbyteslice;
37+
typedef struct nrefnumslice {
38+
void *ptr;
39+
int len;
40+
} nrefnumslice;
3741
typedef int nint;
3842

3943
extern void init_seq();
@@ -55,9 +59,11 @@ extern GoSeqRef *go_seq_from_refnum(int32_t refnum);
5559
extern id go_seq_objc_from_refnum(int32_t refnum);
5660

5761
extern nbyteslice go_seq_from_objc_bytearray(NSData *data, int copy);
62+
extern nrefnumslice go_seq_from_objc_objectarray(NSArray *arr);
5863
extern nstring go_seq_from_objc_string(NSString *s);
5964

6065
extern NSData *go_seq_to_objc_bytearray(nbyteslice, int copy);
66+
extern NSArray *go_seq_to_objc_objectarray(nrefnumslice arr);
6167
extern NSString *go_seq_to_objc_string(nstring str);
6268

6369
#endif // __GO_SEQ_DARWIN_HDR__

bind/testdata/structs.go

+32
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,38 @@ func IdentityWithError(s *S) (*S, error) {
2525
return s, nil
2626
}
2727

28+
func (s *S) Repeat(n int) []*S {
29+
t := make([]*S, n)
30+
for i := range t {
31+
t[i] = s
32+
}
33+
return t
34+
}
35+
36+
func (s *S) RepeatWithError(n int) ([]*S, error) {
37+
return Repeat(s, n), nil
38+
}
39+
40+
func Repeat(s *S, n int) []*S {
41+
t := make([]*S, n)
42+
for i := range t {
43+
t[i] = s
44+
}
45+
return t
46+
}
47+
48+
func RepeatWithError(s *S, n int) ([]*S, error) {
49+
return Repeat(s, n), nil
50+
}
51+
52+
func FirstSum(s []*S) float64 {
53+
return s[0].Sum()
54+
}
55+
56+
func FirstSumWithError(s []*S) (float64, error) {
57+
return s[0].Sum(), nil
58+
}
59+
2860
type (
2961
S2 struct{}
3062
I interface {

bind/testdata/structs.java.golden

+7-2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ public final class S implements Seq.Proxy {
3030
public final native void setY(double v);
3131

3232
public native S identity() throws Exception;
33+
public native S[] repeat(long n);
34+
public native S[] repeatWithError(long n) throws Exception;
3335
public native double sum();
3436
@Override public boolean equals(Object o) {
3537
if (o == null || !(o instanceof S)) {
@@ -176,7 +178,7 @@ import go.Seq;
176178

177179
public abstract class Structs {
178180
static {
179-
Seq.touch(); // for loading the native library
181+
Seq.touch(); // for loading the native library
180182
_init();
181183
}
182184

@@ -200,7 +202,10 @@ public abstract class Structs {
200202
public native void m();
201203
}
202204

203-
205+
public static native double firstSum(S[] s);
206+
public static native double firstSumWithError(S[] s) throws Exception;
204207
public static native S identity(S s);
205208
public static native S identityWithError(S s) throws Exception;
209+
public static native S[] repeat(S s, long n);
210+
public static native S[] repeatWithError(S s, long n) throws Exception;
206211
}

0 commit comments

Comments
 (0)