gojs/bridge.go

429 lines
12 KiB
Go

package gojs
import (
"apigo.cloud/git/apigo/plugin"
"apigo.cloud/git/apigo/qjs"
"errors"
"fmt"
"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 {
if v.IsNil() {
return jsCtx.Null()
}
v = v.Elem()
}
if (v.Kind() == reflect.Slice || v.Kind() == reflect.Map || v.Kind() == reflect.Func) && v.IsNil() {
return jsCtx.Null()
}
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()
}
}