最后核对:2026-10-05(S9 工序④删区对齐——头部现状横幅改写为退役态、架构图切换单轨新版;前一沿革 2026-10-04 S8 收官 + 0.8.0 发版件对齐)
定位:教学 C 子集参考执行引擎(白箱;迁移 v1 = C only,C++ 已裁砍随 oracle 退役)。类比 quickjs 之于 JS——小、白箱、可嵌入、行为与标准对照诚实。 范围:本仓库只做后端(MIT 许可),提供核心引擎与出口(wasm-gc 单出口多宿主 + serve/vitro CLI);前端由社区基于出口协议实现。 定位转型的完整决策依据见
后端定位与白箱计划.md。⚠️ 现状(2026-10-05,S9 工序④删区):本文主体描述的是迁移前 Rust 实现(
native/)的架构——该实现已物理删除(档案 = tagrust-oracle-freeze+ git 历史)。现役唯一实现是 MoonBit(vitro/engine,包切分与逐片进度见 MoonBit迁移总计划.md,操作手册见moonbit/AGENTS.md)。本文的语义章节(内存模型 / 时间旅行帧语义 / 协议 / 诊断七元组)仍作 MoonBit 侧同语义实现的参照有效;文中一切native/src/*.rs、crates/*实现锚均为历史锚(对应删区前终态),MoonBit 侧对应物见各章内 MoonBit 注记与 S8时间旅行与教学智能总览。全文按 MoonBit 目标架构重写已列入待办。
目录
- 1. 设计目标
- 2. 三出口一核心架构
- 3. 编译器管线
- 4. VitroVM 执行模型
- 5. 内存模型
- 6. 统一模式 / 时间旅行
- 7. 诊断与修复系统
- 8. 算法与数据结构支持
- 9. 协议层与出口 API
- 10. 仓库结构
- 11. 关键设计决策
1. 设计目标
Vitro 为教学场景提供一台白箱的 C/C++ 子集执行引擎:
- 行为可对照:以 Clang / Clang++ 为唯一 Golden 来源,任何差异如实记录(
*_FAILURES.md),不粉饰; - 执行可观察:指令级单步、变量快照、内存与指针状态、算法步骤语义,全部通过协议暴露;
- 过程可回放:统一模式(时间旅行)支持回退到任意历史步并正向重放;
- 错误可教学:中文诊断 + 错误码 + 修复建议 + 知识卡片 + 根因推断;
- 安全可保证:1MB 线性内存内的指令级边界检查与教学安全检测(越界 / UAF / Double-Free / 无效 free / 递归深度 / 步数熔断);
- 可嵌入:C ABI / wasm32 / JSON-lines 三个出口,任意语言可消费。
明确不做:不是通用 C 编译器、不是生产级工具链、不是 Clang 替代品;不追求完整 C17/C++20 语义。
2. 三出口一核心架构
┌──────────────────────────────────────────┐
│ vitro 引擎核心(Rust workspace) │
│ 编译器管线 + VitroVM + 统一模式 + 诊断 │
│ ── 禁止平台 API 耦合 ── │
└───────────────┬──────────────────────────┘
│ native/src/session_api.rs
│ (会话语义中立层:三出口共用同一套入口语义)
┌───────────────────────────────┼───────────────────────────────┐
│ │ │
┌───────▼────────┐ ┌─────────▼─────────┐ ┌──────────▼──────────┐
│ 出口 1 │ │ 出口 2 │ │ 出口 3 │
│ C ABI (capi) │ │ wasm32 │ │ vitro_cli serve │
│ cdylib/staticlib│ │ .wasm + JS 绑定 │ │ JSON-lines (NDJSON) │
│ vitro_abi_version│ │ 浏览器 / 在线教学 │ │ headless 脚本化消费 │
└─────────────────┘ └───────────────────┘ └──────────────────────┘
消费者:vitro_cli 消费者:社区 Web 前端 消费者:IDE 后端 /
scripts/shadow_verify.go(已退役)(capi 直调) 移动浏览器"看"场景 判分服务 / 自动化
第三方 IDE(P/Invoke 等)
架构图(由 go run ./scripts/gen_svg 生成):2026-10-05 删区后为单轨格局(MoonBit 现役引擎 + Go 防线层 + Clang 真值源;下节 Rust 历史架构已随删区退役):
架构纪律(写进 AGENTS.md 编码约定):
- 新能力一律先落语言中立的 Rust 层,三个出口只做薄包装;
- 复杂结构(StepPayload、内存区域、事件流)过边界统一走 JSON 字符串——性能换稳定性与版本容忍度;
- capi 是公共 API,承诺即契约:
vitro_abi_version()版本化(当前2.1.0),加函数 = minor,改签名/语义 = major; - 状态码约定:
0 = 成功 / 负数 = 入参错误 / 正数 = 领域状态(1 = trap,2 = 等待输入);Session 指针句柄非线程安全;输入输出为 UTF-8。
MoonBit 目标架构对齐:迁移完成后的出口形态收敛为 wasm-gc 单出口多宿主(浏览器主交付 / Node 22+ CI 主力 / Wasmtime 显式开 -W gc;宿主接口 4 函数 + 21 方法表,总计划 F-5)——capi 45 导出中 28 无消费者(亲证),capi 后续批次已裁不做。wasm-gc 单出口已于 2026-09-29 落地(S7 批五号:gateway library + gateway/wasm foreign_library 薄壳 + Node 宿主 host.js;零功能性 imports)。冻结期内本文第 2 节三出口仍为现役形态,两侧架构纪律同源(MoonBit 侧等价物:依赖严格单向无环 + .mbti 三面收敛,见总计划 §4)。
3. 编译器管线
源代码字符串(可多文件)
│
▼
Lexer::tokenize() vitro_lexer → Vec<Token>
│
▼
Parser::parse() vitro_parser → AST(Box<Program>)
│
▼
TypeChecker vitro_typeck → 类型标注 + 教学诊断(E3xxx / W3xxx)
│
▼
BytecodeGen vitro_codegen → Vec<Instruction>(VitroVM 扁平字节码)
│
▼
SourceMap + 字符串数据段 + 符号表(含 decl_line、变量归属函数)
设计要点:
- AST 用 Rust enum 而非多态类层次(
Expr/Stmt+Box<Expr>),类型系统为完全递归的vitro_ast::Type; - 零进度保护:Parser 在位置未推进时强制
advance(),杜绝死循环(历史事故见docs/archive/INCIDENT_2026_04_27_PARSER_INFINITE_LOOP.md); - 错误处理不 panic:诊断收集进
Vec<Error>后统一返回,跨出口边界处catch_unwind兜底; - 多文件:
vitro_compile_unit逐文件编译,vitro_compile_all链接;file_ranges维护"全局行号 ↔ 文件内行号"映射(多文件语义标签与诊断定位依赖它)。
子集规范(行为契约,修改实现必须同步):
- C:
C语言子集规范.md - C++(已裁砍,规范归档):ARCHIVE_C++子集规范.md
MoonBit 侧对应管线(逐片重建中,包切分见总计划 §4):vitro/engine/{source, opcode}(L0)→ diag(L1)→ ast(L2)→ lexer + parser(L4,含独立预处理 pass)→ names + typeck + libc(L5)→ codegen + bytecode(L6)→ memory + host + vm(L7,✅ 2026-09-26 收官)→ protocol(L8,✅ 批一号+段二 2026-09-27)→ session(L8,✅ 批二号 2026-09-28)→ gateway + gateway/wasm(L8,✅ 批五号 2026-09-29——wasm-gc 单出口落地)+ time_travel + teaching + steps + diagnostics(L8,✅ S8 四域 2026-10-04 收官——时间旅行/教学智能/诊断,见S8 总览)+ cmd/serve(✅ 批三号 2026-09-28,native stdio 壳)+ cmd/vitro 总入口(✅ 2026-10-04 CLI 出口总账 #37:run/compile/step/api 四子命令);0.8.0 = S8 收官版已发布(2026-10-05,tag vitro-engine-0.8.0,线上验收三件全绿)。已收官片与 Rust oracle 的对拍口径:lexer 6002 TSV 逐字节一致、parser 601 语料 AST+诊断逐字节一致、codegen A 级 598/598(含 code 段逐指令)、执行链 cmd/run + vm_diff/clang_direct 双防线全绿(DIFF 0)、teaching 311/311 标注 golden 逐条一致。
4. VitroVM 执行模型
VitroVM 是栈式虚拟机,核心循环逐条解释执行 Instruction,每条指令携带 SourceLoc 供错误映射。
// 示意(实际定义见 crates/vitro_runtime 与 crates/vitro_vm)
struct Instruction {
op: OpCode, // 常量/局部变量/内存/算术/比较/跳转/调用/单步事件……
operand: i32,
loc: SourceLoc, // 源码位置
}
为什么自研 VM 而不是复用现成解释器(历史决策,Phase 2 起):项目初期用 wasm3 作为执行引擎,Phase 3 深入后确认它在教学场景存在结构性瓶颈——
| 能力 | 通用 WASM 解释器 | VitroVM |
|---|---|---|
| 单步调试 | 无法暂停/恢复,只能阻塞宿主函数 | 每条指令后可检查暂停标志,同步单步 |
| 运行时中文诊断 | 只能翻译英文 trap 字符串 | 在除零/越界现场直接读取变量值与声明行 |
| 内存可视化 | 读原始字节,不知道变量名 | 自带符号表,知道某地址是 arr[2] |
| 零侵入可视化 | 需注入 host call | VM 层直接发射教学事件 |
| 执行步数限制 | 需 patch 运行时 | 原生支持(会话级保险丝) |
| 安全隔离 | 依赖宿主内存模型 | 自带边界检查,同等安全 |
核心优势:局部变量也存放在线性内存中,因此 &x、scanf("%d", &x)、指针/数组/结构体语义与真实 C 一致;单步在主线程同步执行(零线程,无线程泄漏风险)。
5. 内存模型
VitroVM 使用 1MB 线性内存,按用途分段(NULL 陷阱区 / 字符串字面量 + 全局区 / 堆 / 栈),具体布局常量与运行时不变量由 vitro_runtime 统一定义。
关键设计:局部变量在内存中
// Call 指令:在线性内存中分配栈帧(向下增长),参数从表达式栈搬入
// LoadLocal / StoreLocal:按 frame.locals_base + index * 4 读写内存
好处:&x 是真实地址;scanf 可直接写入;数组/指针语义与真实 C 一致。
堆:2026-09-11 决议改为 bump 分配 + 有界隔离(quarantine),把 churn 与 leak 分离,配合"三道墙"(1MB 堆墙 / 步数保险丝 / region 表封顶,决议 §6 口径)保证教学场景下的内存行为有界且可解释——见 堆有界隔离决议.md。
教学安全检测(运行时,指令级):
| 检测 | 说明 |
|---|---|
| E3070 栈缓冲区溢出 | 越界写入/读取的栈数组访问 |
| E3060 / E3061 | Use-After-Free / Double-Free(freed_logs 指令层检查) |
| 无效 free 三场景 | 非堆指针 / 已释放 / 未对齐等 |
| E3072 | 头文件循环包含 |
| NULL 陷阱区 | 解引用 NULL 立即 trap 并给出中文诊断 |
| 步数 / 调用深度熔断 | 会话级保险丝,可控地撞上限并给出教学 trap |
6. 统一模式 / 时间旅行
学生点"运行"后,引擎自动逐条推进并收集每一步状态;消费者可随时暂停、单步、拖到任意历史步。
后端组成:
| 组件 | 位置 | 职责 |
|---|---|---|
| VM 全量快照 | vitro_vm::snapshot |
1MB 内存 + 运行时状态 + 内存管理状态 |
| 检查点管理器 | vitro_vm::snapshot::CheckpointManager |
按固定间隔保存快照(按语义标签决定密度) |
| 统一执行引擎 | native/src/unified/engine.rs |
批量自动执行 run_batch + seek_to + Trap 自动回退 |
| 每步收集器 | native/src/unified/collector.rs |
变量快照、调用栈、可视化事件、语义标签、热力图 |
| 帧缓存 / 流 | native/src/unified/{stream,types}.rs |
窗口 2000 帧、差分编码 StepPayloadDelta |
| 根因推断 | native/src/unified/{root_cause,trace_analyzer}.rs |
轨迹切片 + Trap 根因提示 |
| 算法步骤标注 | crates/vitro_algorithm_steps/ |
预定义算法步骤模板 → 教学描述 |
语义契约(消费者必须遵守,schema 见 STEP_PAYLOAD_SCHEMA_V0_1.md):
- 回退到第 N 步时,文件状态、变量历史、输出窗口必须一致;
- 越窗 seek = 检查点恢复 + 正向重放;
- 输出按
OutputKind(Stdout / Stderr / Note)分通道,消费方不得对文本做正则清洗。
设计细节与消费方契约见 统一模式设计.md; 为什么这套体验本身是竞争力见 VM教学体验优势.md。
7. 诊断与修复系统
7.1 三级信息架构
| 级别 | 内容 | 载体 |
|---|---|---|
| L1 感知 | 表情 + 一句话 + 修复按钮 | 诊断条目(含位置与 error code) |
| L2 理解 | 通俗解释 + 代码片段 + 对比 | 诊断 detail + 结构化修复建议 |
| L3 原理 | 概念详解 + 内存动画描述 + 练习 | 知识卡片(JSON,由前端渲染) |
7.2 运行时诊断优势(利用符号表读取真实值)
| 错误 | 静态分析只能说 | Vitro 运行时能说 |
|---|---|---|
| 数组越界 | "索引可能越界" | "当 i=10 时越界了,数组大小是 5" |
| 空指针 | "p 可能未初始化" | "p 的值是 0x00000000,声明于第 3 行,之后无赋值" |
| 无限循环 | "循环条件可能恒真" | "已执行 100000 步,i 始终是 1(你可能注释掉了 i++)" |
7.3 结构化自动修复
诊断在语言中立层(native/src/diagnostics/)生成结构化修复数据(fixKind + 精确替换区间 + 替换文本),三个出口原样透出,不各自实现修复逻辑:
| 级别 | 类型 | 示例 | 自动? |
|---|---|---|---|
| L1 语法 | 缺 ; } ) ]、` |
→ |
|
| L2 语义 | <=→<、补初始化 |
精确替换 | ✅ 全自动 |
| L3 逻辑 | = vs ==、死代码 |
预览确认 | 需确认 |
| L4 教学 | 递归边界、排序逻辑 | 仅建议 | 否 |
前端切割时(2026-09-11)自动修复应用器本体从 FRB 出口层下沉到
native/src/diagnostics/auto_fix.rs,三出口共用 (见CHANGELOG.md [Unreleased] Removed段)。
7.4 认知推理
| 能力 | 位置 |
|---|---|
轨迹切片 + Trap 根因推断(RootCauseHint) |
native/src/unified/trace_analyzer/ |
认知误区模式(MisconceptionPattern)+ 学习路径推荐 |
native/src/diagnostics/ |
| 知识图谱(概念节点 + 关系边) | native/src/diagnostics/ |
| 代码意图推断(CFG + 数据流 + IntentInference) | native/src/compiler/{cfg,data_flow,intent} |
路线与设计见 认知推理系统设计.md、算法与数据结构教学设计.md。
8. 算法与数据结构支持
8.1 零侵入可视化
学生写纯 C,编译器/VM 自动识别算法并发射教学事件——不需要 vis_array() 之类的额外代码。
for (int i = 0; i < n - 1; i++)
for (int j = 0; j < n - i - 1; j++)
if (arr[j] > arr[j + 1]) { int t = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = t; }
引擎侧产出:算法识别结果(置信度)、每步语义标签、vis_events(compare / swap / update)、数组快照、指针快照、热力图。
这些字段通过 StepPayload 协议暴露,渲染完全交给消费方。
设计见 ARCHIVE_零侵入可视化设计.md。
8.2 数据结构支持
当前子集(数组 / 指针 / struct / union / malloc;C++ 模板类容器随砍 C++ 裁定冻结于 oracle,MoonBit 侧零迁移)已可表达并可观察:
| 结构 | 表达方式 |
|---|---|
| 数组 / 动态数组 | int[] / malloc |
| 单链表 / 双链表 | struct Node { int val; Node* next; } |
| 栈 / 队列 | 数组 + 索引,或链表 |
| 二叉树 | struct TreeNode { int val; TreeNode *left, *right; } |
| C++ 容器(已裁砍 2026-09-20) | vitro_vec<T> / vitro_list<T> 等内置容器(仅 Rust 冻结区现役;MoonBit 侧由 C# 批 BCL 数据驱动承接,见 CSharp前端引入计划) |
模板源与教材算法清单见 模板维护指南.md、ARCHIVE_数据结构模板路线图.md。
9. 协议层与出口 API
协议先行:StepPayload schema v0.1 是公共承诺(STEP_PAYLOAD_SCHEMA_V0_1.md),字段只增不改语义,废弃走双写过渡期。协议与引擎内部表示解耦,引擎侧优化(CoW、脏页恢复等)可独立推进。
| 出口 | 形态 | 主要入口 |
|---|---|---|
| C ABI | native/include/vitro_capi.h |
vitro_abi_version / vitro_compile_json / vitro_run_json / vitro_get_program_output* / vitro_step_next_json / vitro_get_step_payloads_json / vitro_set_breakpoints / vitro_free_string 等 |
| wasm32 | .wasm;TS 类型生成链 @vitro/protocol 已接线(2026-09-22) |
与 capi 同一套 C ABI 符号;MoonBit 侧 wasm-gc 单出口已落地(S7 批五号 2026-09-29:gateway.invoke 单口 21 方法表 + Node 宿主 host.js) |
| serve | JSON-lines(NDJSON) | compile / run / output.delta / step.begin / step.next / seek / payload.get / breakpoints.set / memory.regions / config.* / session.* + dump 族(ast/typeck/symbols/diagnostics_probe);MoonBit 侧 cmd/serve 已落同构方法族(S7 批三号起,serve_smoke 双宿主对拍锁定;step 族 S8 已接 2026-10-01,dump 族 S8 已接 2026-10-01~02) |
capi 分批落地进度与签名评审依据见 CAPI评审回复与实现状态.md(已归档——后续批次经 U2 拍板裁不做); serve 方法与协议契约见 CLI使用手册.md §6。
输出通道(E-P1-5):引擎把程序 stdout / stderr 与引擎附注分别打标,绝不再混流后由消费方正则清洗 (历史教训:十余处清洗规则语义不一致,且程序自己打印同类文本时误删真实输出)。
10. 仓库结构
见 README.md(项目结构)与 AGENTS.md(关键目录)——MoonBit 迁移期与 moonbit/ workspace 并行保持同步。
文档索引见 docs/README.md。
11. 关键设计决策
| 决策点 | 选择 | 理由 |
|---|---|---|
| 执行引擎 | 自研 VitroVM(替代 wasm3) | 教学专用:完全可控的单步/诊断/内存可视化;局部变量入线性内存以支持 &x |
| 编译目标 | 自定义扁平字节码(替代 WASM) | 只实现教学子集需要的指令;简化编译器与 VM 耦合 |
| 出口形态 | 三出口共用一套语义层 | 防"typeck 与 codegen 双轨语义"的漂移覆辙 |
| 边界数据 | JSON 字符串 | 稳定、可版本化、任意语言可消费;ctypes 生产验证已证明够用 |
| 对外契约 | 协议先行(schema v0.1) | 协议是公共承诺;与引擎内部表示解耦后各自演进 |
| 可视化 | 零侵入自动识别 | 初学者写纯 C,系统自动给出教学事件 |
| 诊断风格 | 运行时值注入 + 三级信息 | L1 感知 / L2 理解 / L3 原理 |
| 算法修复 | 诊断 + 引导,不代写代码 | 保护学习过程 |
| 内存安全 | 1MB 线性内存 + 指令级检查 + 有界堆隔离 | 教学场景需要"可控地失败并解释" |
| 许可证 | MIT | 对教育产品集成最友好(无 copyleft 顾虑) |
| 前端 | 切割给社区(2026-09-11) | 前端 30 条审查发现几乎全是架构级;移动端取舍持续拖累桌面端 |
| 原生移动端 | 放弃 | 软键盘/IME/触控目标是自研编辑器在移动端的固有硬伤;"看"由 wasm + 浏览器覆盖 |
| 引擎语言 | MoonBit 绞杀者迁移(2026-09-18 定稿,同仓双区) | LLM 效率 / VM 吞吐 / Wasmtime / 快照往返四道证伪门实测全过;Rust 版冻结为差分对照 oracle 逐包对拍,"每搬一包趁 Rust 版仍在做差分扫描"是唯一不重踩坑路径;C++ 随裁砍零迁移(2026-09-20),依据见 MoonBit迁移总计划.md §1 |
历史设计(wasm3 时代、Flutter 前端时代)保留在 ../archive/,仅供追溯。