first
This commit is contained in:
parent
afdf74bcd9
commit
7d632c5021
25
.gitignore
vendored
25
.gitignore
vendored
@ -1,23 +1,2 @@
|
||||
# ---> Go
|
||||
# If you prefer the allow list template instead of the deny list, see community template:
|
||||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||
#
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
# Go workspace file
|
||||
go.work
|
||||
|
||||
.*
|
||||
/go.sum
|
||||
|
422
bridge.go
Normal file
422
bridge.go
Normal file
@ -0,0 +1,422 @@
|
||||
package gojs
|
||||
|
||||
import (
|
||||
"apigo.cloud/git/apigo/plugin"
|
||||
"errors"
|
||||
"fmt"
|
||||
"apigo.cloud/git/apigo/qjs"
|
||||
"github.com/ssgo/log"
|
||||
"github.com/ssgo/u"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func MakeJsValue(ctx *plugin.Context, in interface{}, inArray bool) quickjs.Value {
|
||||
return _makeJsValue(ctx, in, 0, "", "", inArray)
|
||||
}
|
||||
|
||||
func MakeJsValueForPlugin(ctx *plugin.Context, in interface{}, pluginName string, inArray bool) quickjs.Value {
|
||||
return _makeJsValue(ctx, in, 0, "", pluginName, inArray)
|
||||
}
|
||||
|
||||
func makeLowerCaseStartWord(str string) string {
|
||||
if len(str) > 0 && str[0] >= 'A' && str[0] <= 'Z' {
|
||||
return string(str[0]+32) + str[1:]
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
func _makeJsValue(ctx *plugin.Context, in interface{}, n int, key string, pluginName string, inArray bool) quickjs.Value {
|
||||
if n > 100 {
|
||||
return quickjs.Value{}
|
||||
}
|
||||
jsCtx, jsCtxOk := ctx.GetInject("*quickjs.Context").(*quickjs.Context)
|
||||
if !jsCtxOk {
|
||||
return quickjs.Value{}
|
||||
}
|
||||
|
||||
if err, isErr := in.(error); isErr {
|
||||
return jsCtx.ThrowError(err)
|
||||
}
|
||||
|
||||
var v reflect.Value
|
||||
//var ov reflect.To
|
||||
if inV, ok := in.(reflect.Value); ok {
|
||||
v = inV
|
||||
} else {
|
||||
//ov = reflect.ValueOf(in)
|
||||
v = reflect.ValueOf(in)
|
||||
}
|
||||
for v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.Bool:
|
||||
return jsCtx.Bool(v.Bool())
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32:
|
||||
return jsCtx.Int32(int32(v.Int()))
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32:
|
||||
return jsCtx.Uint32(uint32(v.Uint()))
|
||||
case reflect.Int64:
|
||||
return jsCtx.Int64(v.Int())
|
||||
case reflect.Uint64:
|
||||
return jsCtx.Int64(int64(v.Uint()))
|
||||
case reflect.String:
|
||||
return jsCtx.String(v.String())
|
||||
case reflect.Float32, reflect.Float64:
|
||||
//return jsCtx.String(u.String(v.Float()))
|
||||
return jsCtx.Float64(v.Float())
|
||||
case reflect.Slice:
|
||||
if v.Kind() == reflect.Slice && v.Type().Elem().Kind() == reflect.Uint8 {
|
||||
originBuf := v.Bytes()
|
||||
if len(originBuf) == 0 {
|
||||
// 空的ArrayBuffer用空的Array代替
|
||||
arr := jsCtx.Array()
|
||||
arrValue := arr.ToValue()
|
||||
if inArray {
|
||||
if freeJsValues, ok := ctx.GetData("_freeJsValues").(*[]quickjs.Value); ok {
|
||||
*freeJsValues = append(*freeJsValues, arrValue)
|
||||
}
|
||||
}
|
||||
return arrValue
|
||||
}
|
||||
//if freeJsValues, ok := ctx.GetData("_freeJsValues").(*[]quickjs.Value); ok {
|
||||
// *freeJsValues = append(*freeJsValues, bufValue)
|
||||
//}
|
||||
return jsCtx.ArrayBuffer(originBuf)
|
||||
//arr := jsCtx.Array()
|
||||
//for _, b := range v.Bytes() {
|
||||
// o := jsCtx.Uint32(uint32(b))
|
||||
// arr.Push(o)
|
||||
//}
|
||||
//if inArray {
|
||||
// serverLogger.Error("==>2", n, key, "arr", "==")
|
||||
// freeJsValues := ctx.GetData("_freeJsValues").(*[]quickjs.Value)
|
||||
// *freeJsValues = append(*freeJsValues, arr.ToValue())
|
||||
//}
|
||||
//return arr.ToValue()
|
||||
} else {
|
||||
arr := jsCtx.Array()
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
o := _makeJsValue(ctx, v.Index(i), n+1, "", pluginName, true)
|
||||
if o.IsObject() || o.IsArray() || o.IsMap() {
|
||||
if freeJsValues, ok := ctx.GetData("_freeJsValues").(*[]quickjs.Value); ok {
|
||||
*freeJsValues = append(*freeJsValues, o)
|
||||
}
|
||||
}
|
||||
arr.Push(o)
|
||||
}
|
||||
arrValue := arr.ToValue()
|
||||
if inArray {
|
||||
if freeJsValues, ok := ctx.GetData("_freeJsValues").(*[]quickjs.Value); ok {
|
||||
*freeJsValues = append(*freeJsValues, arrValue)
|
||||
}
|
||||
}
|
||||
return arrValue
|
||||
}
|
||||
case reflect.Struct:
|
||||
o := jsCtx.Object()
|
||||
var structInfo *u.StructInfo
|
||||
if v.CanAddr() {
|
||||
structInfo = u.FlatStruct(v.Addr())
|
||||
} else {
|
||||
structInfo = u.FlatStruct(v)
|
||||
}
|
||||
for k2, v2 := range structInfo.Values {
|
||||
o.Set(makeLowerCaseStartWord(k2), _makeJsValue(ctx, v2, n+1, k2, pluginName, false))
|
||||
}
|
||||
for k2, v2 := range structInfo.MethodValues {
|
||||
o.Set(makeLowerCaseStartWord(k2), _makeJsValue(ctx, v2, n+1, k2, pluginName, false))
|
||||
}
|
||||
return o
|
||||
case reflect.Map:
|
||||
o := jsCtx.Object()
|
||||
for _, k2 := range v.MapKeys() {
|
||||
k2s := u.String(u.FinalValue(k2).Interface())
|
||||
if len(k2s) > 0 && k2s[0] != '_' {
|
||||
o.Set(k2s, _makeJsValue(ctx, v.MapIndex(k2), n+1, k2s, pluginName, false))
|
||||
}
|
||||
}
|
||||
return o
|
||||
case reflect.Func:
|
||||
t := v.Type()
|
||||
return jsCtx.Function(func(js *quickjs.Context, this quickjs.Value, args []quickjs.Value) quickjs.Value {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
ctx.GetInject("*log.Logger").(*log.Logger).Error(u.String(err), "func", key)
|
||||
}
|
||||
}()
|
||||
needArgs := make([]reflect.Type, 0)
|
||||
realArgs := make([]reflect.Value, t.NumIn())
|
||||
needArgsIndex := map[int]int{}
|
||||
for i := 0; i < t.NumIn(); i++ {
|
||||
inTypeString := t.In(i).String()
|
||||
if inTypeString == "*plugin.Context" {
|
||||
realArgs[i] = reflect.ValueOf(ctx)
|
||||
continue
|
||||
//} else if inTypeString == "plugin.Config" {
|
||||
// pluginConf := GetPluginConfig(pluginName)
|
||||
// realArgs[i] = reflect.ValueOf(pluginConf)
|
||||
// continue
|
||||
} else if injectObject := ctx.GetInject(inTypeString); injectObject != nil {
|
||||
realArgs[i] = reflect.ValueOf(injectObject)
|
||||
continue
|
||||
}
|
||||
|
||||
if !realArgs[i].IsValid() {
|
||||
needArgs = append(needArgs, t.In(i))
|
||||
needArgsIndex[len(needArgsIndex)] = i
|
||||
}
|
||||
}
|
||||
|
||||
//if len(args) < len(needArgs) {
|
||||
// return js.ThrowError(errors.New(fmt.Sprintf("call %s no enough args, need %d, given %d", key, len(needArgs), len(args))))
|
||||
//}
|
||||
for i, needArgType := range needArgs {
|
||||
var argValue reflect.Value
|
||||
isLastVariadicArg := false
|
||||
if v.Type().IsVariadic() && needArgsIndex[i] == len(realArgs)-1 {
|
||||
// 可变参数函数的最后一个使用成员类型
|
||||
isLastVariadicArg = true
|
||||
argValue = reflect.New(needArgType.Elem())
|
||||
} else {
|
||||
argValue = reflect.New(needArgType)
|
||||
}
|
||||
if i > len(args)-1 {
|
||||
if !isLastVariadicArg && needArgType.Kind() != reflect.Interface && needArgType.Kind() != reflect.Ptr {
|
||||
return js.ThrowError(errors.New(fmt.Sprintf("call %s no enough args, need %d, given %d", key, len(needArgs), len(args))))
|
||||
}
|
||||
realArgs[needArgsIndex[i]] = reflect.ValueOf(argValue.Interface()).Elem()
|
||||
} else if needArgType.Kind() == reflect.Func {
|
||||
jsFunc := args[i]
|
||||
funcType := needArgType
|
||||
argValue = reflect.MakeFunc(funcType, func(goArgs []reflect.Value) []reflect.Value {
|
||||
ins := make([]quickjs.Value, 0)
|
||||
for _, goArg := range goArgs {
|
||||
ins = append(ins, MakeJsValue(ctx, goArg.Interface(), false))
|
||||
}
|
||||
outs := make([]reflect.Value, 0)
|
||||
for j := 0; j < funcType.NumOut(); j++ {
|
||||
outs = append(outs, reflect.New(funcType.Out(j)).Elem())
|
||||
}
|
||||
jsResult := jsCtx.Invoke(jsFunc, jsCtx.Null(), ins...)
|
||||
if !jsResult.IsUndefined() && len(outs) > 0 {
|
||||
out0P := outs[0].Interface()
|
||||
u.Convert(MakeFromJsValue(jsResult), out0P)
|
||||
outs[0] = reflect.ValueOf(out0P).Elem()
|
||||
}
|
||||
return outs
|
||||
})
|
||||
realArgs[needArgsIndex[i]] = argValue
|
||||
} else {
|
||||
//fmt.Println(222, len(args), len(needArgs), reflect.TypeOf(MakeFromJsValue(args[i])).String(), reflect.TypeOf(argValue).String(), MakeFromJsValue(args[i]), argValue)
|
||||
argValueP := argValue.Interface()
|
||||
ff1 := MakeFromJsValue(args[i])
|
||||
u.Convert(ff1, argValueP)
|
||||
argValue = reflect.ValueOf(argValueP).Elem()
|
||||
realArgs[needArgsIndex[i]] = argValue
|
||||
}
|
||||
}
|
||||
|
||||
// 处理可变参数
|
||||
if len(args) > len(needArgs) {
|
||||
lastArgType := needArgs[len(needArgs)-1]
|
||||
if lastArgType.Kind() == reflect.Slice && lastArgType.Elem().Kind() != reflect.Uint8 {
|
||||
//lastRealArgIndex := needArgsIndex[len(needArgs)-1]
|
||||
//fmt.Println(222221, realArgs[lastRealArgIndex].Type().String())
|
||||
for i := len(needArgs); i < len(args); i++ {
|
||||
argValue := reflect.New(lastArgType.Elem()).Interface()
|
||||
//fmt.Println(22222, len(args), len(needArgs), reflect.TypeOf(MakeFromJsValue(args[i])).String(), reflect.TypeOf(argValue).String(), MakeFromJsValue(args[i]), argValue)
|
||||
u.Convert(MakeFromJsValue(args[i]), argValue)
|
||||
realArgs = append(realArgs, reflect.ValueOf(argValue).Elem())
|
||||
//realArgs[lastRealArgIndex] = reflect.Append(realArgs[lastRealArgIndex], reflect.ValueOf(argValue).Elem())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
outValues := v.Call(realArgs)
|
||||
outs := make([]reflect.Value, 0)
|
||||
for _, outValue := range outValues {
|
||||
if outValue.Type().String() == "error" {
|
||||
if !outValue.IsNil() {
|
||||
// 抛出异常
|
||||
return _makeJsValue(ctx, outValue.Interface(), n+1, "", pluginName, false)
|
||||
}
|
||||
// 忽略error参数
|
||||
continue
|
||||
}
|
||||
outs = append(outs, outValue)
|
||||
}
|
||||
if len(outs) == 1 {
|
||||
//fmt.Println("**out, ", u.JsonP(outs[0].Interface()), "**")
|
||||
out := _makeJsValue(ctx, outs[0].Interface(), n+1, "", pluginName, false)
|
||||
return out
|
||||
} else if len(outs) > 1 {
|
||||
arr := jsCtx.Array()
|
||||
for _, outValue := range outs {
|
||||
//r.Set(int64(i), _makeJsValue(ctx, outValue.Interface(), n+1, "", pluginName, inReturn))
|
||||
o := _makeJsValue(ctx, outValue.Interface(), n+1, "", pluginName, true)
|
||||
if o.IsObject() || o.IsArray() || o.IsMap() {
|
||||
if freeJsValues, ok := ctx.GetData("_freeJsValues").(*[]quickjs.Value); ok {
|
||||
*freeJsValues = append(*freeJsValues, o)
|
||||
}
|
||||
}
|
||||
arr.Push(o)
|
||||
}
|
||||
return arr.ToValue()
|
||||
} else {
|
||||
return jsCtx.Null()
|
||||
}
|
||||
})
|
||||
case reflect.Invalid:
|
||||
return jsCtx.Null()
|
||||
default:
|
||||
return jsCtx.String(u.String(in))
|
||||
}
|
||||
}
|
||||
|
||||
func MakeFromJsValue(in quickjs.Value) interface{} {
|
||||
return _makeFromJsValue(in, 0)
|
||||
}
|
||||
|
||||
func _makeFromJsValue(in quickjs.Value, n int) interface{} {
|
||||
if n > 100 {
|
||||
return quickjs.Value{}
|
||||
}
|
||||
if in.IsBool() {
|
||||
return in.Bool()
|
||||
} else if in.IsBigInt() {
|
||||
return in.Int64()
|
||||
} else if in.IsBigFloat() || in.IsBigDecimal() {
|
||||
return in.Float64()
|
||||
} else if in.IsNumber() {
|
||||
if strings.ContainsRune(in.String(), '.') {
|
||||
return in.Float64()
|
||||
} else {
|
||||
return in.Int64()
|
||||
}
|
||||
} else if in.IsString() {
|
||||
return in.String()
|
||||
} else if in.IsByteArray() {
|
||||
buf, err := in.ToByteArray(uint(in.ByteLen()))
|
||||
if err != nil {
|
||||
return []byte{}
|
||||
}
|
||||
return buf
|
||||
} else if in.IsArray() {
|
||||
a := make([]interface{}, 0)
|
||||
//isBytes := true
|
||||
//isChars := true
|
||||
arr := in.ToArray()
|
||||
for i := int64(0); i < arr.Len(); i++ {
|
||||
if v, err := arr.Get(i); err == nil {
|
||||
value := _makeFromJsValue(v, n+1)
|
||||
a = append(a, value)
|
||||
//value.Free()
|
||||
v.Free()
|
||||
//_freeJsValues = append(_freeJsValues, v)
|
||||
}
|
||||
|
||||
//v := in.GetIdx(int64(i))
|
||||
//value := _makeFromJsValue(v, n+1)
|
||||
////if isChars {
|
||||
//// // 判断是否 Uint16Array 或 Uint8Array
|
||||
//// if !v.IsNumber() {
|
||||
//// isBytes = false
|
||||
//// isChars = false
|
||||
//// }
|
||||
////}
|
||||
////if isBytes {
|
||||
//// // 判断是否 Uint8Array
|
||||
//// if v.Int64() > 255 {
|
||||
//// isBytes = false
|
||||
//// }
|
||||
////}
|
||||
//a = append(a, value)
|
||||
//v.Free()
|
||||
}
|
||||
//arr.Free()
|
||||
//_freeJsValues = append(_freeJsValues, in)
|
||||
|
||||
//if isBytes {
|
||||
// buf := make([]byte, len(a))
|
||||
// for i, arrV := range a {
|
||||
// buf[i] = byte(u.Uint(arrV))
|
||||
// }
|
||||
// return buf
|
||||
//}
|
||||
//if isChars {
|
||||
// buf := make([]rune, len(a))
|
||||
// for i, arrV := range a {
|
||||
// buf[i] = rune(u.Uint(arrV))
|
||||
// }
|
||||
// return buf
|
||||
//}
|
||||
return a
|
||||
} else if in.IsFunction() {
|
||||
//reflect.Func
|
||||
return nil
|
||||
} else if in.IsObject() {
|
||||
o := map[string]interface{}{}
|
||||
keys, _ := in.PropertyNames()
|
||||
isBytes := true
|
||||
isChars := true
|
||||
isArray := true
|
||||
arr := make([]interface{}, 0)
|
||||
for i, k := range keys {
|
||||
if k == "prototype" {
|
||||
continue
|
||||
}
|
||||
v := in.Get(k)
|
||||
value := _makeFromJsValue(v, n+1)
|
||||
// 判断是否数组类对象
|
||||
if (i == 0 && k != "0") || (i > 0 && u.Int(k) != i) {
|
||||
isBytes = false
|
||||
isChars = false
|
||||
isArray = false
|
||||
}
|
||||
if isArray {
|
||||
if isChars {
|
||||
// 判断是否 Uint16Array 或 Uint8Array
|
||||
if !v.IsNumber() {
|
||||
isBytes = false
|
||||
isChars = false
|
||||
}
|
||||
}
|
||||
if isBytes {
|
||||
// 判断是否 Uint8Array
|
||||
if v.Int64() > 255 {
|
||||
isBytes = false
|
||||
}
|
||||
}
|
||||
arr = append(arr, value)
|
||||
}
|
||||
o[k] = value
|
||||
v.Free()
|
||||
}
|
||||
|
||||
if isBytes {
|
||||
buf := make([]byte, len(arr))
|
||||
for i, arrV := range arr {
|
||||
buf[i] = byte(u.Uint(arrV))
|
||||
}
|
||||
return buf
|
||||
}
|
||||
if isChars {
|
||||
buf := make([]rune, len(arr))
|
||||
for i, arrV := range arr {
|
||||
buf[i] = rune(u.Uint(arrV))
|
||||
}
|
||||
return buf
|
||||
}
|
||||
if isArray {
|
||||
return arr
|
||||
}
|
||||
return o
|
||||
} else if in.IsNull() || in.IsUndefined() || in.IsUninitialized() {
|
||||
return nil
|
||||
} else {
|
||||
return in.String()
|
||||
}
|
||||
}
|
16
go.mod
Normal file
16
go.mod
Normal file
@ -0,0 +1,16 @@
|
||||
module apigo.cloud/git/apigo/gojs
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
apigo.cloud/git/apigo/plugin v1.0.1
|
||||
apigo.cloud/git/apigo/qjs v0.0.1
|
||||
github.com/ssgo/log v0.6.11
|
||||
github.com/ssgo/u v0.6.11
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/ssgo/config v0.6.11 // indirect
|
||||
github.com/ssgo/standard v0.6.11 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
99
gojs.go
Normal file
99
gojs.go
Normal file
@ -0,0 +1,99 @@
|
||||
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 ""
|
||||
}
|
||||
}
|
149
gojs_test.go
Normal file
149
gojs_test.go
Normal file
@ -0,0 +1,149 @@
|
||||
package gojs_test
|
||||
|
||||
import (
|
||||
"apigo.cloud/git/apigo/gojs"
|
||||
"apigo.cloud/git/apigo/plugin"
|
||||
"fmt"
|
||||
"github.com/ssgo/log"
|
||||
"github.com/ssgo/u"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
||||
type Object struct {
|
||||
id string
|
||||
}
|
||||
|
||||
func (obj *Object) GetId() string {
|
||||
return obj.id
|
||||
}
|
||||
|
||||
func init() {
|
||||
defaultObject := Object{id: "o-00"}
|
||||
plugin.Register(plugin.Plugin{
|
||||
Id: "obj",
|
||||
Name: "test obj plugin",
|
||||
Objects: map[string]interface{}{
|
||||
"getId": defaultObject.GetId,
|
||||
"new": func(id string) interface{} {
|
||||
return &Object{id: id}
|
||||
},
|
||||
"echo": func(text string, echoFunc func(text string) string) interface{} {
|
||||
return echoFunc(text)
|
||||
},
|
||||
"echoTimes": func(echoFunc func(text string)) {
|
||||
for i := 0; i < 5; i++ {
|
||||
//time.Sleep(100 * time.Millisecond)
|
||||
echoFunc(fmt.Sprint(i))
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func test(t *testing.T, name string, check bool, extArgs ...interface{}) {
|
||||
if check {
|
||||
fmt.Println(u.Green(name), u.BGreen("[OK]"))
|
||||
} else {
|
||||
fmt.Println(u.Red(name), u.BRed("[Failed]"), fmt.Sprintln(extArgs...))
|
||||
t.Error(name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGlobal(t *testing.T) {
|
||||
code := `
|
||||
log('test', 'name', 'log')
|
||||
return plus(number,2)
|
||||
`
|
||||
globals := map[string]interface{}{
|
||||
"number": 9,
|
||||
"log": log.DefaultLogger.Info,
|
||||
"plus": func(i, j int) int { return i + j },
|
||||
}
|
||||
|
||||
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)
|
||||
test(t, "obj.getId()", u.String(r) == "o-00", r)
|
||||
|
||||
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(`
|
||||
out = ''
|
||||
obj.echo('123', function(text){
|
||||
out = text
|
||||
})
|
||||
return out
|
||||
`, nil, log.DefaultLogger)
|
||||
t2 := time.Now()
|
||||
fmt.Println("time:", t2.UnixMicro() - t1.UnixMicro())
|
||||
test(t, "callback", u.String(r) == "123", r)
|
||||
|
||||
t1 = time.Now()
|
||||
r = gojs.Run(`
|
||||
out = ''
|
||||
obj.echoTimes(function(text){
|
||||
out += text
|
||||
})
|
||||
return out
|
||||
`, nil, log.DefaultLogger)
|
||||
t2 = time.Now()
|
||||
fmt.Println("time:", t2.UnixMicro() - t1.UnixMicro())
|
||||
test(t, "callbacks", u.String(r) == "01234", r)
|
||||
}
|
||||
|
||||
|
||||
func BenchmarkEcho(tb *testing.B) {
|
||||
tb.StopTimer()
|
||||
ms1 := runtime.MemStats{}
|
||||
runtime.ReadMemStats(&ms1)
|
||||
tb.StartTimer()
|
||||
for i := 0; i < tb.N; i++ {
|
||||
gojs.Run(`return 1`, nil, log.DefaultLogger)
|
||||
}
|
||||
tb.StopTimer()
|
||||
|
||||
ms2 := runtime.MemStats{}
|
||||
runtime.ReadMemStats(&ms2)
|
||||
|
||||
runtime.GC()
|
||||
ms3 := runtime.MemStats{}
|
||||
runtime.ReadMemStats(&ms3)
|
||||
fmt.Println(">>", ms1.HeapInuse, ms2.HeapInuse, ms3.HeapInuse)
|
||||
}
|
||||
|
||||
|
||||
func BenchmarkCallback(tb *testing.B) {
|
||||
tb.StopTimer()
|
||||
ms1 := runtime.MemStats{}
|
||||
runtime.ReadMemStats(&ms1)
|
||||
tb.StartTimer()
|
||||
for i := 0; i < tb.N; i++ {
|
||||
gojs.Run(`
|
||||
out = ''
|
||||
obj.echoTimes(function(text){
|
||||
out += text
|
||||
})
|
||||
return out
|
||||
`, nil, log.DefaultLogger)
|
||||
}
|
||||
tb.StopTimer()
|
||||
|
||||
ms2 := runtime.MemStats{}
|
||||
runtime.ReadMemStats(&ms2)
|
||||
|
||||
runtime.GC()
|
||||
ms3 := runtime.MemStats{}
|
||||
runtime.ReadMemStats(&ms3)
|
||||
fmt.Println(">>", ms1.HeapInuse, ms2.HeapInuse, ms3.HeapInuse)
|
||||
}
|
Loading…
Reference in New Issue
Block a user