internal/kernel/callbacks.go (211 lines of code) (raw):
package kernel
import (
"fmt"
"reflect"
"strings"
"github.com/aws/jsii-runtime-go/internal/api"
)
type callback struct {
CallbackID string `json:"cbid"`
Cookie string `json:"cookie"`
Invoke *invokeCallback `json:"invoke"`
Get *getCallback `json:"get"`
Set *setCallback `json:"set"`
}
func (c *callback) handle(result kernelResponder) error {
var (
retval reflect.Value
err error
)
if c.Invoke != nil {
retval, err = c.Invoke.handle(c.Cookie)
} else if c.Get != nil {
retval, err = c.Get.handle(c.Cookie)
} else if c.Set != nil {
retval, err = c.Set.handle(c.Cookie)
} else {
return fmt.Errorf("invalid callback object: %v", c)
}
if err != nil {
return err
}
type callbackResult struct {
CallbackID string `json:"cbid"`
Result interface{} `json:"result,omitempty"`
Error string `json:"err,omitempty"`
}
type completeRequest struct {
kernelRequester
callbackResult `json:"complete"`
}
client := GetClient()
request := completeRequest{}
request.CallbackID = c.CallbackID
request.Result = client.CastPtrToRef(retval)
if err != nil {
request.Error = err.Error()
}
return client.request(request, result)
}
type invokeCallback struct {
Method string `json:"method"`
Arguments []interface{} `json:"args"`
ObjRef api.ObjectRef `json:"objref"`
}
func (i *invokeCallback) handle(cookie string) (retval reflect.Value, err error) {
client := GetClient()
receiver := reflect.ValueOf(client.GetObject(i.ObjRef))
method := receiver.MethodByName(cookie)
return client.invoke(method, i.Arguments)
}
type getCallback struct {
Property string `json:"property"`
ObjRef api.ObjectRef `json:"objref"`
}
func (g *getCallback) handle(cookie string) (retval reflect.Value, err error) {
client := GetClient()
receiver := reflect.ValueOf(client.GetObject(g.ObjRef))
if strings.HasPrefix(cookie, ".") {
// Ready to catch an error if the access panics...
defer func() {
if r := recover(); r != nil {
if err == nil {
var ok bool
if err, ok = r.(error); !ok {
err = fmt.Errorf("%v", r)
}
} else {
// This is not expected - so we panic!
panic(r)
}
}
}()
// Need to access the underlying struct...
receiver = receiver.Elem()
retval = receiver.FieldByName(cookie[1:])
if retval.IsZero() {
// Omit zero-values if a json tag instructs so...
field, _ := receiver.Type().FieldByName(cookie[1:])
if tag := field.Tag.Get("json"); tag != "" {
for _, attr := range strings.Split(tag, ",")[1:] {
if attr == "omitempty" {
retval = reflect.ValueOf(nil)
break
}
}
}
}
return
} else {
method := receiver.MethodByName(cookie)
return client.invoke(method, nil)
}
}
type setCallback struct {
Property string `json:"property"`
Value interface{} `json:"value"`
ObjRef api.ObjectRef `json:"objref"`
}
func (s *setCallback) handle(cookie string) (retval reflect.Value, err error) {
client := GetClient()
receiver := reflect.ValueOf(client.GetObject(s.ObjRef))
if strings.HasPrefix(cookie, ".") {
// Ready to catch an error if the access panics...
defer func() {
if r := recover(); r != nil {
if err == nil {
var ok bool
if err, ok = r.(error); !ok {
err = fmt.Errorf("%v", r)
}
} else {
// This is not expected - so we panic!
panic(r)
}
}
}()
// Need to access the underlying struct...
receiver = receiver.Elem()
field := receiver.FieldByName(cookie[1:])
meta, _ := receiver.Type().FieldByName(cookie[1:])
field.Set(convert(reflect.ValueOf(s.Value), meta.Type))
// Both retval & err are set to zero values here...
return
} else {
method := receiver.MethodByName(fmt.Sprintf("Set%v", cookie))
return client.invoke(method, []interface{}{s.Value})
}
}
func convert(value reflect.Value, typ reflect.Type) reflect.Value {
retry:
vt := value.Type()
if vt.AssignableTo(typ) {
return value
}
if value.CanConvert(typ) {
return value.Convert(typ)
}
if typ.Kind() == reflect.Ptr {
switch value.Kind() {
case reflect.String:
str := value.String()
value = reflect.ValueOf(&str)
case reflect.Bool:
bool := value.Bool()
value = reflect.ValueOf(&bool)
case reflect.Int:
int := value.Int()
value = reflect.ValueOf(&int)
case reflect.Float64:
float := value.Float()
value = reflect.ValueOf(&float)
default:
iface := value.Interface()
value = reflect.ValueOf(&iface)
}
goto retry
}
// Unsure what to do... let default behavior happen...
return value
}
func (c *Client) invoke(method reflect.Value, args []interface{}) (retval reflect.Value, err error) {
if !method.IsValid() {
err = fmt.Errorf("invalid method")
return
}
// Convert the arguments, if any...
callArgs := make([]reflect.Value, len(args))
methodType := method.Type()
numIn := methodType.NumIn()
for i, arg := range args {
var argType reflect.Type
if i < numIn {
argType = methodType.In(i)
} else if methodType.IsVariadic() {
argType = methodType.In(i - 1)
} else {
err = fmt.Errorf("too many arguments received %d for %d", len(args), numIn)
return
}
if argType.Kind() == reflect.Ptr {
callArgs[i] = reflect.New(argType.Elem())
} else {
callArgs[i] = reflect.New(argType)
}
c.castAndSetToPtr(callArgs[i].Elem(), reflect.ValueOf(arg))
if argType.Kind() != reflect.Ptr {
// The result of `reflect.New` is always a pointer, so if the
// argument is by-value, we have to de-reference it first.
callArgs[i] = callArgs[i].Elem()
}
}
// Ready to catch an error if the method panics...
defer func() {
if r := recover(); r != nil {
if err == nil {
var ok bool
if err, ok = r.(error); !ok {
err = fmt.Errorf("%v", r)
}
} else {
// This is not expected - so we panic!
panic(r)
}
}
}()
result := method.Call(callArgs)
switch len(result) {
case 0:
// Nothing to do, retval is already a 0-value.
case 1:
retval = result[0]
default:
err = fmt.Errorf("too many return values: %v", result)
}
return
}