mirror of https://github.com/ImVexed/muon.git
Fixed cgo callbacks
This commit is contained in:
parent
06751b7aab
commit
78f2c420ef
114
muon.go
114
muon.go
|
@ -2,7 +2,7 @@ package muon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
@ -13,18 +13,18 @@ import (
|
||||||
|
|
||||||
// Window represents a single Ultralight instance
|
// Window represents a single Ultralight instance
|
||||||
type Window struct {
|
type Window struct {
|
||||||
wnd ULWindow
|
wnd ULWindow
|
||||||
ov ULOverlay
|
ov ULOverlay
|
||||||
view ULView
|
view ULView
|
||||||
app ULApp
|
app ULApp
|
||||||
handler http.Handler
|
handler http.Handler
|
||||||
cfg *Config
|
cfg *Config
|
||||||
|
callbacks map[string]*ipf
|
||||||
}
|
}
|
||||||
|
|
||||||
type ipf struct {
|
type ipf struct {
|
||||||
Function reflect.Value
|
Function reflect.Value
|
||||||
ParamTypes []reflect.Type
|
ParamTypes []reflect.Type
|
||||||
ReturnTypes []reflect.Type
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config contains configurable controls for the Ultralight engine
|
// Config contains configurable controls for the Ultralight engine
|
||||||
|
@ -44,8 +44,9 @@ type Config struct {
|
||||||
// New creates a Ultralight Window
|
// New creates a Ultralight Window
|
||||||
func New(cfg *Config, handler http.Handler) *Window {
|
func New(cfg *Config, handler http.Handler) *Window {
|
||||||
w := &Window{
|
w := &Window{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
handler: handler,
|
handler: handler,
|
||||||
|
callbacks: make(map[string]*ipf),
|
||||||
}
|
}
|
||||||
|
|
||||||
ufg := UlCreateConfig()
|
ufg := UlCreateConfig()
|
||||||
|
@ -104,6 +105,8 @@ func (w *Window) Start() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var registerCount int
|
||||||
|
|
||||||
// Bind registers the given function to the given name in the Window's JS global object
|
// Bind registers the given function to the given name in the Window's JS global object
|
||||||
func (w *Window) Bind(name string, function interface{}) {
|
func (w *Window) Bind(name string, function interface{}) {
|
||||||
f := &ipf{
|
f := &ipf{
|
||||||
|
@ -118,13 +121,13 @@ func (w *Window) Bind(name string, function interface{}) {
|
||||||
f.ParamTypes[i] = t.In(i)
|
f.ParamTypes[i] = t.In(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
f.ReturnTypes = make([]reflect.Type, t.NumOut())
|
if t.NumOut() > 1 {
|
||||||
|
panic("Too many return values!")
|
||||||
for i := 0; i < t.NumOut(); i++ {
|
|
||||||
f.ReturnTypes[i] = t.Out(i)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addFunctionToView(w.view, name, w.makeIPCCallback(f))
|
w.callbacks[name] = f
|
||||||
|
|
||||||
|
w.addFunction(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Eval evaluates a given JavaScript string in the given Window view. `ret` is necessary for JSON serialization if an object is returned.
|
// Eval evaluates a given JavaScript string in the given Window view. `ret` is necessary for JSON serialization if an object is returned.
|
||||||
|
@ -134,6 +137,7 @@ func (w *Window) Eval(js string, ret reflect.Type) (interface{}, error) {
|
||||||
|
|
||||||
ref := UlViewEvaluateScript(w.view, us)
|
ref := UlViewEvaluateScript(w.view, us)
|
||||||
ctx := UlViewGetJSContext(w.view)
|
ctx := UlViewGetJSContext(w.view)
|
||||||
|
|
||||||
val, err := fromJSValue(ctx, ref, ret)
|
val, err := fromJSValue(ctx, ref, ret)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -153,40 +157,41 @@ func (w *Window) Move(x int, y int) {
|
||||||
UlOverlayMoveTo(w.ov, int32(x), int32(y))
|
UlOverlayMoveTo(w.ov, int32(x), int32(y))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Window) makeIPCCallback(f *ipf) func(JSContextRef, JSObjectRef, JSObjectRef, uint, []JSValueRef, []JSValueRef) JSValueRef {
|
func (w *Window) ipcCallback(ctx JSContextRef, functin JSObjectRef, thisObject JSObjectRef, argumentCount uint, arguments []JSValueRef, exception []JSValueRef) JSValueRef {
|
||||||
return func(
|
jsName := JSStringCreateWithUTF8CString("name")
|
||||||
ctx JSContextRef,
|
defer JSStringRelease(jsName)
|
||||||
function JSObjectRef,
|
|
||||||
thisObject JSObjectRef,
|
|
||||||
argumentCount uint,
|
|
||||||
arguments []JSValueRef,
|
|
||||||
exception []JSValueRef) JSValueRef {
|
|
||||||
|
|
||||||
params := make([]reflect.Value, argumentCount)
|
prop := JSObjectGetProperty(ctx, functin, jsName, nil)
|
||||||
|
jsProp := JSValueToStringCopy(ctx, prop, nil)
|
||||||
|
defer JSStringRelease(jsProp)
|
||||||
|
|
||||||
for i := uint(0); i < argumentCount; i++ {
|
name := fromJSString(jsProp)
|
||||||
val, err := fromJSValue(ctx, arguments[i], f.ParamTypes[i])
|
|
||||||
|
|
||||||
if err != nil {
|
f := w.callbacks[name]
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
params[i] = val
|
params := make([]reflect.Value, argumentCount)
|
||||||
|
|
||||||
|
for i := uint(0); i < argumentCount; i++ {
|
||||||
|
val, err := fromJSValue(ctx, arguments[i], f.ParamTypes[i])
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
val := f.Function.Call(params)
|
params[i] = val
|
||||||
|
|
||||||
if len(val) > 1 {
|
|
||||||
panic("Javascript does not support more than 1 return value!")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(val) == 0 {
|
|
||||||
return JSValueMakeNull(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
return toJSValue(ctx, val[0])
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val := f.Function.Call(params)
|
||||||
|
|
||||||
|
if len(val) > 1 {
|
||||||
|
panic("Javascript does not support more than 1 return value!")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(val) == 0 {
|
||||||
|
return JSValueMakeNull(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return toJSValue(ctx, val[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
func fromJSValue(ctx JSContextRef, value JSValueRef, rtype reflect.Type) (reflect.Value, error) {
|
func fromJSValue(ctx JSContextRef, value JSValueRef, rtype reflect.Type) (reflect.Value, error) {
|
||||||
|
@ -205,6 +210,11 @@ func fromJSValue(ctx JSContextRef, value JSValueRef, rtype reflect.Type) (reflec
|
||||||
|
|
||||||
prop := JSObjectGetProperty(ctx, obj, l, nil)
|
prop := JSObjectGetProperty(ctx, obj, l, nil)
|
||||||
length := int(JSValueToNumber(ctx, prop, nil))
|
length := int(JSValueToNumber(ctx, prop, nil))
|
||||||
|
|
||||||
|
if rtype.Kind() != reflect.Slice {
|
||||||
|
return reflect.Zero(rtype), errors.New("JS return is of type Array while Go type target is not")
|
||||||
|
}
|
||||||
|
|
||||||
values := reflect.MakeSlice(rtype, length, length)
|
values := reflect.MakeSlice(rtype, length, length)
|
||||||
|
|
||||||
for i := 0; i < length; i++ {
|
for i := 0; i < length; i++ {
|
||||||
|
@ -289,20 +299,24 @@ func toJSValue(ctx JSContextRef, value reflect.Value) JSValueRef {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
|
||||||
return JSValueMakeNull(ctx)
|
return JSValueMakeNull(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
return jsv
|
return jsv
|
||||||
}
|
}
|
||||||
|
|
||||||
func addFunctionToView(view ULView, name string, callback JSObjectCallAsFunctionCallback) {
|
func (w *Window) addFunction(name string) {
|
||||||
ctx := UlViewGetJSContext(view)
|
ctx := UlViewGetJSContext(w.view)
|
||||||
gobj := JSContextGetGlobalObject(ctx)
|
gobj := JSContextGetGlobalObject(ctx)
|
||||||
|
|
||||||
fn := JSStringCreateWithUTF8CString(name)
|
fn := JSStringCreateWithUTF8CString(name)
|
||||||
defer JSStringRelease(fn)
|
defer JSStringRelease(fn)
|
||||||
|
|
||||||
fob := JSObjectMakeFunctionWithCallback(ctx, fn, callback)
|
fname := JSStringCreateWithUTF8CString("name")
|
||||||
|
defer JSStringRelease(fname)
|
||||||
|
|
||||||
|
fob := JSObjectMakeFunctionWithCallback(ctx, fn, w.ipcCallback)
|
||||||
|
JSObjectSetProperty(ctx, fob, fname, JSValueMakeString(ctx, fname), KJSPropertyAttributeNone, []JSValueRef{})
|
||||||
|
|
||||||
val := *(*JSValueRef)(unsafe.Pointer(&fob))
|
val := *(*JSValueRef)(unsafe.Pointer(&fob))
|
||||||
|
|
||||||
|
@ -322,7 +336,9 @@ func serveHandler(handler http.Handler) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
panic(http.Serve(ln, handler))
|
if err := http.Serve(ln, handler); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return "http://" + ln.Addr().String(), nil
|
return "http://" + ln.Addr().String(), nil
|
||||||
|
|
83
muon_test.go
83
muon_test.go
|
@ -2,10 +2,28 @@ package muon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var w *Window
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
cfg := &Config{
|
||||||
|
Height: 1,
|
||||||
|
Width: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
w = New(cfg, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
w.Start()
|
||||||
|
}()
|
||||||
|
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
||||||
|
|
||||||
type testObject struct {
|
type testObject struct {
|
||||||
S1 string
|
S1 string
|
||||||
F1 float64
|
F1 float64
|
||||||
|
@ -13,14 +31,8 @@ type testObject struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestComplexType(t *testing.T) {
|
func TestComplexType(t *testing.T) {
|
||||||
cfg := &Config{
|
|
||||||
Height: 1,
|
|
||||||
Width: 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
m := New(cfg, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
w.Bind("complexTest", func(to *testObject) *testObject {
|
||||||
|
|
||||||
m.Bind("test", func(to *testObject) *testObject {
|
|
||||||
return &testObject{
|
return &testObject{
|
||||||
S1: to.S1 + " World!",
|
S1: to.S1 + " World!",
|
||||||
F1: to.F1 + 1,
|
F1: to.F1 + 1,
|
||||||
|
@ -28,11 +40,7 @@ func TestComplexType(t *testing.T) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
go func() {
|
res, err := w.Eval(`complexTest({S1: "Hello,", F1: 9000, B1: false})`, reflect.TypeOf(&testObject{}))
|
||||||
m.Start()
|
|
||||||
}()
|
|
||||||
|
|
||||||
res, err := m.Eval(`test({S1: "Hello,", F1: 9000, B1: false})`, reflect.TypeOf(&testObject{}))
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
@ -53,15 +61,17 @@ func TestComplexType(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestArrayType(t *testing.T) {
|
func t2(to *testObject) *testObject {
|
||||||
cfg := &Config{
|
return &testObject{
|
||||||
Height: 1,
|
S1: to.S1 + " World!",
|
||||||
Width: 1,
|
F1: to.F1 + 1,
|
||||||
|
B1: !to.B1,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m := New(cfg, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
func TestArrayType(t *testing.T) {
|
||||||
|
|
||||||
m.Bind("test", func(strings []string) []float64 {
|
w.Bind("arrayTest", func(strings []string) []float64 {
|
||||||
if strings[0] != "Hello" {
|
if strings[0] != "Hello" {
|
||||||
t.Errorf("strings[0] was not Hello, got %s", strings[0])
|
t.Errorf("strings[0] was not Hello, got %s", strings[0])
|
||||||
}
|
}
|
||||||
|
@ -71,11 +81,7 @@ func TestArrayType(t *testing.T) {
|
||||||
return []float64{1, 2, 3}
|
return []float64{1, 2, 3}
|
||||||
})
|
})
|
||||||
|
|
||||||
go func() {
|
res, err := w.Eval(`arrayTest(["Hello","World!"])`, reflect.TypeOf([]float64{}))
|
||||||
m.Start()
|
|
||||||
}()
|
|
||||||
|
|
||||||
res, err := m.Eval(`test(["Hello","World!"])`, reflect.TypeOf([]float64{}))
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
@ -97,14 +103,8 @@ func TestArrayType(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEmptyType(t *testing.T) {
|
func TestEmptyType(t *testing.T) {
|
||||||
cfg := &Config{
|
|
||||||
Height: 1,
|
|
||||||
Width: 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
m := New(cfg, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
w.Bind("emptyTypeTest", func(nullValue string, undefinedValue string) {
|
||||||
|
|
||||||
m.Bind("test", func(nullValue string, undefinedValue string) {
|
|
||||||
if nullValue != "" {
|
if nullValue != "" {
|
||||||
t.Errorf("nullType was not empty!")
|
t.Errorf("nullType was not empty!")
|
||||||
}
|
}
|
||||||
|
@ -113,11 +113,24 @@ func TestEmptyType(t *testing.T) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
go func() {
|
_, err := w.Eval(`emptyTypeTest(null, undefined)`, nil)
|
||||||
m.Start()
|
|
||||||
}()
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
_, err := m.Eval(`test(null, undefined)`, nil)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMultipleFuncs(t *testing.T) {
|
||||||
|
w.Bind("multiple1Test", func(value1 string) {})
|
||||||
|
w.Bind("multiple2Test", func(value2 bool) {})
|
||||||
|
|
||||||
|
_, err := w.Eval(`multiple1Test("Hello, World1")`, nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = w.Eval(`multiple2Test(true)`, nil)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
|
Loading…
Reference in New Issue