Skip to content

Commit b2e453e

Browse files
committed
bind/java: support byte arrays.
Fixes golang/go#9338. Change-Id: I6e2af67cdf7f923963fa525b944613a91aac994e Reviewed-on: https://go-review.googlesource.com/1884 Reviewed-by: David Crawshaw <[email protected]>
1 parent 1aa04cf commit b2e453e

File tree

10 files changed

+182
-22
lines changed

10 files changed

+182
-22
lines changed

bind/java/SeqTest.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package go;
22

33
import android.test.suitebuilder.annotation.Suppress;
4+
import android.test.MoreAsserts;
5+
import java.util.Arrays;
6+
import java.util.Random;
47

58
import go.testpkg.Testpkg;
69

@@ -52,6 +55,32 @@ public void testErr() {
5255
}
5356
}
5457

58+
public void testByteArray() {
59+
for (int i = 0; i < 2048; i++) {
60+
if (i == 0) {
61+
byte[] got = Testpkg.BytesAppend(null, null);
62+
assertEquals("Bytes(null+null) should match", (byte[])null, got);
63+
got = Testpkg.BytesAppend(new byte[0], new byte[0]);
64+
assertEquals("Bytes(empty+empty) should match", (byte[])null, got);
65+
continue;
66+
}
67+
68+
byte[] want = new byte[i];
69+
new Random().nextBytes(want);
70+
71+
byte[] s1 = null;
72+
byte[] s2 = null;
73+
if (i > 0) {
74+
s1 = Arrays.copyOfRange(want, 0, 1);
75+
}
76+
if (i > 1) {
77+
s2 = Arrays.copyOfRange(want, 1, i);
78+
}
79+
byte[] got = Testpkg.BytesAppend(s1, s2);
80+
MoreAsserts.assertEquals("Bytes(len="+i+") should match", want, got);
81+
}
82+
}
83+
5584
public void testGoRefGC() {
5685
Testpkg.S s = Testpkg.New();
5786
runGC();

bind/java/seq_android.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,20 @@ Java_go_Seq_readUTF16(JNIEnv *env, jobject obj) {
203203
return (*env)->NewString(env, (jchar*)mem_read(env, obj, 2*size), size);
204204
}
205205

206+
JNIEXPORT jbyteArray JNICALL
207+
Java_go_Seq_readByteArray(JNIEnv *env, jobject obj) {
208+
// Send the (array length, pointer) pair encoded as two int64.
209+
// The pointer value is omitted if array length is 0.
210+
jlong size = Java_go_Seq_readInt64(env, obj);
211+
if (size == 0) {
212+
return NULL;
213+
}
214+
jbyteArray res = (*env)->NewByteArray(env, size);
215+
jlong ptr = Java_go_Seq_readInt64(env, obj);
216+
(*env)->SetByteArrayRegion(env, res, 0, size, (jbyte*)(intptr_t)(ptr));
217+
return res;
218+
}
219+
206220
#define MEM_WRITE(ty) (*(ty*)mem_write(env, obj, sizeof(ty)))
207221

208222
JNIEXPORT void JNICALL
@@ -246,6 +260,35 @@ Java_go_Seq_writeUTF16(JNIEnv *env, jobject obj, jstring v) {
246260
(*env)->GetStringRegion(env, v, 0, size, (jchar*)mem_write(env, obj, 2*size));
247261
}
248262

263+
JNIEXPORT void JNICALL
264+
Java_go_Seq_writeByteArray(JNIEnv *env, jobject obj, jbyteArray v) {
265+
// For Byte array, we pass only the (array length, pointer) pair
266+
// encoded as two int64 values. If the array length is 0,
267+
// the pointer value is omitted.
268+
if (v == NULL) {
269+
MEM_WRITE(int64_t) = 0;
270+
return;
271+
}
272+
273+
jsize len = (*env)->GetArrayLength(env, v);
274+
MEM_WRITE(int64_t) = len;
275+
if (len == 0) {
276+
return;
277+
}
278+
279+
jboolean isCopy;
280+
jbyte* b = (*env)->GetByteArrayElements(env, v, &isCopy);
281+
if (isCopy) {
282+
// TODO: It's not clear how to handle if b is pointing to
283+
// a copy that may become invalid with ReleaseByteArrayElements.
284+
// Should we fall back to copy the byte array into the buffer?
285+
LOG_FATAL("got a copied byte array (len=%d)", len);
286+
}
287+
// gross pointer-to-int64 conversion.
288+
MEM_WRITE(int64_t) = (int64_t)((intptr_t)b);
289+
(*env)->ReleaseByteArrayElements(env, v, (jbyte*)b, 0);
290+
}
291+
249292
JNIEXPORT void JNICALL
250293
Java_go_Seq_resetOffset(JNIEnv *env, jobject obj) {
251294
mem *m = mem_get(env, obj);

bind/java/testpkg/Testpkg.java

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,17 @@ public static long Add(long x, long y) {
2020
return _result;
2121
}
2222

23+
public static byte[] BytesAppend(byte[] a, byte[] b) {
24+
go.Seq _in = new go.Seq();
25+
go.Seq _out = new go.Seq();
26+
byte[] _result;
27+
_in.writeByteArray(a);
28+
_in.writeByteArray(b);
29+
Seq.send(DESCRIPTOR, CALL_BytesAppend, _in, _out);
30+
_result = _out.readByteArray();
31+
return _result;
32+
}
33+
2334
public static void Call(I i) {
2435
go.Seq _in = new go.Seq();
2536
go.Seq _out = new go.Seq();
@@ -171,12 +182,13 @@ public static String StrDup(String s) {
171182
}
172183

173184
private static final int CALL_Add = 1;
174-
private static final int CALL_Call = 2;
175-
private static final int CALL_Err = 3;
176-
private static final int CALL_GC = 4;
177-
private static final int CALL_Keep = 5;
178-
private static final int CALL_New = 6;
179-
private static final int CALL_NumSCollected = 7;
180-
private static final int CALL_StrDup = 8;
185+
private static final int CALL_BytesAppend = 2;
186+
private static final int CALL_Call = 3;
187+
private static final int CALL_Err = 4;
188+
private static final int CALL_GC = 5;
189+
private static final int CALL_Keep = 6;
190+
private static final int CALL_New = 7;
191+
private static final int CALL_NumSCollected = 8;
192+
private static final int CALL_StrDup = 9;
181193
private static final String DESCRIPTOR = "testpkg";
182194
}

bind/java/testpkg/go_testpkg/go_testpkg.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ func proxy_Add(out, in *seq.Buffer) {
1616
out.WriteInt(res)
1717
}
1818

19+
func proxy_BytesAppend(out, in *seq.Buffer) {
20+
param_a := in.ReadByteArray()
21+
param_b := in.ReadByteArray()
22+
res := testpkg.BytesAppend(param_a, param_b)
23+
out.WriteByteArray(res)
24+
}
25+
1926
func proxy_Call(out, in *seq.Buffer) {
2027
var param_i testpkg.I
2128
param_i_ref := in.ReadRef()
@@ -99,11 +106,12 @@ func proxy_StrDup(out, in *seq.Buffer) {
99106

100107
func init() {
101108
seq.Register("testpkg", 1, proxy_Add)
102-
seq.Register("testpkg", 2, proxy_Call)
103-
seq.Register("testpkg", 3, proxy_Err)
104-
seq.Register("testpkg", 4, proxy_GC)
105-
seq.Register("testpkg", 5, proxy_Keep)
106-
seq.Register("testpkg", 6, proxy_New)
107-
seq.Register("testpkg", 7, proxy_NumSCollected)
108-
seq.Register("testpkg", 8, proxy_StrDup)
109+
seq.Register("testpkg", 2, proxy_BytesAppend)
110+
seq.Register("testpkg", 3, proxy_Call)
111+
seq.Register("testpkg", 4, proxy_Err)
112+
seq.Register("testpkg", 5, proxy_GC)
113+
seq.Register("testpkg", 6, proxy_Keep)
114+
seq.Register("testpkg", 7, proxy_New)
115+
seq.Register("testpkg", 8, proxy_NumSCollected)
116+
seq.Register("testpkg", 9, proxy_StrDup)
109117
}

bind/java/testpkg/testpkg.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,7 @@ func Err(s string) error {
6868
}
6969
return nil
7070
}
71+
72+
func BytesAppend(a []byte, b []byte) []byte {
73+
return append(a, b...)
74+
}

bind/seq.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func seqType(t types.Type) string {
2525
return "Int32"
2626
case types.Int64:
2727
return "Int64"
28-
case types.Uint8:
28+
case types.Uint8: // Byte.
2929
// TODO(crawshaw): questionable, but vital?
3030
return "Byte"
3131
// TODO(crawshaw): case types.Uint, types.Uint16, types.Uint32, types.Uint64:
@@ -37,7 +37,7 @@ func seqType(t types.Type) string {
3737
return "UTF16"
3838
default:
3939
// Should be caught earlier in processing.
40-
panic(fmt.Sprintf("unsupported return type: %s", t))
40+
panic(fmt.Sprintf("unsupported basic seqType: %s", t))
4141
}
4242
case *types.Named:
4343
switch u := t.Underlying().(type) {
@@ -46,6 +46,19 @@ func seqType(t types.Type) string {
4646
default:
4747
panic(fmt.Sprintf("unsupported named seqType: %s / %T", u, u))
4848
}
49+
case *types.Slice:
50+
switch e := t.Elem().(type) {
51+
case *types.Basic:
52+
switch e.Kind() {
53+
case types.Uint8: // Byte.
54+
return "ByteArray"
55+
default:
56+
panic(fmt.Sprintf("unsupported seqType: %s(%s) / %T(%T)", t, e, t, e))
57+
}
58+
default:
59+
panic(fmt.Sprintf("unsupported seqType: %s(%s) / %T(%T)", t, e, t, e))
60+
}
61+
// TODO: let the types.Array case handled like types.Slice?
4962
default:
5063
panic(fmt.Sprintf("unsupported seqType: %s / %T", t, t))
5164
}

bind/seq/buffer.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,22 @@ func (b *Buffer) ReadFloat64() float64 {
9191
return v
9292
}
9393

94+
func (b *Buffer) ReadByteArray() []byte {
95+
sz := b.ReadInt64()
96+
if sz == 0 {
97+
return nil
98+
}
99+
100+
ptr := b.ReadInt64()
101+
org := (*[1 << 30]byte)(unsafe.Pointer(uintptr(ptr)))[:sz]
102+
103+
// Make a copy managed by Go, so the returned byte array can be
104+
// used safely in Go.
105+
slice := make([]byte, sz)
106+
copy(slice, org)
107+
return slice
108+
}
109+
94110
func (b *Buffer) ReadRef() *Ref {
95111
ref := &Ref{b.ReadInt32()}
96112
if ref.Num > 0 {
@@ -137,6 +153,19 @@ func (b *Buffer) WriteFloat64(v float64) {
137153
b.Offset += 8
138154
}
139155

156+
func (b *Buffer) WriteByteArray(byt []byte) {
157+
sz := len(byt)
158+
if sz == 0 {
159+
b.WriteInt64(int64(sz))
160+
return
161+
}
162+
163+
ptr := uintptr(unsafe.Pointer(&byt[0]))
164+
b.WriteInt64(int64(sz))
165+
b.WriteInt64(int64(ptr))
166+
return
167+
}
168+
140169
func (b *Buffer) WriteGoRef(obj interface{}) {
141170
refs.Lock()
142171
num := refs.refs[obj]
@@ -154,6 +183,8 @@ func (b *Buffer) WriteGoRef(obj interface{}) {
154183
b.WriteInt32(int32(num))
155184
}
156185

186+
/* TODO: Will we need it?
157187
func (b *Buffer) WriteRef(ref *Ref) {
158188
b.WriteInt32(ref.Num)
159189
}
190+
*/

bind/testdata/basictypes.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@ func Ints(x int8, y int16, z int32, t int64, u int) {}
99
func Error() error { return nil }
1010

1111
func ErrorPair() (int, error) { return 0, nil }
12+
13+
func ByteArrays(x []byte) []byte { return nil }

bind/testdata/basictypes.go.golden

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ import (
99
"golang.org/x/mobile/bind/seq"
1010
)
1111

12+
func proxy_ByteArrays(out, in *seq.Buffer) {
13+
param_x := in.ReadByteArray()
14+
res := basictypes.ByteArrays(param_x)
15+
out.WriteByteArray(res)
16+
}
17+
1218
func proxy_Error(out, in *seq.Buffer) {
1319
err := basictypes.Error()
1420
if err == nil {
@@ -38,7 +44,8 @@ func proxy_Ints(out, in *seq.Buffer) {
3844
}
3945

4046
func init() {
41-
seq.Register("basictypes", 1, proxy_Error)
42-
seq.Register("basictypes", 2, proxy_ErrorPair)
43-
seq.Register("basictypes", 3, proxy_Ints)
47+
seq.Register("basictypes", 1, proxy_ByteArrays)
48+
seq.Register("basictypes", 2, proxy_Error)
49+
seq.Register("basictypes", 3, proxy_ErrorPair)
50+
seq.Register("basictypes", 4, proxy_Ints)
4451
}

bind/testdata/basictypes.java.golden

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@ import go.Seq;
99
public abstract class Basictypes {
1010
private Basictypes() {} // uninstantiable
1111

12+
public static byte[] ByteArrays(byte[] x) {
13+
go.Seq _in = new go.Seq();
14+
go.Seq _out = new go.Seq();
15+
byte[] _result;
16+
_in.writeByteArray(x);
17+
Seq.send(DESCRIPTOR, CALL_ByteArrays, _in, _out);
18+
_result = _out.readByteArray();
19+
return _result;
20+
}
21+
1222
public static void Error() throws Exception {
1323
go.Seq _in = new go.Seq();
1424
go.Seq _out = new go.Seq();
@@ -43,8 +53,9 @@ public abstract class Basictypes {
4353
Seq.send(DESCRIPTOR, CALL_Ints, _in, _out);
4454
}
4555

46-
private static final int CALL_Error = 1;
47-
private static final int CALL_ErrorPair = 2;
48-
private static final int CALL_Ints = 3;
56+
private static final int CALL_ByteArrays = 1;
57+
private static final int CALL_Error = 2;
58+
private static final int CALL_ErrorPair = 3;
59+
private static final int CALL_Ints = 4;
4960
private static final String DESCRIPTOR = "basictypes";
5061
}

0 commit comments

Comments
 (0)