package gojs_test import ( "apigo.cc/apigo/gojs" "apigo.cc/apigo/gojs/dop251/goja" _ "apigo.cc/apigo/gojs/modules" "fmt" "github.com/ssgo/u" "runtime" "testing" "time" ) type Object struct { id string //plus func(int, int) int plus goja.Callable baseNumber int } //type PlusConfig struct { // BaseNumber int // //Action func(int, int) int // Action goja.Callable //} func (obj *Object) GetId(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { return vm.ToValue(obj.id) } // func (obj *Object) SetPlusFuncAndRun(cb func(int, int) int) int { func (obj *Object) SetPlusFuncAndRun(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) obj.plus = args.Func(0) r, err := obj.plus(args.This, vm.ToValue(1), vm.ToValue(2)) if err != nil { panic(vm.NewGoError(err)) } return r } // func (obj *Object) SetPlusFunc(cb func(int, int) int) { func (obj *Object) SetPlusFunc(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { //obj.plus = cb args := gojs.MakeArgs(&argsIn, vm).Check(1) obj.plus = args.Func(0) return nil } // func (obj *Object) SetPlusFunc2(conf PlusConfig) { func (obj *Object) SetPlusFunc2(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { //obj.baseNumber = conf.BaseNumber //obj.plus = conf.Action args := gojs.MakeArgs(&argsIn, vm).Check(1) o := args.Obj(0) //baseNumber := o.Get("baseNumber") //if baseNumber != nil { // obj.baseNumber = int(baseNumber.ToInteger()) //} obj.baseNumber = o.Int("baseNumber") //action := o.Get("action") //if action != nil { // obj.plus = gojs.GetFunc(action) //} obj.plus = o.Func("action") return nil } // func (obj *Object) Plus(a ...int) int { func (obj *Object) Plus(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(2) r, err := obj.plus(args.This, args.Arguments[0], args.Arguments[1]) if err != nil { panic(vm.NewGoError(err)) } return r } // func (obj *Object) PlusX(a int, b int) int { func (obj *Object) PlusX(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(2) //return obj.plus(a, b) + obj.baseNumber r, err := obj.plus(args.This, args.Arguments[0], args.Arguments[1]) if err != nil { panic(vm.NewGoError(err)) } return vm.ToValue(r.ToInteger() + int64(obj.baseNumber)) } // func (obj *Object) AsyncPlus(a int, b int) int { func (obj *Object) AsyncPlus(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(2) //ch := make(chan bool, 1) //r := 0 //go func() { // time.Sleep(300 * time.Millisecond) // r = obj.plus(a, b) // ch <- true //}() //<-ch //return r ch := make(chan bool, 1) var r goja.Value var err error go func() { time.Sleep(300 * time.Millisecond) //r = obj.plus(a, b) r, err = obj.plus(args.This, args.Arguments[0], args.Arguments[1]) ch <- true }() <-ch if err != nil { panic(vm.NewGoError(err)) } return r } type TestBirthday struct { Year int Month int Day int } type TestBaseUser struct { Id int Name string } type TestUser struct { TestBaseUser Birthday *TestBirthday } // func (b *TestBirthday) String() string { func (b *TestBirthday) String(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { //return fmt.Sprintln(b.Year, b.Month, b.Day) return vm.ToValue(fmt.Sprintln(b.Year, b.Month, b.Day)) } // func (u *TestUser) GetBirthdayString() string { func (u *TestUser) GetBirthdayString(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { //return u.Birthday.String() return vm.ToValue(fmt.Sprintln(u.Birthday.Year, u.Birthday.Month, u.Birthday.Day)) } func init() { defaultObject := Object{id: "o-00"} gojs.Register("aaa/obj", gojs.Module{ Object: map[string]interface{}{ "name": "1233", "list1": []interface{}{1, "2", map[string]interface{}{"aaa": 111, "bbb": "222"}, true}, //"log": log.DefaultLogger.Info, "log1": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) args.Logger.Info(args.Str(0), args.Map2Arr(1)...) return nil }, //"getId": defaultObject.GetId, "getId": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { return vm.ToValue(defaultObject.id) }, "new": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) return vm.ToValue(Object{id: args.Str(0)}) }, //"new": func(id string) interface{} { // return &Object{id: id} //}, "test1": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { //Args := gojs.MakeArgs(&argsIn, vm).Check(3) return nil }, //"test1": func(id int, id2 uint16, id3 float64) *TestUser { // return nil //}, "echo": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(2) r, err := args.Func(1)(args.This, args.Arguments[0], vm.ToValue(1)) if err != nil { panic(vm.NewGoError(err)) } return r }, //"echo": func(text string, echoFunc func(text string, t2 int) string, t3 []uint32, t4 *bool) interface{} { // return echoFunc(text, 0) //}, "echoTimes": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) echoFunc := args.Func(0) for i := 0; i < 5; i++ { //time.Sleep(100 * time.Millisecond) _, err := echoFunc(args.This, vm.ToValue(fmt.Sprint(i))) if err != nil { panic(vm.NewGoError(err)) } } return nil }, //"echoTimes": func(echoFunc func(text string)) { // for i := 0; i < 5; i++ { // //time.Sleep(100 * time.Millisecond) // echoFunc(fmt.Sprint(i)) // } //}, }, TsCode: "", Example: "", }) //plg := plugin.Plugin{ // Id: "obj", // Name: "test obj plugin", // Objects: map[string]interface{}{ // "name": "1233", // "list1": []interface{}{1, "2", map[string]interface{}{"aaa": 111, "bbb": "222"}, true}, // "log1": log.DefaultLogger.Info, // "getId": defaultObject.GetId, // "new": func(id string) interface{} { // return &Object{id: id} // }, // "test1": func(id int, id2 uint16, id3 float64) *TestUser { // return nil // }, // "echo": func(text string, echoFunc func(text string, t2 int) string, t3 []uint32, t4 *bool) interface{} { // return echoFunc(text, 0) // }, // "echoTimes": func(echoFunc func(text string)) { // for i := 0; i < 5; i++ { // //time.Sleep(100 * time.Millisecond) // echoFunc(fmt.Sprint(i)) // } // }, // }, //} //plugin.Register(plg) //gojs.SetPluginsConfig(map[string]plugin.Config{ // "obj": plugin.Config{}, //}) } func test(t *testing.T, name string, check bool, extArgs ...interface{}) { if check { fmt.Println(u.Green(name), u.BGreen("[OK]")) } else { fmt.Println(u.Red(name), u.BRed("[Failed]"), fmt.Sprintln(extArgs...)) t.Fatal(name) } } //func TestTS(t *testing.T) { // plg := plugin.Get("obj") // exportCode, _ := gojs.MakePluginCode(plg) // test(t, "ts code", strings.Contains(exportCode, "(echoFunc: (text: string) => void)"), exportCode) //} func TestGlobal(t *testing.T) { code := ` import lg from 'log' import console from 'console' log1('test', 'name', 'log') lg.info('test', {name:'log'}) console.info('test', {name:'log'}) return plus(number,2) ` globals := map[string]interface{}{ "number": 9, //"log1": log.DefaultLogger.Info, "log1": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) args.Logger.Info(args.Str(0), args.Map2Arr(1)...) return nil }, //"plus": func(i, j int) int { return i + j }, "plus": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(2) return vm.ToValue(args.Int(0) + args.Int(1)) }, } //r, _ := gojs.Run(code, &gojs.RuntimeOption{ // Globals: globals, //}) rt := gojs.New() _ = rt.SetGlobal(globals) err := rt.StartFromCode(code, "test.js") if err != nil { t.Fatal(err.Error()) } r, err := rt.RunMain() if err != nil { t.Fatal(err.Error()) } test(t, "call", u.Int(r) == 11, r) } func TestPlugin(t *testing.T) { rt := gojs.New() _, _ = rt.RunCode("import obj from 'aaa/obj'") r, err := rt.RunCode("obj.getId()") test(t, "obj.getId()", u.String(r) == "o-00", r, err) r, err = rt.RunCode(` let o = obj.new('o-01') o.GetId() `) test(t, "new obj.getId()", u.String(r) == "o-01", r, err) t1 := time.Now() r, err = rt.RunCode(` out = '' obj.echo('123', function(text){ out = text }, null) out `) t2 := time.Now() fmt.Println("time:", t2.UnixMicro()-t1.UnixMicro()) test(t, "callback", u.String(r) == "123", r, err) t1 = time.Now() r, err = rt.RunCode(` out = '' obj.echoTimes(function(text){ out += text }) out `) t2 = time.Now() fmt.Println("time:", t2.UnixMicro()-t1.UnixMicro()) test(t, "callbacks", u.String(r) == "01234", r, err) } func TestCallback(t *testing.T) { r, err := gojs.Run(` import obj from 'aaa/obj' o = obj.new('cb') return o.SetPlusFuncAndRun(function (a, b){ return a+b }) `, "") test(t, "plus with callback1", u.String(r) == "3", r, err) r, err = gojs.Run(` import obj from 'aaa/obj' o = obj.new('cb') o.SetPlusFunc(function(a, b){ return a+b }) return o.Plus(1, 2) `, "") test(t, "plus with callback2.1", u.String(r) == "3", r, err) r, err = gojs.Run(` import obj from 'aaa/obj' o = obj.new('cb') function plus(a, b){ return a+b } o.SetPlusFunc(plus) return o.Plus(1, 2) `, "") test(t, "plus with callback2.2", u.String(r) == "3", r, err) r, err = gojs.Run(` import obj from 'aaa/obj' o = obj.new('cb') o.SetPlusFunc2({action: function(a, b){ return a+b }}) return o.Plus(1, 2) `, "") test(t, "plus with callback3.1", u.String(r) == "3", r, err) r, err = gojs.Run(` import obj from 'aaa/obj' o = obj.new('cb') function plus(a, b){ return a+b } o.SetPlusFunc2({baseNumber:10, action: plus}) return o.PlusX(1, 2) `, "") test(t, "plus with callback3.2", u.String(r) == "13", r, err) rt := gojs.New() rt.RunCode(` import obj from 'aaa/obj' o = obj.new('cb') `) rt.RunCode(` o.SetPlusFunc2({action: function(a, b){ return a+b }}) `) r, err = rt.RunCode(` o.Plus(1, 2) `) test(t, "plus with callback4.1", u.String(r) == "3", r, err) r, err = rt.RunCode(` o.AsyncPlus(1, 2) `) // unexpected, maybe get exception in other go coroutine test(t, "plus with callback4.2", u.String(r) == "3" || u.String(r) == "0", r, err) } //func TestInnerModule(t *testing.T) { // rt := gojs.New() // rt.RunCode(` // import {loadFile} from "std" // import * as os from "os" // import console from 'console' // globalThis.goMod = loadFile('go.mod') // globalThis.platform = os.platform //`) // goModStr, _ := rt.RunCode(` // goMod // `) // platformStr, _ := rt.RunCode(` // platform // `) // test(t, "check std.loadFile", strings.Contains(u.String(goModStr), "apigo"), goModStr) // test(t, "check os.platform", u.String(platformStr) != "", platformStr) //} func TestAsync(t *testing.T) { rt := gojs.New() _, err := rt.RunCode(` import console from 'console' let t1 = new Date().getTime() new Promise(resolve => { setTimeout(resolve, 200) }).then(()=>{ globalThis.promiseValue = new Date().getTime() - t1 }) setTimeout(()=>{ globalThis.timeoutValue = new Date().getTime() - t1 }, 300) `) time.Sleep(310 * time.Millisecond) timeoutValue, err := rt.RunCode(` timeoutValue `) test(t, "check timeoutValue", u.Int(timeoutValue) >= 300 && u.Int(timeoutValue) <= 400, timeoutValue, err) promiseValue, err := rt.RunCode(` promiseValue `) test(t, "check promiseValue", u.Int(promiseValue) >= 200 && u.Int(promiseValue) <= 300, promiseValue, err) } func BenchmarkEcho(tb *testing.B) { tb.StopTimer() ms1 := runtime.MemStats{} runtime.ReadMemStats(&ms1) tb.StartTimer() for i := 0; i < tb.N; i++ { gojs.Run(`return 1`, "") } tb.StopTimer() ms2 := runtime.MemStats{} runtime.ReadMemStats(&ms2) runtime.GC() ms3 := runtime.MemStats{} runtime.ReadMemStats(&ms3) fmt.Println(">>", ms1.HeapInuse, ms2.HeapInuse, ms3.HeapInuse) } func BenchmarkCallback(tb *testing.B) { tb.StopTimer() ms1 := runtime.MemStats{} runtime.ReadMemStats(&ms1) tb.StartTimer() for i := 0; i < tb.N; i++ { gojs.Run(` import obj from 'aaa/obj' out = '' obj.echoTimes(function(text){ out += text }) `, "") } tb.StopTimer() ms2 := runtime.MemStats{} runtime.ReadMemStats(&ms2) runtime.GC() ms3 := runtime.MemStats{} runtime.ReadMemStats(&ms3) fmt.Println(">>", ms1.HeapInuse, ms2.HeapInuse, ms3.HeapInuse) } //func TestExport(t *testing.T) { // fmt.Println(u.BCyan(gojs.ExportForDev())) //}