package gojs import ( "apigo.cloud/git/apigo/plugin" "apigo.cloud/git/apigo/qjs" "errors" "github.com/ssgo/log" "github.com/ssgo/u" "regexp" "strings" ) var pluginNameMatcher = regexp.MustCompile(`(\w+?)\.`) func Run(code string, globals map[string]interface{}, logger *log.Logger) (out interface{}) { // 初始化JS虚拟机 freeJsValues := make([]quickjs.Value, 0) rt := quickjs.NewRuntime() jsCtx := rt.NewContext() defer func() { if err := recover(); err != nil { logger.Error(u.String(err)) } for _, v := range freeJsValues { v.Free() } freeJsValues = make([]quickjs.Value, 0) jsCtx.Close() rt.Close() }() tryPlugins := map[string]bool{} for _, m := range pluginNameMatcher.FindAllStringSubmatch(code, 1024) { tryPlugins[m[1]] = true } goCtx := plugin.NewContext(map[string]interface{}{ "*log.Logger": logger, "*quickjs.Context": jsCtx, }) goCtx.SetData("_freeJsValues", &freeJsValues) for _, plg := range plugin.List() { if tryPlugins[plg.Id] { jsCtx.Globals().Set(plg.Id, MakeJsValueForPlugin(goCtx, plg.Objects, plg.Id, false)) if plg.JsCode != "" { if result, err := jsCtx.Eval(plg.JsCode); err != nil { stack := GetJSError(err, plg.JsCode) logger.Error(err.Error(), "stack", stack) } else { result.Free() } } } } // 全局变量 if globals != nil { for k, obj := range globals { jsCtx.Globals().Set(k, MakeJsValue(goCtx, obj, false)) } } // 运行API if r, err := jsCtx.Eval("(function(){" + code + "})()"); err == nil { result := MakeFromJsValue(r) r.Free() return result } else { // 检查错误 stack := GetJSError(err, code) logger.Error(err.Error(), "stack", stack) return nil } } var jsErrorCodeMatcher = regexp.MustCompile(`code:(\d+)`) func GetJSError(err error, code string) string { if err != nil { var jsErr *quickjs.Error if errors.As(err, &jsErr) { // 在错误信息中加入代码 codeLines := strings.Split(code, "\n") return jsErrorCodeMatcher.ReplaceAllStringFunc(jsErr.Stack, func(s2 string) string { errorLineNumber := u.Int(jsErrorCodeMatcher.FindStringSubmatch(s2)[1]) errorLine := "" if len(codeLines) >= errorLineNumber { errorLine = codeLines[errorLineNumber-1] } return s2 + " ```" + errorLine + "```" }) } else { return err.Error() } } else { return "" } }