1. 目标与范围
这次重构只做一件事:围绕 SRP(单一职责原则),把 VM 里的“执行职责”和“统计职责”拆开。
重构范围如下:
- 栈组件从“带统计”改为“纯数据结构”。
- VM 通过观察者发布事件,不再内置统计接口。
- 同步修复测试与构建系统(Bazel 目标、依赖、测试名)。
2. 改造前的问题
改造前,VM 与 StackStats 的职责边界不清晰:
StackStats同时负责 栈存储 和 统计计数。VM对外暴露了PushCount/PopCount/Depth/MaxDepth等统计接口。- 统计逻辑和执行逻辑耦合,改一处会牵动核心路径。
问题本质是:功能可用,但长期维护和扩展成本会持续升高。
3. 本次改动清单
3.1 栈组件更名与瘦身
- 文件重命名:
src/stack_stats.h->src/vm_stack.hsrc/stack_stats.cc->src/vm_stack.cctests/stack_stats_test.cc->tests/vm_stack_test.cc
- 类型更名:
StackStats->VmStack
- 功能调整:
- 删除统计职责,仅保留
Push/Pop/Top/At/Clear/Empty等栈行为。 - 保留
values()只读接口供 GC root 扫描使用。
- 删除统计职责,仅保留
3.2 观察者机制落地(栈统计)
新增 IVmObserver / VmStackObserver:
IVmObserver提供:OnStackPush()OnStackPop()OnTestEmit(const Value&)
VmStackObserver专门负责统计:push_count_pop_count_max_depth_
3.3 test hook 迁移到观察者
VM增加并打通测试事件路径:TestEmit(const Value&)NotifyTestEmit(const Value&)
__test_emit(x)builtin 仍保持脚本语义不变,但内部统一走 VM 事件广播。- 新增
VmHookObserver(基于OnTestEmit),用于测试侧采集 emit 值。 - 测试代码从旧接口迁移为 observer 方式:
- 旧:
vm.SetTestEmitSink(...) - 新:
VmHookObserver hook; vm.AddObserver(&hook);
- 旧:
3.4 VM 迁移
VM内部栈类型切换为VmStack。VM增加AddObserver(IVmObserver*)。VM::Push/Pop中增加事件通知:- Push 后广播
OnStackPush - Pop 后广播
OnStackPop
- Push 后广播
3.5 Bazel 构建修复
本次重构中,构建系统发生了两类问题并已修复:
- 测试目标名未同步
stack_stats_test已改为vm_stack_test并更新到test_suite(all)。
- frontend 子包依赖冲突
- 移除
//src/frontend:frontend残留依赖。 //src:cilly_core统一收口源码。- 修复
src/BUILD.bazel与tests/BUILD.bazel的依赖关系。
- 移除
4. 验证结果
执行命令:
bazelisk test //tests:"all" --test_output=errors
结果:38/38 测试全部通过。
说明这次 SRP 重构没有破坏现有行为。
5. 改造收益
-
职责更清晰
VmStack只管数据,统计逻辑归VmStackObserver,VM 只负责执行与事件广播。 -
扩展更简单
后续加 profiler/debugger,只需新增 observer,不必反复改 VM 核心执行路径。 -
构建更稳定
Bazel 依赖图更干净,减少跨包引用造成的隐式构建错误。
6. 阶段结论
第 1 阶段(SRP)目标达成:
“将统计职责从核心执行路径剥离”已经完成,且测试全绿。
下一阶段可以按计划推进:
- DIP:GC 接口抽象化。
- OCP:Generator/AST 访问者化重构。