100 lines
2.3 KiB
Go
100 lines
2.3 KiB
Go
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 ""
|
|
}
|
|
}
|