gojs/bridge.go

466 lines
14 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package gojs
import (
"fmt"
"reflect"
"runtime"
"unsafe"
"apigo.cc/gojs/common"
"apigo.cc/gojs/goja"
"github.com/ssgo/u"
)
func ToJS(in any) any {
return go2js(in, 0, nil)
}
func ToMap(in any) Map {
return toMap(in, nil)
}
func toMap(in any, vm *goja.Runtime) Map {
from := u.RealValue(reflect.ValueOf(in))
if from.Kind() == reflect.Struct {
in1 := MakeMap(in)
if from.CanAddr() && vm != nil {
ptr := u.String(from.UnsafeAddr())
vm.SetData(ptr, true)
in1["gojsObjectId"] = ptr
// fmt.Println(u.BMagenta(">>> save ptr"), ptr)
runtime.AddCleanup(&from, func(arg *string) {
// fmt.Println(u.BMagenta(">>> remove ptr"), ptr)
vm.RemoveData(ptr)
}, nil)
}
in = in1
}
jsValue := go2js(in, 0, vm)
if mapValue, ok := jsValue.(Map); ok {
return mapValue
}
return nil
}
func go2js(in any, n int, vm *goja.Runtime) any {
if n > 100 {
return nil
}
var v reflect.Value
if inV, ok := in.(reflect.Value); ok {
v = inV
} else {
v = reflect.ValueOf(in)
}
for v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface {
if v.IsNil() {
return nil
}
v = v.Elem()
}
if (v.Kind() == reflect.Slice || v.Kind() == reflect.Map || v.Kind() == reflect.Func) && v.IsNil() {
return nil
}
if v.Kind() == reflect.Struct {
v = reflect.ValueOf(toMap(in, vm))
}
switch v.Kind() {
case reflect.Slice:
if v.Kind() == reflect.Slice && v.Type().Elem().Kind() != reflect.Uint8 {
arr := make([]any, v.Len())
for i := 0; i < v.Len(); i++ {
o := go2js(v.Index(i).Interface(), n+1, vm)
arr[i] = o
}
return arr
} else {
return in
}
case reflect.Map:
o := map[string]any{}
for _, k2 := range v.MapKeys() {
k2s := u.String(u.FinalValue(k2).Interface())
if len(k2s) > 0 && k2s[0] != '_' {
v2 := go2js(v.MapIndex(k2).Interface(), n+1, vm)
o[k2s] = v2
}
}
return o
case reflect.Func:
// skip goja supported functions
switch v.Type().String() {
case "func(goja.FunctionCall) goja.Value":
return in
case "func(goja.FunctionCall, *goja.Runtime) goja.Value":
return in
case "func(goja.ConstructorCall) *goja.Object":
return in
case "func(goja.ConstructorCall, *goja.Runtime) *goja.Object":
return in
}
// 将go的函数转换为js的函数
t := v.Type()
return func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
vm.SetData("_lastError", nil)
needArgs := make([]reflect.Type, 0)
realArgs := make([]reflect.Value, t.NumIn())
needArgsIndex := map[int]int{}
hasLastVariadicArg := false
// fmt.Println(t.NumIn(), t.String(), u.BYellow(u.JsonP(argsIn.Arguments)))
for i := 0; i < t.NumIn(); i++ {
inTypeString := t.In(i).String()
if inTypeString == "*goja.Runtime" {
realArgs[i] = reflect.ValueOf(vm)
continue
} else if inTypeString == "goja.Value" {
realArgs[i] = reflect.ValueOf(argsIn.This)
continue
}
if !realArgs[i].IsValid() {
needArgs = append(needArgs, t.In(i))
needArgsIndex[len(needArgsIndex)] = i
}
}
for i, needArgType := range needArgs {
var argValue reflect.Value
isLastVariadicArg := false
realNeedArgType := needArgType
if needArgType.Kind() == reflect.Ptr {
realNeedArgType = needArgType.Elem()
}
if v.Type().IsVariadic() && needArgsIndex[i] == len(realArgs)-1 {
// 可变参数函数的最后一个使用成员类型
isLastVariadicArg = true
hasLastVariadicArg = true
argValue = reflect.New(needArgType.Elem())
} else {
argValue = reflect.New(needArgType)
}
if i > len(argsIn.Arguments)-1 {
if !isLastVariadicArg && needArgType.Kind() != reflect.Interface && needArgType.Kind() != reflect.Ptr {
panic(vm.NewGoError(fmt.Errorf("no enough args, need %d, given %d", len(needArgs), len(argsIn.Arguments))))
}
realArgs[needArgsIndex[i]] = reflect.ValueOf(argValue.Interface()).Elem()
} else if realNeedArgType.Kind() == reflect.Func {
jsFunc, ok := goja.AssertFunction(argsIn.Arguments[i])
if !ok {
panic(vm.NewGoError(fmt.Errorf("%s not a function", argsIn.Arguments[i].ExportType().String())))
}
funcType := realNeedArgType
// 传入的参数是函数自动转换为go函数
argValue = MakeFunc(vm, funcType, jsFunc, argsIn.This)
// argValue = reflect.MakeFunc(funcType, func(goArgs []reflect.Value) []reflect.Value {
// ins := make([]goja.Value, 0)
// j := 0
// for i := 0; i < len(goArgs); i++ {
// if j >= funcType.NumIn() {
// break
// }
// var jV any
// if funcType.IsVariadic() && j == funcType.NumIn()-1 && funcType.In(j).Kind() == reflect.Slice {
// jV = reflect.New(funcType.In(j).Elem()).Interface()
// } else {
// jV = reflect.New(funcType.In(j)).Interface()
// j++
// }
// u.Convert(goArgs[i].Interface(), jV)
// // ins = append(ins, MakeJsValue(vm, reflect.ValueOf(jV).Elem(), false))
// ins = append(ins, vm.ToValue(jV))
// }
// outs := make([]reflect.Value, 0)
// jsResult, err := jsFunc(argsIn.This, ins...)
// if funcType.NumOut() == 1 {
// // 只有一个返回值
// if funcType.Out(0).String() != "error" {
// out0P := reflect.New(funcType.Out(0)).Interface()
// if jsResult != nil {
// u.Convert(jsResult.Export(), out0P)
// }
// outs = append(outs, reflect.ValueOf(out0P).Elem())
// } else {
// if err == nil {
// outs = append(outs, reflect.New(funcType.Out(0)).Elem())
// } else {
// outs = append(outs, reflect.ValueOf(err))
// }
// }
// } else if funcType.NumOut() > 1 {
// // 以数组形式返回多个值
// rArr := make([]any, 0)
// if err == nil {
// if jsResult != nil && jsResult.ExportType().Kind() == reflect.Slice && jsResult.ExportType().Elem().Kind() != reflect.Uint8 {
// vm.ForOf(jsResult, func(v goja.Value) bool {
// rArr = append(rArr, v.Export())
// return true
// })
// }
// }
// k := 0
// for j := 0; j < funcType.NumOut(); j++ {
// if funcType.Out(j).String() != "error" {
// if k < len(rArr) {
// out0P := reflect.New(funcType.Out(j)).Interface()
// u.Convert(rArr[k], out0P)
// k++
// outs = append(outs, reflect.ValueOf(out0P).Elem())
// } else {
// outs = append(outs, reflect.New(funcType.Out(j)).Elem())
// }
// } else {
// if err == nil {
// outs = append(outs, reflect.New(funcType.Out(j)).Elem())
// } else {
// outs = append(outs, reflect.ValueOf(err))
// }
// }
// }
// }
// return outs
// })
if needArgType.Kind() == reflect.Ptr {
argValueP := reflect.New(realNeedArgType)
argValueP.Elem().Set(argValue)
realArgs[needArgsIndex[i]] = argValueP
} else {
realArgs[needArgsIndex[i]] = argValue
}
} else {
argValueP := argValue.Interface()
jsValue := argsIn.Arguments[i]
anyV := jsValue.Export()
recovered := false
et := jsValue.ExportType()
if et != nil && et.Kind() == reflect.Map {
mapV := reflect.ValueOf(anyV)
// 尝试使用传递的objId直接恢复对象
if mapV.IsValid() && !mapV.IsNil() {
objIdV := mapV.MapIndex(reflect.ValueOf("gojsObjectId"))
if objIdV.IsValid() && !objIdV.IsNil() {
objId := u.String(objIdV.Interface())
if vm.GetData(objId) == true {
// argValueP := reflect.New(realNeedArgType)
objIdI := u.Int64(objId)
if objIdI > 0 {
func() {
defer recover()
reObj := reflect.NewAt(realNeedArgType, unsafe.Pointer(uintptr(objIdI)))
if reObj.IsValid() && !reObj.IsNil() {
// fmt.Println(u.BCyan(">>>>>>2"), i, objId, vm.GetData(objId), argValue.Elem().Type().String(), reObj.Type().String())
if argValue.Type().Elem().String() == reObj.Type().String() {
argValue = reObj
recovered = true
}
}
}()
}
}
}
}
}
if !recovered {
u.Convert(anyV, argValueP)
// 支持struct中包含函数
if realNeedArgType.Kind() == reflect.Struct {
// argValue = reflect.New(realNeedArgType)
for i := realNeedArgType.NumField() - 1; i >= 0; i-- {
f := realNeedArgType.Field(i)
if f.Type.Kind() == reflect.Func {
fnV := jsValue.ToObject(vm).Get(u.GetLowerName(f.Name))
if fn, ok := goja.AssertFunction(fnV); ok {
fn1 := MakeFunc(vm, f.Type, fn, argsIn.This)
argValueF := u.FinalValue(reflect.ValueOf(argValueP))
if argValueF.Kind() == reflect.Struct && argValueF.NumField() > i {
argValueF.Field(i).Set(fn1)
}
}
}
}
}
argValue = reflect.ValueOf(argValueP).Elem()
}
realArgs[needArgsIndex[i]] = argValue
}
}
// 处理可变参数
if len(needArgs) > 0 {
if len(argsIn.Arguments) > len(needArgs) {
lastArgType := needArgs[len(needArgs)-1]
if lastArgType.Kind() == reflect.Slice && lastArgType.Elem().Kind() != reflect.Uint8 {
for i := len(needArgs); i < len(argsIn.Arguments); i++ {
argValue := reflect.New(lastArgType.Elem()).Interface()
u.Convert(argsIn.Arguments[i].Export(), argValue)
realArgs = append(realArgs, reflect.ValueOf(argValue).Elem())
}
}
} else if len(argsIn.Arguments) < len(needArgs) && hasLastVariadicArg {
realArgs = realArgs[:len(realArgs)-1]
}
}
outValues := v.Call(realArgs)
outs := make([]any, 0)
for _, outValue := range outValues {
outType := outValue.Type()
if outType.String() == "error" {
var retErr error
if !outValue.IsNil() {
// 抛出异常
if err, ok := outValue.Interface().(*common.GoErr); ok && err != nil {
// panic(vm.NewGoError(err))
retErr = err
} else if err, ok := outValue.Interface().(error); ok && err != nil {
if err1, ok1 := err.(*common.GoErr); ok1 {
if err1 != nil {
// panic(vm.NewGoError(err1))
retErr = err1
}
} else {
// panic(vm.NewGoError(err))
retErr = err
}
} else {
errStr := u.String(outValue.Interface())
if errStr != "" {
// panic(vm.NewGoError(errors.New(errStr)))
retErr = err
}
}
}
if retErr != nil {
GetLogger(vm).Error(retErr.Error())
vm.SetData("_lastError", retErr)
}
// 忽略error参数
continue
}
// 如果返回Struct自动转换为Map首字母小写
if outType.Kind() == reflect.Struct || (outType.Kind() == reflect.Ptr && outType.Elem().Kind() == reflect.Struct) {
outs = append(outs, toMap(outValue.Interface(), vm))
} else if outType.Kind() == reflect.Slice && (outType.Elem().Kind() == reflect.Struct || (outType.Elem().Kind() == reflect.Ptr && outType.Elem().Elem().Kind() == reflect.Struct)) {
// 如果返回[]Struct自动转换为[]Map首字母小写
sliceMap := make([]map[string]any, 0)
for i := 0; i < outValue.Len(); i++ {
sliceMap = append(sliceMap, toMap(outValue.Index(i).Interface(), vm))
}
outs = append(outs, sliceMap)
} else if outType.Kind() == reflect.Map && (outType.Elem().Kind() == reflect.Struct || (outType.Elem().Kind() == reflect.Ptr && outType.Elem().Elem().Kind() == reflect.Struct)) {
// 如果返回map[string]Struct自动转换为map[string]Map首字母小写
mapMap := make(map[string]map[string]any)
for _, key := range outValue.MapKeys() {
mapMap[key.String()] = toMap(outValue.MapIndex(key).Interface(), vm)
}
outs = append(outs, mapMap)
} else {
// 其他返回内容转换JS主要是Func自动转换
outs = append(outs, ToJS(outValue.Interface()))
}
}
if len(outs) == 1 {
return vm.ToValue(outs[0])
} else if len(outs) > 1 {
return vm.ToValue(outs)
} else {
return vm.ToValue(nil)
}
}
case reflect.Invalid:
return nil
default:
return in
}
}
func MakeFunc(vm *goja.Runtime, funcType reflect.Type, jsFunc goja.Callable, thisArg goja.Value) reflect.Value {
return reflect.MakeFunc(funcType, func(goArgs []reflect.Value) []reflect.Value {
ins := make([]goja.Value, 0)
j := 0
for i := 0; i < len(goArgs); i++ {
if j >= funcType.NumIn() {
break
}
var jV any
var t reflect.Type
if funcType.IsVariadic() && j == funcType.NumIn()-1 && funcType.In(j).Kind() == reflect.Slice {
t = funcType.In(j).Elem()
} else {
t = funcType.In(j)
j++
}
if t.Kind() != reflect.Interface {
jV = reflect.New(t).Interface()
u.Convert(goArgs[i].Interface(), jV)
} else {
jV = goArgs[i].Interface()
}
// ins = append(ins, MakeJsValue(vm, reflect.ValueOf(jV).Elem(), false))
ins = append(ins, vm.ToValue(go2js(jV, 0, vm)))
}
outs := make([]reflect.Value, 0)
jsResult, err := jsFunc(thisArg, ins...)
if funcType.NumOut() == 1 {
// 只有一个返回值
if funcType.Out(0).String() != "error" {
out0P := reflect.New(funcType.Out(0)).Interface()
if jsResult != nil {
u.Convert(jsResult.Export(), out0P)
}
outs = append(outs, reflect.ValueOf(out0P).Elem())
} else {
if err == nil {
outs = append(outs, reflect.New(funcType.Out(0)).Elem())
} else {
outs = append(outs, reflect.ValueOf(err))
}
}
} else if funcType.NumOut() > 1 {
// 以数组形式返回多个值
rArr := make([]any, 0)
if err == nil {
if jsResult != nil && jsResult.ExportType().Kind() == reflect.Slice && jsResult.ExportType().Elem().Kind() != reflect.Uint8 {
vm.ForOf(jsResult, func(v goja.Value) bool {
rArr = append(rArr, v.Export())
return true
})
}
}
k := 0
for j := 0; j < funcType.NumOut(); j++ {
if funcType.Out(j).String() != "error" {
if k < len(rArr) {
out0P := reflect.New(funcType.Out(j)).Interface()
u.Convert(rArr[k], out0P)
k++
outs = append(outs, reflect.ValueOf(out0P).Elem())
} else {
outs = append(outs, reflect.New(funcType.Out(j)).Elem())
}
} else {
if err == nil {
outs = append(outs, reflect.New(funcType.Out(j)).Elem())
} else {
outs = append(outs, reflect.ValueOf(err))
}
}
}
}
return outs
})
}