package jscriptx import ( "context" "sync/atomic" "github.com/dop251/goja" ) // DefaultFunc 传给 Call/CallInto/WithCall 的 fn 参数时,表示脚本的默认导出本身, // 也就是 `export default function ...` 这种"整个脚本就是一个函数"的写法。 // 默认导出是 class 或对象时用不上它——那种要按方法名调用。 const DefaultFunc = "" // Script 是一份编译好的脚本,内部维护一个 VM 池。它并发安全,可以长期持有。 // // 一个 Script 对应一份不可变的编译产物;热更新时 Engine 会造一个新的 Script // 顶替它,已经拿着旧 Script 的调用不受影响,会继续跑完旧版本。 type Script struct { engine *Engine name string version string prog *goja.Program pool chan *vmHandle closed atomic.Bool created atomic.Int64 // 累计新建过多少个 VM dropped atomic.Int64 // 累计丢弃过多少个 VM(超时/panic/池满) } // vmHandle 是一个 VM:一个 goja.Runtime 加上它跑完脚本后的求值结果。 // Runtime 不是并发安全的,同一时刻只能有一个 goroutine 持有它。 // // 一个 VM 只跑一个脚本。同一作用域下多个脚本要共享数据时靠扩展(Go 侧对象,注入到 // 各个 VM 里的是同一份引用),而不是共用 Runtime——共用 Runtime 会让作用域内的所有 // 脚本被迫串行。 type vmHandle struct { rt *goja.Runtime defFn goja.Value // 脚本求值出的函数(单函数入口写法) exports *goja.Object // 脚本求值出的对象(class 实例写法),方法调用时绑定为 this ctor *goja.Object // 导出的 class 本身。静态方法挂在它上面,实例的原型链上没有 // scoped 表示这个 VM 注入过某个作用域的扩展,因此**不能**回池给别的作用域用。 // 用完直接丢,见 release。 scoped bool } // Name 返回脚本名。 func (s *Script) Name() string { return s.name } // Version 返回脚本版本号(Loader 给的,或者源码哈希)。 func (s *Script) Version() string { return s.version } // Stats 是 VM 池的运行统计,用于观测。 type Stats struct { Pooled int // 池里闲置的 VM 数 MaxVMs int // 池容量 Created int64 // 累计新建 Dropped int64 // 累计丢弃 } // Stats 返回 VM 池的当前统计。 func (s *Script) Stats() Stats { return Stats{ Pooled: len(s.pool), MaxVMs: cap(s.pool), Created: s.created.Load(), Dropped: s.dropped.Load(), } } // Has 判断脚本导出的实例上有没有这个方法(继承来的也算)。 func (s *Script) Has(fn string) bool { inst, err := s.borrow(context.Background()) if err != nil { return false } defer s.release(inst, true) _, _, _, ok := inst.lookup(fn) return ok } // Close 释放池里所有 VM。之后的调用会返回 ErrClosed。 // 已经借出去、正在执行的调用不受影响,它们的 VM 归还时直接丢弃。 // // New 出来的实例不在这条链上——它们的生命周期归调用方,Close 之后那些实例的调用 // 会因为脚本已关闭而报错,VM 等 GC 回收。 func (s *Script) Close() { if s.closed.Swap(true) { return } for { select { case <-s.pool: s.dropped.Add(1) default: return } } }