package gojs import ( "apigo.cc/apigo/plugin" "apigo.cc/apigo/quickjs-go" "errors" "fmt" "github.com/ssgo/log" "github.com/ssgo/u" "reflect" "regexp" "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] != '_' { v2 := _makeJsValue(ctx, v.MapIndex(k2), n+1, k2s, pluginName, false) o.Set(k2s, v2) } } 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 funcCode := jsFunc.String() funcId := "" if !jsFuncNameMatcher.MatchString(funcCode) { anonymousFuncIndex := u.Uint64(ctx.GetData("anonymousFuncIndex")) + 1 ctx.SetData("anonymousFuncIndex", anonymousFuncIndex) funcId = "_func_" + u.String(anonymousFuncIndex) + "_" + u.ShortUniqueId() jsCtx.Eval("var " + funcId + " = " + funcCode) } argValue = reflect.MakeFunc(funcType, func(goArgs []reflect.Value) []reflect.Value { if !jsFunc.IsFunction() && funcId != "" { jsFunc2 := jsCtx.Globals().Get(funcId) if freeJsValues, ok := ctx.GetData("_freeJsValues").(*[]quickjs.Value); ok { *freeJsValues = append(*freeJsValues, jsFunc2) } if jsFunc2.IsFunction() { jsFunc = jsFunc2 } } ins := make([]quickjs.Value, 0) j := 0 for i := 0; i < len(goArgs); i++ { if j >= funcType.NumIn() { break } var jV interface{} 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(ctx, reflect.ValueOf(jV).Elem(), false)) } //for _, goArg := range goArgs { // ins = append(ins, MakeJsValue(ctx, goArg.Interface(), false)) //} //for j := 0; j < funcType.NumIn(); j++ { // if j < len(goArgs) { // if goArgs[j].Type() == funcType.In(j) { // fmt.Println(111, goArgs[j].Type(), funcType.In(j)) // inArg := reflect.New(funcType.In(j)) // inArgValue := inArg.Interface() // u.Convert(goArgs[j].Interface(), inArgValue) // ins = append(ins, MakeJsValue(ctx, reflect.ValueOf(inArgValue).Elem(), false)) // } // } else { // ins = append(ins, MakeJsValue(ctx, reflect.New(funcType.In(j)).Elem(), false)) // } //} outs := make([]reflect.Value, 0) jsResult := jsCtx.Invoke(jsFunc, jsCtx.Null(), ins...) if !jsResult.IsUndefined() { if funcType.NumOut() == 1 { out0P := reflect.New(funcType.Out(0)).Interface() u.Convert(MakeFromJsValue(jsResult, ctx), out0P) outs = append(outs, reflect.ValueOf(out0P).Elem()) } else if funcType.NumOut() > 1 { if jsResult.IsArray() && int(jsResult.Len()) <= funcType.NumOut() { for j := 0; j < funcType.NumOut(); j++ { if j < int(jsResult.Len()) { out0P := reflect.New(funcType.Out(j)).Interface() u.Convert(MakeFromJsValue(jsResult.GetIdx(int64(j)), ctx), out0P) outs = append(outs, reflect.ValueOf(out0P).Elem()) } else { outs = append(outs, reflect.New(funcType.Out(j)).Elem()) } } } else { out0P := reflect.New(funcType.Out(0)).Interface() u.Convert(MakeFromJsValue(jsResult, ctx), out0P) outs = append(outs, reflect.ValueOf(out0P).Elem()) for j := 1; j < funcType.NumOut(); j++ { outs = append(outs, reflect.New(funcType.Out(j)).Elem()) } } } } else { for j := 0; j < funcType.NumOut(); j++ { outs = append(outs, reflect.New(funcType.Out(j)).Elem()) } } return outs //ins := make([]reflect.Value, 0) //j := 0 //for i := 0; i < len(goArgs); i++ { // if j >= fromType.NumIn() { // break // } // var jV interface{} // if fromType.IsVariadic() && j == fromType.NumIn()-1 && fromType.In(j).Kind() == reflect.Slice { // jV = reflect.New(fromType.In(j).Elem()).Interface() // } else { // jV = reflect.New(fromType.In(j)).Interface() // j++ // } // convert(goArgs[i].Interface(), jV) // ins = append(ins, reflect.ValueOf(jV).Elem()) //} //out := fromValue.Call(ins) //outs := make([]reflect.Value, 0) //j = 0 //for i := 0; i < toType.NumOut(); i++ { // iV := reflect.New(toType.Out(i)).Interface() // var jV interface{} // if toType.NumOut() > len(out) && j == len(out)-1 && out[j].Kind() == reflect.Slice { // if out[j].Len() > i-j { // jV = out[j].Index(i - j).Interface() // convert(jV, iV) // } // } else { // jV = out[j].Interface() // convert(jV, iV) // j++ // } // outs = append(outs, reflect.ValueOf(iV).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], ctx) 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], ctx), 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, ctx *plugin.Context) interface{} { return _makeFromJsValue(in, in.Context().Null(), "", 0, ctx) } var jsFuncNameMatcher = regexp.MustCompile(`function\s+(\w+)\s*\(`) func _makeFromJsValue(in quickjs.Value, parent quickjs.Value, key string, n int, ctx *plugin.Context) 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, in.Context().Null(), "", n+1, ctx) 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() { jsCtx := ctx.GetInject("*quickjs.Context").(*quickjs.Context) funcCode := in.String() funcId := "" if !jsFuncNameMatcher.MatchString(funcCode) { anonymousFuncIndex := u.Uint64(ctx.GetData("anonymousFuncIndex")) + 1 ctx.SetData("anonymousFuncIndex", anonymousFuncIndex) funcId = "_func_" + u.String(anonymousFuncIndex) + "_" + u.ShortUniqueId() jsCtx.Eval("var " + funcId + " = " + funcCode) } return func(args ...interface{}) interface{} { if !in.IsFunction() && funcId != "" { in2 := jsCtx.Globals().Get(funcId) if freeJsValues, ok := ctx.GetData("_freeJsValues").(*[]quickjs.Value); ok { *freeJsValues = append(*freeJsValues, in2) } if in2.IsFunction() { in = in2 } } jsIn := make([]quickjs.Value, len(args)) for i, arg := range args { jsIn[i] = MakeJsValue(ctx, arg, false) if freeJsValues, ok := ctx.GetData("_freeJsValues").(*[]quickjs.Value); ok { *freeJsValues = append(*freeJsValues, jsIn[i]) } } var jsOut quickjs.Value if key != "" && !parent.IsNull() { //jsOut = parent.Call(key, jsIn...) //jsOut = jsCtx.Invoke(in, parent, jsIn...) jsOut = jsCtx.Invoke(in, jsCtx.Null(), jsIn...) } else { jsOut = jsCtx.Invoke(in, jsCtx.Null(), jsIn...) } //fmt.Println(u.BBlue(">>>>>>>2 "), in.IsFunction(), jsOut.IsError(), jsOut.IsException(), jsCtx.Exception(), u.JsonP(MakeFromJsValue(jsOut, ctx)), 2222) return MakeFromJsValue(jsOut, ctx) } } 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, in, k, n+1, ctx) // 判断是否数组类对象 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() } }