Arrays and complex objects
This commit is contained in:
parent
17e077f86e
commit
f33f5d2b4f
157
muon.go
157
muon.go
|
@ -1,6 +1,8 @@
|
||||||
package muon
|
package muon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
@ -90,12 +92,28 @@ func (w *Window) Bind(name string, function interface{}) {
|
||||||
f.ReturnTypes = make([]reflect.Type, t.NumOut())
|
f.ReturnTypes = make([]reflect.Type, t.NumOut())
|
||||||
|
|
||||||
for i := 0; i < t.NumOut(); i++ {
|
for i := 0; i < t.NumOut(); i++ {
|
||||||
f.ParamTypes[i] = t.Out(i)
|
f.ReturnTypes[i] = t.Out(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
addFunctionToView(w.view, name, w.makeIPCCallback(f))
|
addFunctionToView(w.view, name, w.makeIPCCallback(f))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Eval evaluates a given JavaScript string in the given Window view. `ret` is necessary for JSON serialization if an object is returned.
|
||||||
|
func (w *Window) Eval(js string, ret reflect.Type) (interface{}, error) {
|
||||||
|
us := UlCreateString(js)
|
||||||
|
defer UlDestroyString(us)
|
||||||
|
|
||||||
|
ref := UlViewEvaluateScript(w.view, us)
|
||||||
|
ctx := UlViewGetJSContext(w.view)
|
||||||
|
val, err := fromJSValue(ctx, ref, ret)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return val.Interface(), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (w *Window) makeIPCCallback(f *ipf) func(JSContextRef, JSObjectRef, JSObjectRef, uint, []JSValueRef, []JSValueRef) JSValueRef {
|
func (w *Window) makeIPCCallback(f *ipf) func(JSContextRef, JSObjectRef, JSObjectRef, uint, []JSValueRef, []JSValueRef) JSValueRef {
|
||||||
return func(
|
return func(
|
||||||
ctx JSContextRef,
|
ctx JSContextRef,
|
||||||
|
@ -108,39 +126,128 @@ func (w *Window) makeIPCCallback(f *ipf) func(JSContextRef, JSObjectRef, JSObjec
|
||||||
params := make([]reflect.Value, argumentCount)
|
params := make([]reflect.Value, argumentCount)
|
||||||
|
|
||||||
for i := uint(0); i < argumentCount; i++ {
|
for i := uint(0); i < argumentCount; i++ {
|
||||||
switch JSValueGetType(ctx, arguments[i]) {
|
val, err := fromJSValue(ctx, arguments[i], f.ParamTypes[i])
|
||||||
case KJSTypeBoolean:
|
|
||||||
params[i] = reflect.ValueOf(JSValueToBoolean(ctx, arguments[i]))
|
if err != nil {
|
||||||
case KJSTypeNumber:
|
panic(err)
|
||||||
params[i] = reflect.ValueOf(JSValueToNumber(ctx, arguments[i], exception))
|
|
||||||
case KJSTypeString:
|
|
||||||
ref := JSValueToStringCopy(ctx, arguments[i], exception)
|
|
||||||
params[i] = reflect.ValueOf(*(*string)(unsafe.Pointer(&ref)))
|
|
||||||
JSStringRelease(ref)
|
|
||||||
default:
|
|
||||||
panic("Not implemented!") // TODO: Object JSON
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
params[i] = val
|
||||||
}
|
}
|
||||||
|
|
||||||
val := f.Function.Call(params)
|
val := f.Function.Call(params)
|
||||||
|
|
||||||
if len(val) > 1 {
|
if len(val) > 1 {
|
||||||
panic("Not implemented!") // TODO: more than 1 return type
|
panic("Javascript does not support more than 1 return value!")
|
||||||
}
|
}
|
||||||
|
|
||||||
switch val[0].Kind() {
|
return toJSValue(ctx, val[0])
|
||||||
case reflect.Float64:
|
|
||||||
return JSValueMakeNumber(ctx, val[0].Float())
|
|
||||||
case reflect.Bool:
|
|
||||||
return JSValueMakeBoolean(ctx, val[0].Bool())
|
|
||||||
case reflect.String:
|
|
||||||
return JSValueMakeString(ctx, JSStringCreateWithUTF8CString(val[0].String()))
|
|
||||||
default:
|
|
||||||
panic("Not implemented!") // TODO: Object JSON
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fromJSValue(ctx JSContextRef, value JSValueRef, rtype reflect.Type) (reflect.Value, error) {
|
||||||
|
var rv reflect.Value
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if JSValueIsArray(ctx, value) {
|
||||||
|
l := JSStringCreateWithUTF8CString("length")
|
||||||
|
defer JSStringRelease(l)
|
||||||
|
|
||||||
|
obj := *(*JSObjectRef)(unsafe.Pointer(&value))
|
||||||
|
|
||||||
|
prop := JSObjectGetProperty(ctx, obj, l, nil)
|
||||||
|
length := int(JSValueToNumber(ctx, prop, nil))
|
||||||
|
values := reflect.MakeSlice(rtype, length, length)
|
||||||
|
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
ref := JSObjectGetPropertyAtIndex(ctx, obj, uint32(i), nil)
|
||||||
|
|
||||||
|
val, err := fromJSValue(ctx, ref, rtype.Elem())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return reflect.Value{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
values.Index(i).Set(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
return values, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch JSValueGetType(ctx, value) {
|
||||||
|
case KJSTypeBoolean:
|
||||||
|
rv = reflect.ValueOf(JSValueToBoolean(ctx, value))
|
||||||
|
case KJSTypeNumber:
|
||||||
|
rv = reflect.ValueOf(JSValueToNumber(ctx, value, nil))
|
||||||
|
case KJSTypeString:
|
||||||
|
ref := JSValueToStringCopy(ctx, value, nil)
|
||||||
|
rv = reflect.ValueOf(fromJSString(ref))
|
||||||
|
JSStringRelease(ref)
|
||||||
|
case KJSTypeObject:
|
||||||
|
ref := JSValueCreateJSONString(ctx, value, 0, nil)
|
||||||
|
obj := reflect.New(rtype).Interface()
|
||||||
|
|
||||||
|
if err = json.Unmarshal([]byte(fromJSString(ref)), &obj); err == nil {
|
||||||
|
rv = reflect.Indirect(reflect.ValueOf(obj))
|
||||||
|
}
|
||||||
|
|
||||||
|
JSStringRelease(ref)
|
||||||
|
default:
|
||||||
|
panic("Not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func fromJSString(str JSStringRef) string {
|
||||||
|
len := JSStringGetMaximumUTF8CStringSize(str)
|
||||||
|
data := make([]byte, len)
|
||||||
|
written := JSStringGetUTF8CString(str, data, len)
|
||||||
|
|
||||||
|
return string(data[:written-1])
|
||||||
|
}
|
||||||
|
|
||||||
|
func toJSValue(ctx JSContextRef, value reflect.Value) JSValueRef {
|
||||||
|
var jsv JSValueRef
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch value.Kind() {
|
||||||
|
case reflect.Float64:
|
||||||
|
jsv = JSValueMakeNumber(ctx, value.Float())
|
||||||
|
case reflect.Bool:
|
||||||
|
jsv = JSValueMakeBoolean(ctx, value.Bool())
|
||||||
|
case reflect.String:
|
||||||
|
str := JSStringCreateWithUTF8CString(value.String())
|
||||||
|
jsv = JSValueMakeString(ctx, str)
|
||||||
|
JSStringRelease(str)
|
||||||
|
case reflect.Ptr:
|
||||||
|
return toJSValue(ctx, reflect.Indirect(value))
|
||||||
|
case reflect.Struct:
|
||||||
|
if json, err := json.Marshal(value.Interface()); err == nil {
|
||||||
|
str := JSStringCreateWithUTF8CString(string(json))
|
||||||
|
jsv = JSValueMakeFromJSONString(ctx, str)
|
||||||
|
JSStringRelease(str)
|
||||||
|
}
|
||||||
|
case reflect.Slice, reflect.Array:
|
||||||
|
rets := make([]JSValueRef, value.Len())
|
||||||
|
|
||||||
|
for i := 0; i < value.Len(); i++ {
|
||||||
|
rets[i] = toJSValue(ctx, value.Index(i))
|
||||||
|
}
|
||||||
|
arr := JSObjectMakeArray(ctx, uint(len(rets)), rets, nil)
|
||||||
|
jsv = *(*JSValueRef)(unsafe.Pointer(&arr))
|
||||||
|
default:
|
||||||
|
panic("Not implemented!")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
return JSValueMakeNull(ctx)
|
||||||
|
}
|
||||||
|
return jsv
|
||||||
|
}
|
||||||
|
|
||||||
func addFunctionToView(view ULView, name string, callback JSObjectCallAsFunctionCallback) {
|
func addFunctionToView(view ULView, name string, callback JSObjectCallAsFunctionCallback) {
|
||||||
ctx := UlViewGetJSContext(view)
|
ctx := UlViewGetJSContext(view)
|
||||||
gobj := JSContextGetGlobalObject(ctx)
|
gobj := JSContextGetGlobalObject(ctx)
|
||||||
|
@ -155,8 +262,8 @@ func addFunctionToView(view ULView, name string, callback JSObjectCallAsFunction
|
||||||
JSObjectSetProperty(ctx, gobj, fn, val, KJSPropertyAttributeNone, []JSValueRef{})
|
JSObjectSetProperty(ctx, gobj, fn, val, KJSPropertyAttributeNone, []JSValueRef{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func resizeCallback(user_data unsafe.Pointer, width uint32, height uint32) {
|
func resizeCallback(userData unsafe.Pointer, width uint32, height uint32) {
|
||||||
overlay := *(*ULOverlay)(user_data)
|
overlay := *(*ULOverlay)(userData)
|
||||||
UlOverlayResize(overlay, width, height)
|
UlOverlayResize(overlay, width, height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
package muon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testObject struct {
|
||||||
|
S1 string
|
||||||
|
F1 float64
|
||||||
|
B1 bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestComplexType(t *testing.T) {
|
||||||
|
cfg := &Config{
|
||||||
|
Height: 1,
|
||||||
|
Width: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
m := New(cfg, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
||||||
|
|
||||||
|
m.Bind("test", func(to *testObject) *testObject {
|
||||||
|
return &testObject{
|
||||||
|
S1: to.S1 + " World!",
|
||||||
|
F1: to.F1 + 1,
|
||||||
|
B1: !to.B1,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
m.Start()
|
||||||
|
}()
|
||||||
|
|
||||||
|
res, err := m.Eval(`test({S1: "Hello,", F1: 9000, B1: false})`, reflect.TypeOf(&testObject{}))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
to := res.(*testObject)
|
||||||
|
|
||||||
|
if to.S1 != "Hello, World!" {
|
||||||
|
t.Errorf("to.S1 was not correct, got %s", to.S1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if to.F1 != 9001 {
|
||||||
|
t.Errorf("to.F1 was under 9000, got %f", to.F1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !to.B1 {
|
||||||
|
t.Errorf("to.B1 was not True, got false")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestArrayType(t *testing.T) {
|
||||||
|
cfg := &Config{
|
||||||
|
Height: 1,
|
||||||
|
Width: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
m := New(cfg, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
||||||
|
|
||||||
|
m.Bind("test", func(strings []string) []float64 {
|
||||||
|
if strings[0] != "Hello" {
|
||||||
|
t.Errorf("strings[0] was not Hello, got %s", strings[0])
|
||||||
|
}
|
||||||
|
if strings[1] != "World!" {
|
||||||
|
t.Errorf("strings[1] was not World!, got %s", strings[1])
|
||||||
|
}
|
||||||
|
return []float64{1, 2, 3}
|
||||||
|
})
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
m.Start()
|
||||||
|
}()
|
||||||
|
|
||||||
|
res, err := m.Eval(`test(["Hello","World!"])`, reflect.TypeOf([]float64{}))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nums := res.([]float64)
|
||||||
|
|
||||||
|
if nums[0] != 1 {
|
||||||
|
t.Errorf("nums[0] was not 1, got %f", nums[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
if nums[1] != 2 {
|
||||||
|
t.Errorf("nums[1] was not 2, got %f", nums[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
if nums[2] != 3 {
|
||||||
|
t.Errorf("nums[2] was not 3, got %f", nums[2])
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue