`deprecate.rb` methods fail in WASM (wasm32-wasi): blocks not received by `class_eval` / `define_method`
RubyGems
### Describe the problem as clearly as you can
In Ruby 3.4+ compiled to WebAssembly (wasm32-wasi), the block-based patterns in `lib/rubygems/deprecate.rb` fail at boot. Converting these methods to use `class_eval <<~RUBY` with `def` (instead of `define_method`) resolves the issue.
The same code works correctly in Ruby 3.3 WASM builds and in all native (non-WASM) Ruby environments.
### Did you try upgrading RubyGems?
<!--
Make sure you're using the latest version of both RubyGems by running `gem
update --system`.
-->
### Post steps to reproduce the problem
1. Build Ruby 3.4 or 4.0 for WASM with parse.y parser
(to avoid the separate Prism crash — see ruby/prism#4065)
2. Boot a Rails application or any code that triggers `Gem::Deprecate`
### Which command did you run?
Boot a Rails application or any code that triggers `Gem::Deprecate` on Ruby 3.4+ compiled to wasm32-wasi.
### What were you expecting to happen?
The application boots successfully without errors, as it does in Ruby 3.3 WASM builds and all native Ruby environments.
### What actually happened?
```
ArgumentError: wrong number of arguments (given 0, expected 1..3)
from internal:gem_prelude:2:in 'class_eval'
from internal:gem_prelude:2:in 'module:Deprecate'
from internal:gem_prelude:2:in 'module:Gem'
from internal:gem_prelude:2:in '<main>'
```
We tested multiple approaches to isolate the problem. All tests used Ruby 3.4 compiled to WASM with parse.y as the parser.
| No. | Approach | Result | Error |
|-----|---------|--------|-------|
| 1 | `class_eval do...end` (original) | Fail | `ArgumentError: wrong number of arguments (given 0, expected 1..3)` |
| 2 | `class_eval {...}` | Fail | Same error as No.1 |
| 3 | Remove `class_eval`, use `define_method` directly | Fail | `tried to create Proc object without a block` |
| 4 | `define_method(name) do` (with parentheses) | Fail | Same error as No.3 |
| 5 | `class_eval <<~RUBY` with `define_method` inside | Fail | Same error as No.3 (block not received within string eval) |
| 6 | `class_eval <<~RUBY` with `def #{name}` | **Pass** | No error |
Key observations:
- Both `do...end` and `{}` block styles fail (No.1, No.2)
- `define_method` also cannot receive blocks (No.3, No.4, No.5)
- Only the combination of string-based `class_eval` and `def`-based method definition (no blocks at all) works (No.6)
- The identical `class_eval do...end` code in Ruby 3.3 WASM builds works without issues
### Run `gem env` and paste the output below
- Ruby: 3.4.8 / 4.0.2 (compiled to wasm32-unknown-wasip1 via ruby_wasm 2.9.0)
- RubyGems: bundled (3.6.x for 3.4, 3.7.x for 4.0)
- wasi_sdk: 24.0
---
**Workaround:** Convert `deprecate.rb`'s three methods (`deprecate`, `rubygems_deprecate`, `rubygems_deprecate_command`) from `class_eval do / define_method` to `class_eval <<~RUBY / def` style.
This eliminates all block passing and has no effect on non-WASM environments.
Patch file (for Ruby 4.0; minor differences for 3.4 due to method signature changes):
https://github.com/aim2bpg/rubree/blob/main/ruby_wasm_patches/fix-deprecate-class-eval-block.patch
**Related:**
- ruby/prism#4065 — `pm_parser_init` out-of-bounds memory access in WASM (must be fixed independently)
1 条评论