fix(js): 修复可变参数桥接,JS 剩余参数逐个追加而非打包为单一切片(by AI)
Co-Authored-By: deepseek-v4-pro[1m] <deepseek-ai@claude-code-best.win>
This commit is contained in:
parent
5ade0d0981
commit
c0fa98a5e1
@ -1,5 +1,9 @@
|
||||
# CHANGELOG - go/js
|
||||
|
||||
## v1.5.6 (2026-06-21)
|
||||
- **可变参数桥接修复**: `wrapGoFunc` 修复对 Go 可变参数(`...any`)的桥接处理。之前将 JS 剩余参数错误打包为单一切片元素,导致 `Call` 二次嵌套;现在改为逐个追加到 `goArgs` 尾部,由 `reflect.Call` 自动构建可变切片,确保 JS 调用 `redis.Do('HSET', a, b, c)` 正确展开。
|
||||
- **新增测试**: `TestBridgeVariadic` 覆盖可变参数 0/1/多参数场景。
|
||||
|
||||
## v1.5.5 (2026-06-21)
|
||||
- **JS 对齐**: 重构 JS 运行时的报错堆栈提取逻辑,采用高效的字符解析替换正则表达式,并支持 `jsmod.MakeError` 错误包装在桥接层还原出真实的 Go 运行时调用堆栈。
|
||||
- **依赖更新**: 升级依赖 `jsmod` 至 `v1.5.3`,`cast` 至 `v1.5.3`,`log` 至 `v1.5.8`。
|
||||
|
||||
31
bridge.go
31
bridge.go
@ -52,9 +52,10 @@ func wrapGoFunc(vm *goja.Runtime, fn any, isUnsafe bool) goja.Value {
|
||||
|
||||
// 2. Prepare Arguments
|
||||
numIn := t.NumIn()
|
||||
goArgs := make([]reflect.Value, numIn)
|
||||
goArgs := make([]reflect.Value, 0, numIn+len(call.Arguments))
|
||||
jsArgs := call.Arguments
|
||||
jsArgIdx := 0
|
||||
isVariadic := t.IsVariadic()
|
||||
|
||||
for i := 0; i < numIn; i++ {
|
||||
argType := t.In(i)
|
||||
@ -65,7 +66,7 @@ func wrapGoFunc(vm *goja.Runtime, fn any, isUnsafe bool) goja.Value {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
goArgs[i] = reflect.ValueOf(ctx)
|
||||
goArgs = append(goArgs, reflect.ValueOf(ctx))
|
||||
continue
|
||||
}
|
||||
|
||||
@ -80,24 +81,42 @@ func wrapGoFunc(vm *goja.Runtime, fn any, isUnsafe bool) goja.Value {
|
||||
if logger == nil {
|
||||
logger = log.DefaultLogger
|
||||
}
|
||||
goArgs[i] = reflect.ValueOf(logger)
|
||||
goArgs = append(goArgs, reflect.ValueOf(logger))
|
||||
continue
|
||||
}
|
||||
|
||||
// 可变参数:剩余 JS 参数直接追加到 goArgs,不由 Call 再包一层
|
||||
if isVariadic && i == numIn-1 {
|
||||
elemType := argType.Elem()
|
||||
for jsArgIdx < len(jsArgs) {
|
||||
exported := jsArgs[jsArgIdx].Export()
|
||||
expV := reflect.ValueOf(exported)
|
||||
if !expV.IsValid() || !expV.Type().AssignableTo(elemType) {
|
||||
elem := reflect.New(elemType).Elem()
|
||||
cast.Convert(elem.Addr().Interface(), exported)
|
||||
expV = elem
|
||||
}
|
||||
goArgs = append(goArgs, expV)
|
||||
jsArgIdx++
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Normal JS Argument with go/cast
|
||||
goArgs[i] = reflect.New(argType).Elem()
|
||||
goArg := reflect.New(argType).Elem()
|
||||
if jsArgIdx < len(jsArgs) {
|
||||
jsVal := jsArgs[jsArgIdx]
|
||||
exported := jsVal.Export()
|
||||
|
||||
expV := reflect.ValueOf(exported)
|
||||
if expV.IsValid() && expV.Type().AssignableTo(argType) {
|
||||
goArgs[i].Set(expV)
|
||||
goArg.Set(expV)
|
||||
} else {
|
||||
cast.Convert(goArgs[i].Addr().Interface(), exported)
|
||||
cast.Convert(goArg.Addr().Interface(), exported)
|
||||
}
|
||||
jsArgIdx++
|
||||
}
|
||||
goArgs = append(goArgs, goArg)
|
||||
}
|
||||
|
||||
// 3. Call the Go function
|
||||
|
||||
@ -106,3 +106,42 @@ func TestBridgeOptionalParams(t *testing.T) {
|
||||
t.Errorf("Optional param failed (val), got %v", val.Export())
|
||||
}
|
||||
}
|
||||
|
||||
func TestBridgeVariadic(t *testing.T) {
|
||||
vm := goja.New()
|
||||
|
||||
variadicFn := func(cmd string, args ...any) string {
|
||||
t.Logf("DEBUG variadicFn received: cmd=%q len(args)=%d args=%v argsType=%T", cmd, len(args), args, args)
|
||||
for i, a := range args {
|
||||
t.Logf("DEBUG args[%d] = %v (type=%T)", i, a, a)
|
||||
}
|
||||
return fmt.Sprintf("%s:%d:%v", cmd, len(args), args)
|
||||
}
|
||||
|
||||
vm.Set("vfn", wrapGoFunc(vm, variadicFn, false))
|
||||
|
||||
// 无额外参数
|
||||
val, _ := vm.RunString(`vfn("PING")`)
|
||||
t.Logf("DEBUG vfn(PING) result: %v", val.Export())
|
||||
if val.Export() != "PING:0:[]" {
|
||||
t.Errorf("Variadic zero args failed, got %v", val.Export())
|
||||
}
|
||||
|
||||
// 一个额外参数
|
||||
val, _ = vm.RunString(`vfn("GET", "key1")`)
|
||||
t.Logf("DEBUG vfn(GET, key1) result: %v", val.Export())
|
||||
if val.Export() != "GET:1:[key1]" {
|
||||
t.Errorf("Variadic one arg failed, got %v", val.Export())
|
||||
}
|
||||
|
||||
// 多个额外参数
|
||||
val, _ = vm.RunString(`vfn("HSET", "key1", "field1", "val1")`)
|
||||
result := val.Export().(string)
|
||||
t.Logf("DEBUG vfn(HSET, key1, field1, val1) result: %v", result)
|
||||
if !strings.Contains(result, "HSET:3:") {
|
||||
t.Errorf("Variadic multi args failed, got %v", result)
|
||||
}
|
||||
if !strings.Contains(result, "key1") || !strings.Contains(result, "field1") || !strings.Contains(result, "val1") {
|
||||
t.Errorf("Variadic args content wrong, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user