This is to enable more flexible encoding/decoding of Go string. For Java, we use UTF16 to be compatible with java string. For other languages, we will want other way to represent a string. Change-Id: Iccd53e2eea18d37636c3c619d06cb473facef0cd Reviewed-on: https://go-review.googlesource.com/8628 Reviewed-by: David Crawshaw <crawshaw@golang.org>
189 lines
4.5 KiB
Go
189 lines
4.5 KiB
Go
// Copyright 2014 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package java // import "golang.org/x/mobile/bind/java"
|
|
|
|
//#cgo LDFLAGS: -llog
|
|
//#include <android/log.h>
|
|
//#include <stdint.h>
|
|
//#include <string.h>
|
|
//#include "seq_android.h"
|
|
import "C"
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
"unsafe"
|
|
|
|
"golang.org/x/mobile/app"
|
|
"golang.org/x/mobile/bind/seq"
|
|
)
|
|
|
|
const maxSliceLen = 1<<31 - 1
|
|
|
|
const debug = false
|
|
|
|
// Send is called by Java to send a request to run a Go function.
|
|
//export Send
|
|
func Send(descriptor string, code int, req *C.uint8_t, reqlen C.size_t, res **C.uint8_t, reslen *C.size_t) {
|
|
fn := seq.Registry[descriptor][code]
|
|
if fn == nil {
|
|
panic(fmt.Sprintf("invalid descriptor(%s) and code(0x%x)", descriptor, code))
|
|
}
|
|
in := new(seq.Buffer)
|
|
if reqlen > 0 {
|
|
in.Data = (*[maxSliceLen]byte)(unsafe.Pointer(req))[:reqlen]
|
|
}
|
|
out := new(seq.Buffer)
|
|
fn(out, in)
|
|
// BUG(hyangah): the function returning a go byte slice (so fn writes a pointer into 'out') is unsafe.
|
|
// After fn is complete here, Go runtime is free to collect or move the pointed byte slice
|
|
// contents. (Explicitly calling runtime.GC here will surface the problem?)
|
|
// Without pinning support from Go side, it will be hard to fix it without extra copying.
|
|
|
|
seqToBuf(res, reslen, out)
|
|
}
|
|
|
|
// DestroyRef is called by Java to inform Go it is done with a reference.
|
|
//export DestroyRef
|
|
func DestroyRef(refnum C.int32_t) {
|
|
seq.Delete(int32(refnum))
|
|
}
|
|
|
|
type request struct {
|
|
ref *seq.Ref
|
|
handle int32
|
|
code int
|
|
in *seq.Buffer
|
|
}
|
|
|
|
var recv struct {
|
|
sync.Mutex
|
|
cond sync.Cond // signals req is not empty
|
|
req []request
|
|
next int32 // next handle value
|
|
}
|
|
|
|
var res struct {
|
|
sync.Mutex
|
|
cond sync.Cond // signals a response is filled in
|
|
out map[int32]*seq.Buffer // handle -> output
|
|
}
|
|
|
|
func init() {
|
|
recv.cond.L = &recv.Mutex
|
|
recv.next = 411 // arbitrary starting point distinct from Go and Java obj ref nums
|
|
|
|
res.cond.L = &res.Mutex
|
|
res.out = make(map[int32]*seq.Buffer)
|
|
}
|
|
|
|
func initSeq() {
|
|
vm := app.State.(interface {
|
|
JavaVM() unsafe.Pointer
|
|
}).JavaVM()
|
|
C.init_seq(vm)
|
|
}
|
|
|
|
func seqToBuf(bufptr **C.uint8_t, lenptr *C.size_t, buf *seq.Buffer) {
|
|
if debug {
|
|
fmt.Printf("seqToBuf tag 1, len(buf.Data)=%d, *lenptr=%d\n", len(buf.Data), *lenptr)
|
|
}
|
|
if len(buf.Data) == 0 {
|
|
*lenptr = 0
|
|
return
|
|
}
|
|
if len(buf.Data) > int(*lenptr) {
|
|
// TODO(crawshaw): realloc
|
|
C.free(unsafe.Pointer(*bufptr))
|
|
m := C.malloc(C.size_t(len(buf.Data)))
|
|
if uintptr(m) == 0 {
|
|
panic(fmt.Sprintf("malloc failed, size=%d", len(buf.Data)))
|
|
}
|
|
*bufptr = (*C.uint8_t)(m)
|
|
*lenptr = C.size_t(len(buf.Data))
|
|
}
|
|
C.memcpy(unsafe.Pointer(*bufptr), unsafe.Pointer(&buf.Data[0]), C.size_t(len(buf.Data)))
|
|
}
|
|
|
|
// Recv is called by Java in a loop and blocks until Go requests a callback
|
|
// be executed by the JVM. Then a request object is returned, along with a
|
|
// handle for the host to respond via RecvRes.
|
|
//export Recv
|
|
func Recv(in **C.uint8_t, inlen *C.size_t) (ref, code, handle C.int32_t) {
|
|
recv.Lock()
|
|
for len(recv.req) == 0 {
|
|
recv.cond.Wait()
|
|
}
|
|
req := recv.req[0]
|
|
recv.req = recv.req[1:]
|
|
seqToBuf(in, inlen, req.in)
|
|
recv.Unlock()
|
|
|
|
return C.int32_t(req.ref.Num), C.int32_t(req.code), C.int32_t(req.handle)
|
|
}
|
|
|
|
// RecvRes is called by JNI to return the result of a requested callback.
|
|
//export RecvRes
|
|
func RecvRes(handle C.int32_t, out *C.uint8_t, outlen C.size_t) {
|
|
outBuf := &seq.Buffer{
|
|
Data: make([]byte, outlen),
|
|
}
|
|
copy(outBuf.Data, (*[maxSliceLen]byte)(unsafe.Pointer(out))[:outlen])
|
|
|
|
res.Lock()
|
|
res.out[int32(handle)] = outBuf
|
|
res.Unlock()
|
|
res.cond.Broadcast()
|
|
}
|
|
|
|
// transact calls a method on a Java object instance.
|
|
// It blocks until the call is complete.
|
|
func transact(ref *seq.Ref, code int, in *seq.Buffer) *seq.Buffer {
|
|
recv.Lock()
|
|
if recv.next == 1<<31-1 {
|
|
panic("recv handle overflow")
|
|
}
|
|
handle := recv.next
|
|
recv.next++
|
|
recv.req = append(recv.req, request{
|
|
ref: ref,
|
|
code: code,
|
|
in: in,
|
|
handle: handle,
|
|
})
|
|
recv.Unlock()
|
|
recv.cond.Signal()
|
|
|
|
res.Lock()
|
|
for res.out[handle] == nil {
|
|
res.cond.Wait()
|
|
}
|
|
out := res.out[handle]
|
|
delete(res.out, handle)
|
|
res.Unlock()
|
|
|
|
return out
|
|
}
|
|
|
|
func encodeString(out *seq.Buffer, v string) {
|
|
out.WriteUTF16(v)
|
|
}
|
|
|
|
func decodeString(in *seq.Buffer) string {
|
|
return in.ReadUTF16()
|
|
}
|
|
|
|
func init() {
|
|
seq.FinalizeRef = func(ref *seq.Ref) {
|
|
if ref.Num < 0 {
|
|
panic(fmt.Sprintf("not a Java ref: %d", ref.Num))
|
|
}
|
|
transact(ref, -1, new(seq.Buffer))
|
|
}
|
|
|
|
seq.Transact = transact
|
|
seq.EncString = encodeString
|
|
seq.DecString = decodeString
|
|
}
|