blob: 6b5cd8f281eb596531395049ae5f68dc45967345 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin,ios
package rpc
import (
"reflect"
"unsafe"
"v.io/v23"
"v.io/v23/context"
"v.io/v23/options"
"v.io/v23/rpc"
"v.io/v23/security"
"v.io/v23/vdl"
"v.io/v23/vom"
sutil "v.io/x/swift/util"
scontext "v.io/x/swift/v23/context"
)
/*
#include <string.h> // memcpy
#import "../../../types.h"
// These sizes (including C struct memory alignment/padding) isn't available from Go, so we make that available via CGo.
static const size_t sizeofSwiftByteArray = sizeof(SwiftByteArray);
static const size_t sizeofSwiftByteArrayArray = sizeof(SwiftByteArrayArray);
*/
import "C"
func doStartCall(context *context.T, name, method string, skipServerAuth bool, client rpc.Client, args []interface{}) (rpc.ClientCall, error) {
var opts []rpc.CallOpt
if skipServerAuth {
opts = append(opts,
options.NameResolutionAuthorizer{security.AllowEveryone()},
options.ServerAuthorizer{security.AllowEveryone()})
}
// Invoke StartCall
call, err := client.StartCall(context, name, method, args, opts...)
if err != nil {
return nil, err
}
return call, nil
}
// TODO From JNI, implement this for Swift
//func decodeArgs(cVomArgs C.SwiftByteArrayArray) ([]interface{}, error) {
// if (cVomArgs == nil || cVomArgs.length == 0) {
// return make([]interface{}, 0), nil
// }
// // VOM-decode each arguments into a *vdl.Value.
// args := make([]interface{}, cVomArgs.length)
// for i := 0; i < cVomArgs.length; i++ {
// ret[i] = byte(*ptr)
// ptr = (*C.jbyte)(unsafe.Pointer(uintptr(unsafe.Pointer(ptr)) + unsafe.Sizeof(*ptr)))
//
// cVomArgs
//
// var err error
// if args[i], err = jutil.VomDecodeToValue(vomArgs[i]); err != nil {
// return nil, err
// }
// }
// return args, nil
//}
//export swift_io_v_impl_google_rpc_ClientImpl_nativeStartCallAsync
func swift_io_v_impl_google_rpc_ClientImpl_nativeStartCallAsync(ctxHandle C.GoContextHandle, cName *C.char, cMethod *C.char, cVomArgs C.SwiftByteArrayArray, skipServerAuth bool, asyncId C.AsyncCallbackIdentifier, successCallback C.SwiftAsyncSuccessHandleCallback, failureCallback C.SwiftAsyncFailureCallback) {
name := C.GoString(cName)
method := C.GoString(cMethod)
ctx := scontext.GoContext(uint64(ctxHandle))
client := v23.GetClient(ctx)
// TODO Get args (we don't have VOM yet in Swift so nothing to get until then)
// args, err := decodeArgs(env, jVomArgs)
// if err != nil {
// sutil.ThrowSwiftError(ctx, err, unsafe.Pointer(errOut))
// return C.GoClientCallHandle(0)
// }
args := make([]interface{}, 0)
go func() {
result, err := doStartCall(ctx, name, method, skipServerAuth == true, client, args)
if err != nil {
var swiftVError C.SwiftVError
sutil.ThrowSwiftError(ctx, err, unsafe.Pointer(&swiftVError))
sutil.DoFailureCallback(unsafe.Pointer(failureCallback), int32(asyncId), unsafe.Pointer(&swiftVError))
} else {
handle := C.GoClientCallHandle(SwiftClientCall(result))
sutil.DoSuccessHandlerCallback(unsafe.Pointer(successCallback), int32(asyncId), uint64(handle))
}
}()
}
//export swift_io_v_impl_google_rpc_ClientImpl_nativeClose
func swift_io_v_impl_google_rpc_ClientImpl_nativeClose(ctxHandle C.GoContextHandle) {
ctx := scontext.GoContext(uint64(ctxHandle))
client := v23.GetClient(ctx)
<-client.Closed()
}
//export swift_io_v_impl_google_rpc_ClientCallImpl_nativeCloseSend
func swift_io_v_impl_google_rpc_ClientCallImpl_nativeCloseSend(ctxHandle C.GoContextHandle, callHandle C.GoClientCallHandle, errOut *C.SwiftVError) {
ctx := scontext.GoContext(uint64(ctxHandle))
call := GoClientCall(uint64(callHandle))
if err := call.CloseSend(); err != nil {
sutil.ThrowSwiftError(ctx, err, unsafe.Pointer(errOut))
return
}
}
func doFinish(call rpc.ClientCall, numResults int) (C.SwiftByteArrayArray, error) {
// Have all the results be decoded into *vdl.Value.
resultPtrs := make([]interface{}, numResults)
for i := 0; i < numResults; i++ {
value := new(vdl.Value)
resultPtrs[i] = &value
}
if err := call.Finish(resultPtrs...); err != nil {
// Invocation error.
return EmptySwiftByteArrayArray(), err
}
// VOM-encode the results. Note in the future we'll want a pathway where we can get the original VOM results
// from finish so we don't end up wasting CPU & memory here.
// Prepare the byte array array that can be accessed from Swift via C.malloc
vomResultsMemory := C.malloc(C.size_t(numResults * int(C.sizeofSwiftByteArray)))
// Make that malloc'd memory available as a slice to Go.
vomResultsPtrsHdr := reflect.SliceHeader{
Data: uintptr(vomResultsMemory),
Len: numResults,
Cap: numResults,
}
vomResults := *(*[]C.SwiftByteArray)(unsafe.Pointer(&vomResultsPtrsHdr))
// Create the C Struct to return that encapsulates our byte array array
var cVomResults C.SwiftByteArrayArray
cVomResults.length = C._GoUint64(numResults)
cVomResults.data = (*C.SwiftByteArray)(vomResultsMemory)
// For each result, VOM encode into a byte array that we stick into the returned struct
for i, resultPtr := range resultPtrs {
// Remove the pointer from the result. Simply *resultPtr doesn't work
// as resultPtr is of type interface{}.
result := interface{}(sutil.DerefOrDie(resultPtr))
var vomResult []byte
var err error
if vomResult, err = vom.Encode(result); err != nil {
return EmptySwiftByteArrayArray(), err
}
cVomResultCopy := C.malloc(C.size_t(len(vomResult)))
C.memcpy(cVomResultCopy, unsafe.Pointer(&vomResult[0]), C.size_t(len(vomResult)))
var cVomResult C.SwiftByteArray
cVomResult.length = C._GoUint64(len(vomResult))
cVomResult.data = (*C.char)(cVomResultCopy)
vomResults[i] = cVomResult
}
return cVomResults, nil
}
//export swift_io_v_impl_google_rpc_ClientCallImpl_nativeFinishAsync
func swift_io_v_impl_google_rpc_ClientCallImpl_nativeFinishAsync(ctxHandle C.GoContextHandle, callHandle C.GoClientCallHandle, numResults int, asyncId C.AsyncCallbackIdentifier, successCallback C.SwiftAsyncSuccessByteArrayArrayCallback, failureCallback C.SwiftAsyncFailureCallback) {
ctx := scontext.GoContext(uint64(ctxHandle))
call := GoClientCall(uint64(callHandle))
go func() {
result, err := doFinish(call, numResults)
if err != nil {
var swiftVError C.SwiftVError
sutil.ThrowSwiftError(ctx, err, unsafe.Pointer(&swiftVError))
sutil.DoFailureCallback(unsafe.Pointer(failureCallback), int32(asyncId), unsafe.Pointer(&swiftVError))
} else {
sutil.DoSuccessByteArrayArrayCallback(unsafe.Pointer(successCallback), int32(asyncId), unsafe.Pointer(&result))
}
}()
}
//export swift_io_v_impl_google_rpc_ClientCallImpl_nativeFinalize
func swift_io_v_impl_google_rpc_ClientCallImpl_nativeFinalize(callHandle C.GoClientCallHandle) {
sutil.GoUnref(uint64(callHandle))
}