add JSRuntime
This commit is contained in:
parent
7d632c5021
commit
c58bf345c6
@ -2,9 +2,9 @@ package gojs
|
||||
|
||||
import (
|
||||
"apigo.cloud/git/apigo/plugin"
|
||||
"apigo.cloud/git/apigo/qjs"
|
||||
"errors"
|
||||
"fmt"
|
||||
"apigo.cloud/git/apigo/qjs"
|
||||
"github.com/ssgo/log"
|
||||
"github.com/ssgo/u"
|
||||
"reflect"
|
||||
|
148
gojs.go
148
gojs.go
@ -4,6 +4,7 @@ import (
|
||||
"apigo.cloud/git/apigo/plugin"
|
||||
"apigo.cloud/git/apigo/qjs"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/ssgo/log"
|
||||
"github.com/ssgo/u"
|
||||
"regexp"
|
||||
@ -12,66 +13,137 @@ import (
|
||||
|
||||
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()
|
||||
}()
|
||||
type JSRuntime struct {
|
||||
freeJsValues []quickjs.Value
|
||||
rt quickjs.Runtime
|
||||
JsCtx *quickjs.Context
|
||||
GoCtx *plugin.Context
|
||||
logger *log.Logger
|
||||
plugins map[string]*plugin.Plugin
|
||||
}
|
||||
|
||||
func (rt *JSRuntime) Close() {
|
||||
for _, v := range rt.freeJsValues {
|
||||
v.Free()
|
||||
}
|
||||
rt.freeJsValues = make([]quickjs.Value, 0)
|
||||
rt.JsCtx.Close()
|
||||
rt.rt.Close()
|
||||
}
|
||||
|
||||
func (rt *JSRuntime) initCode(code string) {
|
||||
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 tryPlugins[plg.Id] && rt.plugins[plg.Id] == nil {
|
||||
rt.plugins[plg.Id] = &plg
|
||||
rt.JsCtx.Globals().Set(plg.Id, MakeJsValueForPlugin(rt.GoCtx, plg.Objects, plg.Id, false))
|
||||
if plg.JsCode != "" {
|
||||
if result, err := jsCtx.Eval(plg.JsCode); err != nil {
|
||||
if result, err := rt.JsCtx.Eval(plg.JsCode); err != nil {
|
||||
stack := GetJSError(err, plg.JsCode)
|
||||
logger.Error(err.Error(), "stack", stack)
|
||||
rt.logger.Error(err.Error(), "stack", stack)
|
||||
} else {
|
||||
result.Free()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (rt *JSRuntime) run(code string) (out interface{}, err error, stack string) {
|
||||
if r, err := rt.JsCtx.Eval(code); err == nil {
|
||||
result := MakeFromJsValue(r)
|
||||
r.Free()
|
||||
return result, nil, ""
|
||||
} else {
|
||||
// 检查错误
|
||||
stack := GetJSError(err, code)
|
||||
rt.logger.Error(err.Error(), "stack", stack)
|
||||
return nil, err, stack
|
||||
}
|
||||
}
|
||||
|
||||
func (rt *JSRuntime) Exec(code string) (err error, stack string) {
|
||||
rt.initCode(code)
|
||||
_, err, stack = rt.run(code)
|
||||
return err, stack
|
||||
}
|
||||
|
||||
func (rt *JSRuntime) Run(code string) (out interface{}, err error, stack string) {
|
||||
rt.initCode(code)
|
||||
return rt.run("(function(){" + code + "})()")
|
||||
}
|
||||
|
||||
func SetPluginsConfig(conf map[string]plugin.Config) {
|
||||
for _, plg := range plugin.List() {
|
||||
if plg.Init != nil {
|
||||
plg.Init(conf[plg.Id])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func New(globals map[string]interface{}, logger *log.Logger) *JSRuntime {
|
||||
if logger == nil {
|
||||
logger = log.DefaultLogger
|
||||
}
|
||||
|
||||
// 初始化JS虚拟机
|
||||
jsRt := quickjs.NewRuntime()
|
||||
jsCtx := jsRt.NewContext()
|
||||
goCtx := plugin.NewContext(map[string]interface{}{
|
||||
"*log.Logger": logger,
|
||||
"*quickjs.Context": jsCtx,
|
||||
})
|
||||
|
||||
rt := &JSRuntime{
|
||||
freeJsValues: make([]quickjs.Value, 0),
|
||||
rt: jsRt,
|
||||
JsCtx: jsCtx,
|
||||
GoCtx: goCtx,
|
||||
logger: logger,
|
||||
plugins: map[string]*plugin.Plugin{},
|
||||
}
|
||||
|
||||
rt.GoCtx.SetData("_freeJsValues", &rt.freeJsValues)
|
||||
|
||||
// 全局变量
|
||||
if globals != nil {
|
||||
for k, obj := range globals {
|
||||
jsCtx.Globals().Set(k, MakeJsValue(goCtx, obj, false))
|
||||
rt.JsCtx.Globals().Set(k, MakeJsValue(rt.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
|
||||
}
|
||||
// 注入 console
|
||||
rt.JsCtx.Globals().Set("console", MakeJsValue(rt.GoCtx, map[string]interface{}{
|
||||
"log": func(args ...interface{}) {
|
||||
fmt.Println(args...)
|
||||
},
|
||||
"info": func(args ...interface{}) {
|
||||
fmt.Println(u.Cyan(fmt.Sprintln(args...)))
|
||||
},
|
||||
"warn": func(args ...interface{}) {
|
||||
fmt.Println(u.BYellow(fmt.Sprintln(args...)))
|
||||
},
|
||||
"error": func(args ...interface{}) {
|
||||
fmt.Println(u.BRed(fmt.Sprintln(args...)))
|
||||
},
|
||||
}, false))
|
||||
|
||||
return rt
|
||||
}
|
||||
|
||||
func Run(code string, globals map[string]interface{}, logger *log.Logger) (out interface{}, err error, stack string) {
|
||||
rt := New(globals, logger)
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
rt.logger.Error(u.String(err))
|
||||
}
|
||||
rt.Close()
|
||||
}()
|
||||
return rt.Run(code)
|
||||
}
|
||||
|
||||
var jsErrorCodeMatcher = regexp.MustCompile(`code:(\d+)`)
|
||||
|
10
gojs_test.go
10
gojs_test.go
@ -63,22 +63,22 @@ return plus(number,2)
|
||||
"plus": func(i, j int) int { return i + j },
|
||||
}
|
||||
|
||||
r := gojs.Run(code, globals, log.DefaultLogger)
|
||||
r, _, _ := gojs.Run(code, globals, log.DefaultLogger)
|
||||
test(t, "call", u.Int(r) == 11, r)
|
||||
}
|
||||
|
||||
func TestPlugin(t *testing.T) {
|
||||
r := gojs.Run("return obj.getId()", nil, log.DefaultLogger)
|
||||
r, _, _ := gojs.Run("return obj.getId()", nil, log.DefaultLogger)
|
||||
test(t, "obj.getId()", u.String(r) == "o-00", r)
|
||||
|
||||
r = gojs.Run(`
|
||||
r, _, _ = gojs.Run(`
|
||||
o = obj.new('o-01')
|
||||
return o.getId()
|
||||
`, nil, log.DefaultLogger)
|
||||
test(t, "new obj.getId()", u.String(r) == "o-01", r)
|
||||
|
||||
t1 := time.Now()
|
||||
r = gojs.Run(`
|
||||
r, _, _ = gojs.Run(`
|
||||
out = ''
|
||||
obj.echo('123', function(text){
|
||||
out = text
|
||||
@ -90,7 +90,7 @@ return out
|
||||
test(t, "callback", u.String(r) == "123", r)
|
||||
|
||||
t1 = time.Now()
|
||||
r = gojs.Run(`
|
||||
r, _, _ = gojs.Run(`
|
||||
out = ''
|
||||
obj.echoTimes(function(text){
|
||||
out += text
|
||||
|
Loading…
Reference in New Issue
Block a user