docs: 加一篇函数流走向

call-flow.md 讲的是三个阶段各自在做什么,缺的是**函数之间怎么串起来的**——
想改代码、想知道加一层该加在哪,没有一处能查。

docs/flow.md 七张图:

  全景            公开 API → 注册表 / 取 VM / 调用 三条链
  runner 三态     Script / Instance / staticTarget 的 acquire+finish 差别,
                  「同一实例串行」就是 acquire 到 finish 之间一直持着 i.mu
  newVM 内部      为什么 scope 是显式参数而不是从 ctx 嗅
  作用域进 VM      WithScope → vmGlobals → bind → lazyGlobal → freeze 五跳,
                  这是全库最容易看不清的一条链
  invoke 内部     含 defer 的注册顺序(finish → stop → recover,后进先出)
  lookup 三级     exports → ctor 静态 → 全局兜底,解释了 Script.Has 为什么
                  对静态方法也返回 true
  编译链          Prepared 为什么不是优化开关而是正确性要求

写的时候实地核对了每条断言,抓出三处我自己写错的:

  1. 图上把 Bundle → FinalizeBundle 画成两步,实际 Bundle 末尾自己就调了
  2. 把「省 44% 常驻内存」归因给惰性注入——那是关掉 console 的收益
  3. 「只读全局赋值静默失败」只对全局本身成立,freeze 拷出来的嵌套只读属性
     (store.Set = null)实测是抛错的

顺带修一处上一轮改名的残留:engine.go 的注释还指着 lazyglobal_test.go。
This commit is contained in:
2026-09-10 16:24:18 +08:00
parent 6477b27ef4
commit 0c947dde70
4 changed files with 296 additions and 1 deletions
+3
View File
@@ -38,6 +38,9 @@
// 没有事件循环(定时器、网络、真正的异步都没有)、不提供沙箱隔离
// (脚本能拿到你放行的 Go 对象,它们的方法是真能调的)。
//
// 想看函数之间怎么串起来的(调用链、作用域从哪一步进 VM、想加一层该加在哪),
// 见 docs/flow.md。
//
// # 基本用法
//
// loader, err := esm.NewLoader("app/src")