From 9e0d17f0a3d9304cd50a11202c9f864a1135bf97 Mon Sep 17 00:00:00 2001 From: Star <> Date: Thu, 4 Jul 2024 11:10:22 +0800 Subject: [PATCH] update quickjs, support async by gojs.Start --- bridge.go | 2 +- go.mod | 12 +++---- gojs.go | 90 +++++++++++++++++++++++++++++++++++++++++++++++++--- gojs_test.go | 48 ++++++++++++++++++++++++++-- 4 files changed, 138 insertions(+), 14 deletions(-) diff --git a/bridge.go b/bridge.go index 5e2f599..fb74eb3 100644 --- a/bridge.go +++ b/bridge.go @@ -2,7 +2,7 @@ package gojs import ( "apigo.cc/apigo/plugin" - quickjs "apigo.cc/apigo/qjs" + "apigo.cc/apigo/quickjs-go" "errors" "fmt" "github.com/ssgo/log" diff --git a/go.mod b/go.mod index 557b57c..ba929e5 100644 --- a/go.mod +++ b/go.mod @@ -1,16 +1,16 @@ module apigo.cc/apigo/gojs -go 1.17 +go 1.18 require ( apigo.cc/apigo/plugin v1.0.2 - github.com/ssgo/log v1.7.5 - github.com/ssgo/u v1.7.5 + apigo.cc/apigo/quickjs-go v0.4.12 + github.com/ssgo/log v1.7.6 + github.com/ssgo/u v1.7.6 ) require ( - apigo.cc/apigo/qjs v0.0.3 // indirect - github.com/ssgo/config v1.7.5 // indirect - github.com/ssgo/standard v1.7.5 // indirect + github.com/ssgo/config v1.7.6 // indirect + github.com/ssgo/standard v1.7.6 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/gojs.go b/gojs.go index 989cd9d..186a9a0 100644 --- a/gojs.go +++ b/gojs.go @@ -2,7 +2,7 @@ package gojs import ( "apigo.cc/apigo/plugin" - quickjs "apigo.cc/apigo/qjs" + "apigo.cc/apigo/quickjs-go" "errors" "fmt" "github.com/ssgo/log" @@ -248,7 +248,9 @@ func PreCompile(code, filename string, logger *log.Logger) (*PreCompiledCode, er } return "let " + m[1] + " = " + varName } else { - return "throw new Error('import file not found: " + jsFile + " in [" + strings.Join(searchList, ", ") + "]')" + // import 未知模块是保留代码 + //return "throw new Error('import file not found: " + jsFile + " in [" + strings.Join(searchList, ", ") + "]')" + return importStr } } else { outErr = err @@ -290,6 +292,12 @@ func (rt *JSRuntime) ExecPreCompiled(cc *PreCompiledCode) (jsErr *JSError) { return err } +func (rt *JSRuntime) StartPreCompiled(cc *PreCompiledCode) (jsErr *JSError) { + err := rt.ExecPreCompiled(cc) + rt.JsCtx.Loop() + return err +} + func (rt *JSRuntime) RunPreCompiled(cc *PreCompiledCode) (out interface{}, jsErr *JSError) { return rt.runPreCompiled(cc, true, "") } @@ -304,9 +312,12 @@ func (rt *JSRuntime) runPreCompiled(cc *PreCompiledCode, isClosure bool, setToVa } } for _, plg := range cc.Plugins { - rt.JsCtx.Globals().Set(fixPluginId(plg.Id), MakeJsValueForPlugin(rt.GoCtx, plg.Objects, plg.Id, false)) + plgImpVar := fixPluginId(plg.Id) + rt.JsCtx.Globals().Set(plgImpVar, MakeJsValueForPlugin(rt.GoCtx, plg.Objects, plg.Id, false)) if plg.JsCode != "" { - if result, err := rt.JsCtx.EvalFile(plg.JsCode, plg.Id+".js"); err != nil { + jsCode := strings.ReplaceAll(plg.JsCode, "${OBJECT}", plgImpVar) + //if result, err := rt.JsCtx.EvalFile(jsCode, plg.Id+".js"); err != nil { + if result, err := rt.JsCtx.Eval(jsCode, quickjs.EvalFileName(plg.Id+".js")); err != nil { stack := rt.getJSError(err) rt.logger.Error(err.Error(), "stack", stack) } else { @@ -330,7 +341,8 @@ func (rt *JSRuntime) runPreCompiled(cc *PreCompiledCode, isClosure bool, setToVa } } - if r, err := rt.JsCtx.EvalFile(fixedCode, cc.Filename); err == nil { + //if r, err := rt.JsCtx.EvalFile(fixedCode, cc.Filename); err == nil { + if r, err := rt.JsCtx.Eval(fixedCode, quickjs.EvalFileName(cc.Filename)); err == nil { result := MakeFromJsValue(r, rt.GoCtx) r.Free() return result, nil @@ -473,6 +485,30 @@ func (rt *JSRuntime) ExecAtFile(code string, filename string) (jsErr *JSError) { return jsErr } +func (rt *JSRuntime) Start(code string) (jsErr *JSError) { + err := rt.ExecAt(code, "") + rt.JsCtx.Loop() + return err +} + +func (rt *JSRuntime) StartAt(code string, dir string) (jsErr *JSError) { + err := rt.ExecAt(code, dir) + rt.JsCtx.Loop() + return err +} + +func (rt *JSRuntime) StartFile(filename string) (jsErr *JSError) { + err := rt.ExecFile(filename) + rt.JsCtx.Loop() + return err +} + +func (rt *JSRuntime) StartAtFile(code string, filename string) (jsErr *JSError) { + err := rt.ExecAtFile(code, filename) + rt.JsCtx.Loop() + return err +} + func (rt *JSRuntime) Run(code string) (out interface{}, jsErr *JSError) { return rt.RunAt(code, "") } @@ -758,6 +794,50 @@ func RunPreCompiled(cc *PreCompiledCode, option *RuntimeOption) (out interface{} return rt.RunPreCompiled(cc) } +func Start(code string, option *RuntimeOption) *JSError { + rt := New(option) + defer func() { + if err := recover(); err != nil { + rt.logger.Error(u.String(err)) + } + rt.Close() + }() + return rt.Start(code) +} + +func StartAt(code, dir string, option *RuntimeOption) *JSError { + rt := New(option) + defer func() { + if err := recover(); err != nil { + rt.logger.Error(u.String(err)) + } + rt.Close() + }() + return rt.StartAt(code, dir) +} + +func StartFile(filename string, option *RuntimeOption) *JSError { + rt := New(option) + defer func() { + if err := recover(); err != nil { + rt.logger.Error(u.String(err)) + } + rt.Close() + }() + return rt.StartFile(filename) +} + +func StartPreCompiled(cc *PreCompiledCode, option *RuntimeOption) *JSError { + rt := New(option) + defer func() { + if err := recover(); err != nil { + rt.logger.Error(u.String(err)) + } + rt.Close() + }() + return rt.StartPreCompiled(cc) +} + var jsErrorCodeMatcher = regexp.MustCompile(`([\w./\\\-]+):(\d+)`) func (rt *JSRuntime) getJSError(err error) string { diff --git a/gojs_test.go b/gojs_test.go index adc358e..f1c2d92 100644 --- a/gojs_test.go +++ b/gojs_test.go @@ -266,6 +266,51 @@ func TestCallback(t *testing.T) { rt.Close() } +func TestInnerModule(t *testing.T) { + rt := gojs.New(nil) + rt.Exec(` + import {loadFile} from "std" + import * as os from "os" + import console from 'console' + globalThis.goMod = loadFile('go.mod') + globalThis.platform = os.platform +`) + goModStr, _ := rt.Run(` + return goMod + `) + platformStr, _ := rt.Run(` + return platform + `) + test(t, "check std.loadFile", strings.Contains(u.String(goModStr), "apigo"), goModStr) + test(t, "check os.platform", u.String(platformStr) != "", platformStr) + rt.Close() +} + +func TestAsync(t *testing.T) { + rt := gojs.New(nil) + rt.Start(` + 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) +`) + timeoutValue, _ := rt.Run(` + return timeoutValue + `) + promiseValue, _ := rt.Run(` + return promiseValue + `) + test(t, "check timeoutValue", u.Int(timeoutValue) >= 300 && u.Int(timeoutValue) <= 400, timeoutValue) + test(t, "check promiseValue", u.Int(promiseValue) >= 200 && u.Int(promiseValue) <= 300, promiseValue) + rt.Close() +} + func BenchmarkEcho(tb *testing.B) { tb.StopTimer() ms1 := runtime.MemStats{} @@ -291,13 +336,12 @@ func BenchmarkCallback(tb *testing.B) { runtime.ReadMemStats(&ms1) tb.StartTimer() for i := 0; i < tb.N; i++ { - gojs.Run(` + gojs.Start(` import obj from 'obj' out = '' obj.echoTimes(function(text){ out += text }) -return out `, nil) } tb.StopTimer()