mruby 3.3 从 mruby 3.2 升级的完整变更指南:语言、C API 与构建系统的破坏性改动
【免费下载链接】fluent-bitFast and Lightweight Logs, Metrics and Traces processor for Linux, BSD, OSX and Windows项目地址: https://gitcode.com/GitHub_Trending/fl/fluent-bit
mruby 是轻量级 Ruby 实现,本仓库在 lib/nghttp2-1.65.0/third-party/mruby 目录下以第三方源码形式随 nghttp2 一并托管,供嵌入式场景参考与复用。本文以官方变更说明 lib/nghttp2-1.65.0/third-party/mruby/doc/mruby3.3.md 为骨架,系统梳理 mruby 3.3 相对 mruby 3.2 的用户可见变更,涵盖语言语义、构建配置、内存分配 API、C API、mrbgems、构建系统以及大量 Bug 修复,并结合本仓库内随附的 mruby 源码逐项印证。读完本文,你将掌握 mruby 3.2 升级到 3.3 时所有需要关注的破坏性改动、新 API 用法与踩坑清单,能够安全完成宿主应用(如嵌入式解释器、HTTP 服务扩展)的迁移。
文中所有标注**_NOTE_**的内容为官方明确提示的破坏性变更,升级前务必逐条核对。官方变更清单还包含一系列指向 mruby 项目 commit / issue / PR 的跳转链接,此处按技术内容整理为可执行的说明,不再粘贴外部地址。
一、语言层面的变更:语义修正与移除的旧方法
mruby 3.3 在语言语义上做了多处修正,其中super、别名、return跨 C 边界等改动直接影响既有 Ruby 代码的运行时行为。
1.1 别名(alias)与super配合工作正常
- aliases work properly with
super:在 mruby 3.2 及更早版本中,对方法进行alias后调用super可能出现语义偏差;3.3 修正了别名方法与super的组合行为。 callee方法与别名配合时的行为不同:callee(返回当前正在执行的方法名)在 mruby 中与别名的组合行为与 CRuby 不同,3.3 中该差异仍然存在,属于 mruby 特有的已知行为,升级后若代码依赖callee需重新验证。
1.2 新增Kernel#respond_to_missing?
mruby 3.3 在Kernel中定义了respond_to_missing?方法。该方法与method_missing配合使用,用于告知解释器某个"缺失的方法"实际上是可以响应的,从而让respond_to?对动态方法返回正确结果,是编写代理对象、动态方法分发类时的标准配套手段。
1.3 移除_inspect(带递归检查的inspect)
_inspect方法(即带递归检测的inspect内部实现)被整体移除。这意味着依赖_inspect完成对象自引用检测的代码必须改用其他方式(如自行维护访问标记),而不是继续调用_inspect。
1.4 移除__printstr__,改用print
__printstr__方法被移除,官方明确要求改用print。若在 mrblib 扩展或宿主代码中直接调用了__printstr__,升级后必须替换为print,否则将抛出 NoMethodError。
1.5 新增String#bytesplice
mruby 3.3 新增String#bytesplice方法,提供按字节位置安全地替换字符串片段的 API。从 src/string.c 的源码注释可以看到其支持的四种调用形式:
bytesplice(index, length, str) # 用 str 整体替换 [index, length) bytesplice(index, length, str, str_index, str_length) # 用 str[str_index, str_length] 替换 bytesplice(range, str) # 用 str 整体替换 range 区间 bytesplice(range, str, str_range) # 用 str 的子区间替换 range 区间底层实现str_bytesplice(src/string.c)同时处理了String#bytesplice与String#bytesplice涉及 Range 参数的解析,并处理了RSTRING_LEN(replace)与指定子区间两种取值分支。3.3.0 早期版本中该方法的 Bug([#5986])在 3.3 正式版已修复。
1.6 允许块中的return跨越 C 边界
在 mruby 3.2 中,块的return一旦穿过 C 函数边界(例如从 C 函数中 yield 的块中执行return)会触发异常或崩溃;3.3 通过修改OP_RETURN_BLK指令的语义(PR [#6125])允许这种跨 C 边界的return,并配套修复了"C 函数直接调用OP_RETURN_BLK时返回值错误"([#6126])以及"块展开可能指向非法 PC"([#6076])等问题。
二、配置与构建平台:Docker 构建与新平台支持
2.1 Docker 构建支持
mruby 3.3 起可以使用 Docker 完成构建与全部测试。仓库根目录的 Dockerfile 与 docker-compose.yml 即为该能力提供了配置。官方推荐的用法是直接执行docker-compose build,该方案同时负责运行全部 mruby 测试、执行 pre-commit 检查以及生成 YARD 文档(PR [#5961]),适合希望获得一致构建环境、避免本机工具链差异的开发者。
2.2 新平台支持
- DJGPP(MS-DOS):新增构建配置(PR [#6022]),支持在 MS-DOS 环境编译运行 mruby。
- Nintendo Wii:新增构建配置(PR [#6086]),将 mruby 带到家用游戏机平台。
2.3 平台改进
- Android:修复 Android 工具链(PR [#6013]),改善在 Android 上的构建体验。
- Dreamcast:
dreamcast_shelf build config被彻底重写(PR [#6130]),覆盖度与可用性大幅提升。
2.4 测试控制与虚拟机分派开关
- 允许针对特定 gem 禁用测试:构建系统现在可以针对单个 mrbgem 关闭测试,并对被禁用的测试给出警告(PR [#6012]),避免在裁剪 mrbgem 时测试任务报错。
MRB_NO_DIRECT_THREADING更名为MRB_USE_VM_SWITCH_DISPATCH(PR [#5902]):旧的MRB_NO_DIRECT_THREADING宏被替换为MRB_USE_VM_SWITCH_DISPATCH。该宏控制 mruby 虚拟机采用 switch 分派而非直接线程化(direct threading)执行字节码,凡是此前通过定义前者来切换 VM 执行方式的构建配置都必须改用新宏名。
三、mruby 内存 API:分配器可覆写与弃用
3.1mrb_default_allocf可被应用覆写
mruby 3.3 允许应用层直接定义mrb_default_allocf来接管 mruby 的默认内存分配逻辑。在 src/allocf.c 中可以看到其实现:
mrb_default_allocf(mrb_state *mrb, void *p, size_t size, void *ud)这是 mruby 中唯一直接调用标准 C 库内存分配函数的地方。因此,在宿主应用中重新定义同名函数,即可在不改动 mruby 源码的前提下全局定制内存管理(例如接入自有内存池、统计或沙箱配额)。官方的内存管理指南 doc/guides/memory.md 将自定义内存管理归纳为三种方式:
- 定义
mrb_default_allocf()覆写默认分配器; - 使用
mrb_open_allocf()为每个mrb_state指定独立分配函数; - 在
mrb_state创建后直接替换其allocf字段(指南中提及,但推荐度最低)。
其中第 2 种方式在 3.3 中被标记为弃用(见下文)。
3.2mrb_open_allocf将被弃用
mrb_open_allocf被官方标记为即将弃用(deprecated)。在 src/state.c 中仍保留其实现,同时 src/state.c 的逻辑显示:当传入的分配函数为NULL时,内部会回退到mrb_default_allocf。官方在 doc/guides/memory.md 中解释弃用原因:按mrb_state粒度做内存管理的真实使用场景几乎不存在,且会增加维护负担。API 声明位于 include/mruby.h:
MRB_API mrb_state* mrb_open_allocf(mrb_allocf f, void *ud);升级建议:若宿主代码使用mrb_open_allocf,优先改为覆写mrb_default_allocf(或直接调用mrb_open_core/mrb_open并使用默认分配器),为将来 API 移除做准备。
四、C API 变更:新增错误处理与可限制的allocate
4.1 新增错误处理 API
mruby 3.3 增加了一组新的错误处理 API 函数(对应 [8c8bbd9] 一系列 commit),宿主 C 扩展在抛出、捕获和检查异常时有更规范的入口可用。
4.2 新增mrb_vm_ci_env_clear()(MRB_API 导出)
新增mrb_vm_ci_env_clear()函数并以MRB_API对外导出(PR [#5945]),同时配套避免暴露REnv内部对象。该函数用于在特定时机清理调用信息(CI)中缓存的词法环境引用,对在 C 侧长期持有调用帧的场景有实际意义。
4.3 新增mrb_check_frozen_value()
新增mrb_check_frozen_value()函数,统一检查并抛出 frozen 对象被修改的异常。其实现位于 src/error.c,声明在 include/mruby.h:
MRB_API void mrb_check_frozen_value(mrb_state *mrb, mrb_value v);在 src/class.c 和 src/class.c 中,类变量赋值、initialize参数修改等路径均通过该函数做冻结检查,取代了此前各处零散的mrb_frozen_p手工判断。
4.4mrb_bug()不再做格式化
mrb_bug()不再接受格式化字符串参数(对应 [82a48bd])。
NOTE:如果宿主代码正在使用
mrb_bug(),必须传入简单字符串,或改用mrb_raise()系列函数抛出异常。格式化需求请自行先完成字符串构造。
4.5 编译器上下文不再使用mrbc_前缀
编译器上下文结构体停止使用mrbc_前缀命名(对应 [c5e3cbe])。旧名称仍以别名形式保留可用,但官方建议逐步替换为新名称,避免未来版本移除旧别名。
4.6Class#allocate可被禁止(MRB_UNDEF_ALLOCATOR/MRB_DEFINE_ALLOCATOR)
这是 3.3 的 C API 中最重要的安全相关变更(PR [#5979]、[#6122]、[#6123]):现在允许禁止某个类被allocate,防止外部绕过initialize直接构造实例。
- 禁用:
MRB_UNDEF_ALLOCATOR(c),将MRB_FL_UNDEF_ALLOCATE标志置位; - 显式允许:
MRB_DEFINE_ALLOCATOR(c),清除该标志; - 判断:
MRB_UNDEF_ALLOCATOR_P(c)。
宏定义位于 include/mruby/class.h:
#define MRB_UNDEF_ALLOCATOR(c) (mrb_assert((c)->tt == MRB_TT_CLASS), (c)->flags |= MRB_FL_UNDEF_ALLOCATE) #define MRB_UNDEF_ALLOCATOR_P(c) ((c)->flags & MRB_FL_UNDEF_ALLOCATE) #define MRB_DEFINE_ALLOCATOR(c) ((c)->flags &= ~MRB_FL_UNDEF_ALLOCATE)关键行为:该禁用标志会自动遗传给子类(PR [#6123] 使子类继承MRB_FL_UNDEF_ALLOCATE);如果子类确实需要allocate,必须显式调用MRB_DEFINE_ALLOCATOR()重新开启。这是编写严格封装类(如单例、对象池)的标准做法。
五、mrbgems 变更:拆分、重命名与新能力
mruby 3.3 对 mrbgems 做了一次较大范围的调整,其中mruby-binding与mruby-eval的重组是最容易踩坑的破坏性变更。
5.1 默认 gembox 新增调试器
default.gembox现在默认包含 mruby 调试器mruby-bin-debugger(mrdb,PR [#5966])。使用默认 gembox 构建后即可获得mrdb交互式调试工具,相关使用说明见 doc/guides/debugger.md。
5.2 mruby-bin-config:新增编译器/汇编器选项
mruby-config新增--cxx、--cxxflags、--as、--asflags、--objc、--objcflags选项(PR [#6054]),在集成 C++、汇编与 Objective-C 代码时可以直接从构建系统拿到对应编译器的完整参数。
5.3 mruby-binding:重命名与行为收紧
mruby-binding由 mruby 3.2 的mruby-binding-core更名而来(对应 [11af5db])。NOTE:mruby 3.2 用户若使用
mruby-binding-core,必须改用mruby-binding。- 实现
Binding#initialize_copy方法(PR [#5517]),修复复制 binding 对象时局部变量未正确分离的问题。 Kernel#binding现在只响应来自 Ruby 侧的调用(PR [#5981]),从 C 侧调用将不再返回 binding。
5.4 mruby-eval:并入 binding 功能
mruby-eval合并了 mruby 3.2 的mruby-binding(对应 [501b22a] 与 PR [#5989])。
NOTE:mruby 3.2 用户若使用
mruby-binding,在 3.3 中应改用mruby-eval。同时注意 mruby 3.2 的mruby-binding-core在 3.3 中对应mruby-binding,两处改名方向相反,升级时务必核对 gembox 与 build_config 中的 gem 名称。
5.5 其余 mrbgems 功能变更汇总
| mrbgem | 变更内容 |
|---|---|
| mruby-compar-ext | Comparable#clamp接受nil作为参数 |
| mruby-compiler | 为标识符 token 增加打印名称 |
| mruby-data | 允许空的 Data 对象 |
| mruby-enumerator | 移除内部属性方法obj、args、kwd、meth、fib;新增Enumerator#size |
| mruby-fiber | 新增以MRB_API导出的mrb_fiber_new()(声明见 include/mruby.h,实现见 mrbgems/mruby-fiber/src/fiber.c);允许从 C 调用Fiber#resume;Fiber#to_s输出格式改变 |
| mruby-io | 新增File#atime、File#ctime;IO.open支持"x"独占创建模式;File.new不再接受块 |
| mruby-method | Method#to_s输出格式改变 |
| mruby-numeric-ext | int.pow(n, m)的指数可接受 bigint |
| mruby-pack | 支持新指令j、J、b、B、# |
| mruby-range-ext | 新增Range#overlap?方法 |
| mruby-string-ext | 新增String#valid_encoding?方法 |
| mruby-struct | 未指定名字时允许空的 Struct |
| mruby-time | 允许 1900 年以前的年份;必要时支持 bigint 到time_t的转换;正确处理负的time_t |
六、构建系统变更:rake install与符号链接
6.1rake install任务扩展
rake install任务被扩展(PR [#5928])。
NOTE:受此影响,
mruby/bin/目录下的可执行文件默认变成符号链接(Windows 上为批处理文件)。如果此前脚本直接依赖mruby/bin/下的可执行文件,应改为直接引用构建目录中生成的真实实体,例如mruby/build/host/bin/。
6.2 presym 转义字符编码/解码
构建系统现在对 presym(预置符号表)中的转义字符进行编码与解码(PR [#6011]),并修复了扫描 presym 时的转义序列 Bug 与生成文件包含非法字符的问题(PR [#6045]、[#6044])。
6.3deep_clean清理构建目录
Rakefile的deep_clean任务现在会移除默认构建目标目录(PR [#6032]),彻底清理build与bin目录,避免旧产物污染新构建。
七、其他破坏性变更(Breaking Changes)
mrb_f_raise()变为内部函数(PR [#5923]、[#6070]):从MRB_API降级为内部函数,宿主 C 扩展不再能调用。mrb_make_exception()变为内部函数且签名改变(对应 [431f83e]、[78137f3]):参数与之前不同,宿主代码若引用需改走mrb_raise系列。File#path不再使用#to_path做隐式转换(对应 [d86c4a7]):传入非字符串对象时行为改变。- 每个测试文件的 mrb 隔离被移除(对应 [a20fbe5]):测试运行机制变化,mrbgem 测试共享状态的方式需重新审视。
RBreak记录 CI 位置(PR [#6103]):异常回溯(backtrace)信息中块跳出(break)相关的调用位置信息更准确。
八、Bug 修复清单(按问题领域归类)
mruby 3.3 修复了大量跨模块的缺陷,按主题归类如下,便于对应排查历史问题:
数值与运算
Rational#**缺失([#5724]);- 大数取整函数问题([0906cd7]);
- 64 位整数极值比较失败([#5975]);
~(-1 << 64)结果错误([#5987]);OP_LOADI32在get_int_operand()中的符号扩展问题([#5985])。
字符串与 IO
String#bytesplice的 Bug([#5986]);String#codepoints性能问题([#6061]);String#unpack1返回数组而非单个字符串([#6134]);TCPSocket#write失败([#5943]);- mruby-3.2.0 中
gets不工作([#5991]); IO#read配合 buf 时的问题([#6118]);check_file_descriptor()失败时未设置EBADF([#6120]);mrb_string_value_cstr空指针解引用([#6067]);mrb_addrinfo_unix_path空指针解引用([#6051]);- io.c 中的拼写错误([#5994])。
GC 与内存
- 栈错误导致 backtrace 信息内存不释放([#5789]);
- 分代 GC 中
clear_all_old的 Bug([b47c8b7]); - GC 性能可能退化的问题([#6041]);
mruby-os-memsize中 irep 大小计算错误([a264965])。
VM 与块/协程
- 用 C API 创建块时
mrb_yield崩溃([#5932]); - 带块的括号省略时 caller 行号错误([#5949]);
- 块参数中传入看似多余的参数([#5995]);
mrb_vm_exec栈溢出([#6068]);ensure段在 proc 内通过 return 退出时未执行([#6099]);- VM 中
break导致崩溃([#6108]); - 组合使用
mrb_fiber_resume()与Fiber#transfer时的问题([#6136]); - ngx_mruby 开发中 Fiber 相关的 SEGFAULT([#6060]);
Proc类处理中的空指针解引用([#6052]、[#6065]);Struct.new()相关的空指针解引用([#6066])。
类、模块与异常
- mrblib 代码中奇怪的
const_missing异常([#5725]); - 常量搜索路径不应从模块切换到 Object([#5946]);
binding.eval()对def表达式的处理与 CRuby 不同([#6089]);- 调用包装方法的超类失败([#6098]);
- 带哈希变量调用方法的行为([#5944]);
- 调用了被包装方法的超类([#6098])。
构建与工具链
- Windows 上
gem_init.c的非法转义序列([#5974]); - mruby 在 mrbgems 目录下构建失败([#6029]);
- 生成的 presym/table.h 包含非法字符([#6044]);
MRUBY_PACKAGE_DIR并非始终有值([#6064]);- VisualC++ 下 libmruby 命名问题([#6055]);
- Linux 构建产物中不正确的符号 sink([#6084]);
- 构建时报 'Remote branch HEAD not found in upstream origin'([#6087]);
- bintest 依赖目录([#6008]);
mrb-test/mrbgem.rake中mrb_state处理 Bug([3310e10])。
九、值得关注的用户可见 Pull Request
除了上述分类整理的修复,官方还单独列出以下用户可见 PR,其中多数已在对应章节展开,此处从工程影响角度再补几点提醒:
- Binding 相关:[#5517] 修复复制 binding 对象间局部变量未分离;[#5989] 将 binding-eval 集成进
mruby-eval。依赖 binding 做元编程的宿主代码升级后建议重点回归。 - C API 收紧:[#5923] 与 [#6070] 将
mrb_f_raise()降级为内部函数;[#5945] 新增mrb_vm_ci_env_clear()并避免暴露REnv对象。C 扩展需核对是否引用了被降级的函数。 - 分配控制:[#5979]、[#6122]、[#6123] 构成
Class#allocate禁用能力的完整实现(含子类继承标志),是安全敏感应用的首选加固点。 - Fiber 能力放宽:[#6097] 新增
mrb_fiber_new();[#6105] 实现Fiber#to_s;[#6106] 放宽 fiber 限制;[#6136] 修复 resume 与 transfer 组合问题。C 侧驱动协程的场景在 3.3 中显著更顺滑。 - 构建体验:[#5961] Docker 构建;[#6013] Android 工具链修复;[#6022] DJGPP;[#6086] Nintendo Wii;[#6130] Dreamcast 重写;[#6054] 扩展
bin/mruby-config。
十、升级到 mruby 3.3 的落地检查清单
综合以上全部变更,从 mruby 3.2 升级到 3.3 时可按下表逐项自查:
| 检查项 | 处理动作 |
|---|---|
是否使用mruby-binding-core/mruby-binding(3.2 命名) | 分别替换为mruby-binding/mruby-eval |
是否调用__printstr__、_inspect | 改用print/ 自行实现递归检查 |
是否定义MRB_NO_DIRECT_THREADING | 改用MRB_USE_VM_SWITCH_DISPATCH |
是否调用mrb_bug()并传入格式化参数 | 传简单字符串或改用mrb_raise() |
是否调用mrb_f_raise()、mrb_make_exception() | 改为mrb_raise系列公开 API |
是否调用mrb_open_allocf() | 改为覆写mrb_default_allocf()或默认分配器 |
是否依赖mruby/bin/下的可执行文件 | 改为引用mruby/build/host/bin/等构建产物 |
是否使用带mrbc_前缀的编译器上下文名 | 迁移到新命名(旧名仍兼容但建议替换) |
自定义类是否需要allocate | 若禁止则MRB_UNDEF_ALLOCATOR(),子类需显式MRB_DEFINE_ALLOCATOR() |
File#path、IO.open、File.new的调用方式 | 核对#to_path隐式转换移除、"x"模式、块参数限制 |
官方还维护了此前各版本(3.0/3.1/3.2)的变更说明文档,分别位于 doc/mruby3.0.md、doc/mruby3.1.md、doc/mruby3.2.md,连续跨版本升级时可一并参考;随仓库附带的 NEWS 文件与本文内容互为印证,也可作为快速检索入口。
【免费下载链接】fluent-bitFast and Lightweight Logs, Metrics and Traces processor for Linux, BSD, OSX and Windows项目地址: https://gitcode.com/GitHub_Trending/fl/fluent-bit
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考