PR #3301 重构了 Axmol v3 的 Lua 绑定系统。长期使用的 Python 和 tolua++ 工具链已被基于 PowerShell、C# 和 libclang 的生成器取代。运行时,sol2 处理常规类型转换,而 Axmol 保持对对象标识、生命周期、继承和回调的控制。

这不仅仅是生成器的替换。目标是让 Lua API 可靠地遵循 C++ API,保留现有项目依赖的对象行为,并使每次生成的变更在 CI 中可审查和可验证。

对于大多数 Lua 项目,现有的 sprite:method() 调用、Lua 派生类、动态字段和回调将继续正常工作。引擎开发者通过一个入口点重新生成绑定:

axmol genbindings

使用描述导出的 API 而非维护生成器脚本

新生成器直接读取 C++ AST,而非依赖脆弱的文本规则。类型化的 JSON 配置用于选择头文件、命名空间、类、字段、重命名、跳过项和平台条件。当前覆盖了引擎核心、RHI、UI、3D、物理、NavMesh、音频、视频、WebView、Spine、FairyGUI 和扩展中的 15 个模块和 499 个类注册。

会生成类、构造函数、继承、重载、默认参数、枚举、选定字段和 std::function 回调。大型模块被拆分为多个翻译单元,以避免过大的编译任务。JSON API 清单使得 C++ 变更对 Lua 可见的影响易于审查。

生成是事务性的:解析失败不会替换上次的有效输出。CI 从当前源码重新生成,并拒绝 generated 下未提交的变更。在更改 C++ API 后忘记更新 Lua 绑定,现在会自动失败,而不再是手动审查的问题。

生成代码、运行时和适配器之间的清晰界限

新布局具有三个职责:

  • generated 包含可从普通 C++ 签名安全派生的注册;
  • runtime 管理 Lua VM、Axmol 对象标识、对等表、继承、回调和失效;
  • adapters 保留 Lua 表、特殊所有权、可变参数工厂和无法自动推断的平台桥接。

旧的 auto/ 树和 Axmol 拥有的 tolua++ 运行时代码已移除,而 manual/ 已重组为 adapters/ 以描述其实际用途。添加普通 API 不再需要复制注册和参数检查样板代码。

sol2 提供栈转换和可调用适配,但它不拥有 Axmol 对象。重复推送同一个 ax::Object 仍会生成相同的逻辑 Lua 对象。动态 Lua 字段保持附着,真实的派生类型被保留,当原生对象被销毁时,所有相关的用户数据都会失效。运行时支持 Lua 5.1 到 5.5 以及 LuaJIT 2.1 或更新版本。

保留继承、虚分发和重载

生成器使用 Clang 的覆盖信息来识别真正冗余的虚绑定。仅当派生类的 Lua 名称、返回类型、常量性、参数和默认参数与导出的基类声明完全匹配时,派生类的注册才会被省略。

当派生类引入同名的重载时,完整的重载组仍会注册,以避免隐藏基类 API。因此 Sprite 可以继承 Node::setPosition 等方法,缓存的调用如 ax.Node.setPosition(sprite, ...) 仍然有效,Lua 覆盖和 C++ 虚分发都保持现有行为。

更安全的回调和对象生命周期

每个 Lua 回调现在都属于特定的 VM 和所有者线程。协程会归一化到其主 VM,独立的 VM 可以分别创建和关闭。跨线程调用永远不会触及 Lua 状态。Lua 错误通过受保护调用处理,并返回安全的原生结果。回调状态在重入期间保持活跃,因此回调可以安全地清除或替换自身。

失效也按每个 VM 跟踪。原生销毁会同步清除已暴露给 Lua 的用户数据中的原生指针,借出的事件用户数据在其回调结束时过期。从未进入 Lua 的对象则避免注册和锁定开销。

性能:定位成本而不削弱安全性

新的独立绑定性能测试通过大量 Sprite 上的普通 Lua 方法调用来测量绑定开销。以下数据来自相同的 Windows Release/O3 环境。它们比较的是实现变更,并非不同设备上的固定预期:

调用路径 约 55 FPS 时的星星数量
移除了 Lua 方法调用 约 31,000
缓存方法 约 17,000
旧绑定,普通调用 约 12,500–13,000
新绑定(查找优化前) 约 9,000–9,500
新绑定(查找优化后) 约 14,500

缓存结果表明生成的包装器已接近旧绑定。大部分差距在于解析每个 sprite:method() 调用。最终路径首先检查类及其已注册基类上的具体成员,然后在未命中时使用完整的访问器和 sol2 回退。在 Release 构建中,所有者线程失效还避免了重复的 WeakPtr 查找。

那些通过削弱生命周期检查来提高数字的实验已被回滚,一个没有可测量收益的闭包缓存也被移除。已接受的实现仍然支持运行时类表编辑、对等字段、访问器、协程、多个 VM 和过期用户数据拒绝。性能提升保留了绑定的语义,而不是用它们换取基准分数。

项目需要迁移的内容

大多数 Lua 调用保持兼容,但重构移除了 v3 中不再支持的几个接口:ax.Controller Lua API 和基于旧 GLProgram 栈的过时 OpenGL 测试已移除,迁移后的 Node ScriptHandler 事件路径现在使用原生回调。仓库中的 extensions/scripting/lua-bindings/MIGRATION.md 记录了剩余的关于表、所有权和仅原生的变更。

引擎贡献者应通过 JSON 配置和重新生成来暴露普通 API。适配器保留用于 C++ 签名无法描述的行为。应用程序开发者通常无需仅因生成器变更而修改 Lua 源码,但应查看迁移指南中明确移除的接口。

新系统为生成、运行时语义、特殊适配器和验证提供了清晰的所有权。它减少了手写绑定和历史兼容层,同时为 Axmol 的 API 增长和持续性能回归测试提供了更安全的基础。