refactor: 取 VM 的三条路径合一,newVM 成为唯一入口

borrow(池化)、ensure(实例)、borrowStatic(静态)各自写了一遍「取作用域 →
拼 extra → 建 Runtime → load」。borrowStatic 甚至把 newVM 逐行抄了一遍,只为把
noInstance 传成 true。

结果就是规则会漏:上一个 commit 修的「borrowStatic 没标 scoped」正是这么来的。
现在三条都走 newVM,作用域注入和 scoped 标记只有一份。

顺带删掉两处无意义的间接:

  loadInto     s.load(ctx, rt, ctorArgs, false) 的单行包装,只被 newVM 调一次
  newRuntime   它的 scope 参数两个调用点都传 s.name,而注释描述的「会话 VM 用
               会话 key」这条路径根本没实现(作用域 key 走的是 vmGlobals)。
               参数名还跟同包的 type scope struct 撞名

**scope 是显式参数,不是从 ctx 嗅的**——这一点差点被我改坏:Instance 重建 VM
时要回到它**创建时**那个作用域,而不是当次调用 ctx 里的。对一个作用域实例调
Call(context.Background()) 不该把它的扩展弄丢。加了
TestScope_实例重建VM后仍在原作用域 守这条,用超时逼出 VM 重建(第一版用抛异常,
那不会丢 VM,两种写法都通过,等于没测)。
This commit is contained in:
2026-09-10 15:25:12 +08:00
parent 111665d2e7
commit 1f25425762
4 changed files with 91 additions and 66 deletions
+46
View File
@@ -431,3 +431,49 @@ func toNumber(t *testing.T, v any) float64 {
return 0
}
}
// 作用域实例重建 VM 时,要回到它**创建时**那个作用域。
//
// Instance 的 VM 超时后会被丢弃,下次调用重建(见 Resets)。重建走的是 i.scope
// 不是当次调用 ctx 里的——对一个作用域实例调 Call(context.Background()) 不该把
// 它的扩展弄丢。
//
// 这条守的是 newVM 的 scope 参数为什么是显式传的:改成从 ctx 嗅探就会破坏它。
func TestScope_实例重建VM后仍在原作用域(t *testing.T) {
e := newEngine(t, jscriptx.WithTimeout(80*time.Millisecond))
s := mustCompile(t, e, "s.ts", `
export default class S {
spin() { while (true) {} }
seeStore() { return typeof store }
}
`)
st := testext.New()
ctx := jscriptx.WithScope(context.Background(), jscriptx.ScopeExtensions(st))
obj, err := s.New(ctx)
if err != nil {
t.Fatal(err)
}
defer obj.Close()
if got, _ := obj.Call(ctx, "seeStore"); got != "object" {
t.Fatalf("一开始就没看见扩展: %#v", got)
}
// 超时会让实例丢掉 VM,下次调用重建
if _, err := obj.Call(ctx, "spin"); !errors.Is(err, jscriptx.ErrTimeout) {
t.Fatalf("该超时: %v", err)
}
if obj.Resets() != 1 {
t.Fatalf("VM 没被丢弃,这条测不到重建(Resets=%d", obj.Resets())
}
// 用**不带作用域**的 ctx 触发重建:实例仍属于原来那个作用域
got, err := obj.Call(context.Background(), "seeStore")
if err != nil {
t.Fatal(err)
}
if got != "object" {
t.Errorf("重建之后扩展丢了(%#v)——实例被当成了无作用域的", got)
}
}