gojs/gojs.go

100 lines
2.3 KiB
Go
Raw Permalink Normal View History

2024-02-17 12:55:08 +08:00
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 ""
}
}