Lua C 宿主完整实践:受信任计数器插件 · 确定性验收规格至参考解答
前置为整数与栈协议和userdata 所有权。目标是让 C 宿主加载本地可信 Lua 插件,导出带生命周期的计数器,再验证一个整数返回值。示例未执行;以下结果均为预期,不代表构建、运行或测试通过。
确定性验收规格
Lua宿主应该验证哪些失败输入?tests.json 共十九条规格,覆盖五条成功与十四条失败,包含上下边界、重复关闭、越界、关闭后访问、错误类型、错误返回值、加载错误及 CLI 错误。捕获 stdout/stderr 后以 UTF-8 解码,并把 CRLF 规范化为 LF,再对 stdout 做精确文本比较,错误流只匹配稳定片段;这不是跨平台原始字节相等的保证。Windows C 文本模式可能输出 CRLF,绝对路径、Lua 行号和系统文件错误也会因平台而变。
string-delta.lua 和 float-delta.lua 证明整数子类型校验,huge-delta.lua 证明参数范围,add.lua 的两条越界输入证明在加法前拒绝。syntax-error.lua 应在 load 阶段失败;not-function.lua 编译并执行成功却违反 chunk 返回协议;object-error.lua 通过调用缺失能力在 callback 阶段失败。它的文件名只表示运行时错误测试,并不构造 table 类型错误对象;后者在诊断篇另作机制解释。
验收者应从项目根逐条用 args 调用宿主,并检查退出码、stdout 和 stderrContains。这里没有执行规格,也没有宣称 C 或 Lua 的 AST 检查通过。静态审阅可以核对文件、分支与预期对应,不能证实目标平台的编译器、链接器和 Lua 分配失败行为。
能力限制与提交语义
不打开Lua标准库是不是安全沙箱?不是;这里只缩小能力面,未限制 CPU 或内存。插件仍能写无限循环、递归或分配 table;数值范围仅保护计数器业务域和算术,不提供进程资源预算。本工程只接受本地受信任脚本,不面向公网用户提交代码。
回调出错也不自动回滚先前成功的 add。若脚本先 add 再触发错误,内部值已经变化;本例因为一次性状态被关闭而不再观察这个值,但不能据此宣称 pcall 具有事务性。长期复用、持久化或共享资源版本应在运行前准备快照或在校验后提交,独立定义取消和失败策略。
当前内存契约是 Counter 内联数据由 Lua 管理、状态由 main 持有、无回调外部分配;不是“内存使用有固定上限”。未来加入 allocator 预算也需考虑 Lua 之外的 C 库分配,以及内存耗尽时错误报告和终结器的行为。不可信代码应采用独立进程与操作系统资源/权限限制,不能只靠删掉 io、os、debug。
综合练习
- 新增插件先 add(1),再调用缺失函数,预测是否有 stdout 成功结果,以及此前计数器是否变化。
- 将接口扩展为返回 table,列出必须校验的字段和是否触发元方法的决策,不直接修改现有协议。
- 把宿主改为循环处理多个插件时,失败路径需要新增什么清理和隔离约定?
参考解答
- 退出 1、stdout 为空;add 已经发生,pcall 不回滚内存。本例随后销毁状态,业务结果不再发布。
- 至少定义 table 类型、字段名、类型、必需性与数值范围;选择 raw 读取可以避免字段查询执行元方法,但仍需校验缺字段。不能只做 tostring 或强转。
- 记录本次引用,失败也 unref 并重置变量;关闭本次 userdata,定义全局变更和其他持久对象是否回滚,拒绝留存已失效借用指针;必要时每请求新状态以减少共享污染,但资源预算仍需另行设计。
版本与规则来源:Lua 5.4 正式手册、官方下载校验值,2026-10-17 核对。工程 README 与本篇共同明确未执行边界。
继续阅读
- 知识专题
- 知识专题
- 排错