Files
jscriptx/caller_example_test.go
T
what 7f484d7a71 test: 引擎核心的测试与基准
除了常规用例,有几组是专门钉住「引擎的既定事实」的,改动时会先红:

  - vmstate_test  goja 的硬限制:非原始值不是 goroutine 安全的、对象跨不了
                  Runtime。其中一条是哨兵——goja 哪天允许对象跨 Runtime 了,
                  它会失败,提醒我们可以简化设计。
  - govalue_test  Go 的切片/map 进到脚本里长什么样。这不是本库的行为而是
                  goja 的,但脚本作者照着它写代码,变了会静默走错分支。
  - safety_test   并发下坏 VM 不会被别的 goroutine 捡到。
  - bench_gobind  脚本碰 Go 对象的单次开销,README 性能一节的数据来源。

missing_global_test 里有一条断言 typeof setTimeout === "undefined":
定时器必须保持未定义,库里的特性探测才能正常降级,防止以后有人把桩加回来。
2026-09-05 22:13:57 +08:00

92 lines
2.5 KiB
Go
Raw 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 jscriptx_test
import (
"context"
"fmt"
"log"
"git.fsdpf.net/go/jscriptx"
)
// Caller 用来实现自定义的调用约定——本库不预设脚本回调该长什么样,
// 这个例子实现的是「回调 + next」这种中间件式的约定:脚本函数写成哪种形状,
// 运行时探测出来自动适配,脚本作者不用额外声明签名。
//
// 它同时也说明了为什么需要 Caller 而不是直接用 Callnext 是个 Go 闭包,
// 得先看脚本声明了几个形参,才知道要不要把它传进去——这些都必须在同一次
// VM 借出期间完成。
func ExampleCaller() {
e, err := jscriptx.New(jscriptx.WithLogger(nil))
if err != nil {
log.Fatal(err)
}
defer e.Close()
// 三种回调写法,脚本作者挑顺手的用
s, err := e.Compile("ctrl.ts", `
export function twoArgs(v, next) { return next(v) + "|脚本自己调的 next" }
export function decorator(v) { return v + "|装饰过" }
export function sideEffect(v) { /* 只做事,不返回 */ }
`)
if err != nil {
log.Fatal(err)
}
next := func(v string) string { return "[" + v + "]" }
// dispatch 按脚本函数的形状分派
dispatch := func(fn, value string) (string, error) {
var out string
err := s.WithCall(context.Background(), fn, func(c jscriptx.Caller) error {
switch n := c.Arity(); n {
case 2:
// handle(v, next):调不调 next 由脚本决定,但得把结果返回出来
res, err := c.Call(value, next)
if err != nil {
return err
}
if res.IsEmpty() {
return fmt.Errorf("%w: 两个形参的回调必须返回结果",
jscriptx.ErrUnsupportedSignature)
}
return res.Into(&out)
case 1:
// handle(v):有返回值当装饰器,没返回值当纯副作用
res, err := c.Call(value)
if err != nil {
return err
}
if res.IsEmpty() {
out = next(value)
return nil
}
var decorated string
if err := res.Into(&decorated); err != nil {
return err
}
out = next(decorated)
return nil
default:
return fmt.Errorf("%w: 形参个数必须是 1 或 2,当前是 %d",
jscriptx.ErrUnsupportedSignature, n)
}
})
return out, err
}
for _, fn := range []string{"twoArgs", "decorator", "sideEffect"} {
got, err := dispatch(fn, "输入")
if err != nil {
log.Fatal(err)
}
fmt.Printf("%-11s → %s\n", fn, got)
}
// Output:
// twoArgs → [输入]|脚本自己调的 next
// decorator → [输入|装饰过]
// sideEffect → [输入]
}