Skip to content

Commit

Permalink
x/mobile/bind: support slices of structs
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
kevmo314 committed May 5, 2024
1 parent 09dbf07 commit 8ba93b9
Show file tree
Hide file tree
Showing 9 changed files with 180 additions and 8 deletions.
10 changes: 10 additions & 0 deletions bind/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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) {
Expand Down
14 changes: 14 additions & 0 deletions bind/gengo.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
}
Expand Down
35 changes: 33 additions & 2 deletions bind/genjava.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
}
Expand Down
34 changes: 30 additions & 4 deletions bind/genobjc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
}
}
}
}
Expand Down Expand Up @@ -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.
Expand Down
42 changes: 42 additions & 0 deletions bind/java/seq_android.c.support
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
6 changes: 6 additions & 0 deletions bind/java/seq_android.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down
6 changes: 6 additions & 0 deletions bind/objc/seq_darwin.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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__
32 changes: 32 additions & 0 deletions bind/testdata/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
9 changes: 7 additions & 2 deletions bind/testdata/structs.java.golden
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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();
}

Expand All @@ -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;
}

0 comments on commit 8ba93b9

Please sign in to comment.