mini_racer_rs 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. checksums.yaml +7 -0
  2. data/AI-DISCLOSURE.md +13 -0
  3. data/CHANGELOG.md +45 -0
  4. data/COMPATIBILITY.md +235 -0
  5. data/Cargo.lock +1263 -0
  6. data/Cargo.toml +62 -0
  7. data/LICENSE +21 -0
  8. data/README.md +254 -0
  9. data/THIRD_PARTY_NOTICES.md +4501 -0
  10. data/crates/mini_racer_engine/Cargo.toml +25 -0
  11. data/crates/mini_racer_engine/build.rs +13 -0
  12. data/crates/mini_racer_engine/src/context.rs +802 -0
  13. data/crates/mini_racer_engine/src/control.rs +239 -0
  14. data/crates/mini_racer_engine/src/error.rs +88 -0
  15. data/crates/mini_racer_engine/src/fast_calls.rs +681 -0
  16. data/crates/mini_racer_engine/src/lib.rs +20 -0
  17. data/crates/mini_racer_engine/src/notify.rs +107 -0
  18. data/crates/mini_racer_engine/src/process/code_cache.rs +412 -0
  19. data/crates/mini_racer_engine/src/process/fork.rs +261 -0
  20. data/crates/mini_racer_engine/src/process/platform.rs +194 -0
  21. data/crates/mini_racer_engine/src/process/tasks.rs +96 -0
  22. data/crates/mini_racer_engine/src/process/watchdog.rs +185 -0
  23. data/crates/mini_racer_engine/src/process/workers.rs +165 -0
  24. data/crates/mini_racer_engine/src/process.rs +69 -0
  25. data/crates/mini_racer_engine/src/runner.rs +1560 -0
  26. data/crates/mini_racer_engine/src/serialize.rs +253 -0
  27. data/crates/mini_racer_engine/src/snapshot.rs +221 -0
  28. data/crates/mini_racer_engine/src/spin.rs +23 -0
  29. data/crates/mini_racer_engine/src/sys.rs +566 -0
  30. data/crates/mini_racer_sync/Cargo.toml +18 -0
  31. data/crates/mini_racer_sync/src/channel.rs +621 -0
  32. data/crates/mini_racer_sync/src/fork.rs +116 -0
  33. data/crates/mini_racer_sync/src/lib.rs +20 -0
  34. data/crates/mini_racer_sync/src/lock.rs +49 -0
  35. data/crates/mini_racer_sync/src/parker.rs +44 -0
  36. data/crates/mini_racer_sync/src/primitives.rs +10 -0
  37. data/crates/mini_racer_sync/src/tasks.rs +345 -0
  38. data/crates/mini_racer_sync/src/termination.rs +1234 -0
  39. data/crates/mini_racer_sync/src/workers.rs +283 -0
  40. data/crates/mini_racer_wire/Cargo.toml +18 -0
  41. data/crates/mini_racer_wire/src/decode.rs +926 -0
  42. data/crates/mini_racer_wire/src/lib.rs +67 -0
  43. data/crates/mini_racer_wire/src/marker.rs +43 -0
  44. data/crates/mini_racer_wire/src/text.rs +106 -0
  45. data/crates/mini_racer_wire/src/write.rs +187 -0
  46. data/ext/mini_racer_rs/Cargo.toml +26 -0
  47. data/ext/mini_racer_rs/build.rs +16 -0
  48. data/ext/mini_racer_rs/extconf.rb +78 -0
  49. data/ext/mini_racer_rs/src/context.rs +485 -0
  50. data/ext/mini_racer_rs/src/decode.rs +450 -0
  51. data/ext/mini_racer_rs/src/encode.rs +333 -0
  52. data/ext/mini_racer_rs/src/errors.rs +156 -0
  53. data/ext/mini_racer_rs/src/fork.rs +31 -0
  54. data/ext/mini_racer_rs/src/gc_root.rs +117 -0
  55. data/ext/mini_racer_rs/src/guard.rs +7 -0
  56. data/ext/mini_racer_rs/src/host.rs +339 -0
  57. data/ext/mini_racer_rs/src/lib.rs +44 -0
  58. data/ext/mini_racer_rs/src/options.rs +269 -0
  59. data/ext/mini_racer_rs/src/platform.rs +42 -0
  60. data/ext/mini_racer_rs/src/rb_raw.rs +368 -0
  61. data/ext/mini_racer_rs/src/snapshot.rs +149 -0
  62. data/ext/mini_racer_rs/v8-archives.sha256 +8 -0
  63. data/lib/mini_racer/shared.rb +420 -0
  64. data/lib/mini_racer/truffleruby.rb +439 -0
  65. data/lib/mini_racer/version.rb +6 -0
  66. data/lib/mini_racer.rb +3 -0
  67. data/lib/mini_racer_rs/internal_error.rb +7 -0
  68. data/lib/mini_racer_rs/version.rb +5 -0
  69. data/lib/mini_racer_rs/vocabulary.rb +105 -0
  70. data/lib/mini_racer_rs.rb +33 -0
  71. data/licenses/mini_racer.txt +21 -0
  72. data/licenses/rust/SOURCES.tsv +6 -0
  73. data/licenses/rust/std/COPYRIGHT +34 -0
  74. data/licenses/rust/std/COPYRIGHT-library.html +29357 -0
  75. data/licenses/rust/std/LICENSE-APACHE +176 -0
  76. data/licenses/rust/std/LICENSE-MIT +25 -0
  77. data/licenses/v8/SOURCES.tsv +20 -0
  78. data/licenses/v8/abseil-cpp/LICENSE +203 -0
  79. data/licenses/v8/chromium-build/LICENSE +27 -0
  80. data/licenses/v8/components.tsv +29 -0
  81. data/licenses/v8/dragonbox/LICENSE-Boost +23 -0
  82. data/licenses/v8/fast_float/LICENSE-MIT +27 -0
  83. data/licenses/v8/highway/LICENSE +371 -0
  84. data/licenses/v8/icu/LICENSE +568 -0
  85. data/licenses/v8/inspector_protocol/LICENSE +27 -0
  86. data/licenses/v8/libc++/LICENSE.TXT +311 -0
  87. data/licenses/v8/libc++abi/LICENSE.TXT +311 -0
  88. data/licenses/v8/llvm-libc/LICENSE.TXT +278 -0
  89. data/licenses/v8/rapidhash/LICENSE +34 -0
  90. data/licenses/v8/simdutf/LICENSE +18 -0
  91. data/licenses/v8/siphash/LICENSE +116 -0
  92. data/licenses/v8/utf8-decoder/LICENSE +19 -0
  93. data/licenses/v8/v8/LICENSE +61 -0
  94. data/licenses/v8/v8/LICENSE.fdlibm +6 -0
  95. data/licenses/v8/v8/LICENSE.strongtalk +29 -0
  96. data/licenses/v8/v8/LICENSE.v8 +26 -0
  97. metadata +156 -0
data/Cargo.toml ADDED
@@ -0,0 +1,62 @@
1
+ [workspace]
2
+ exclude = ["crates/mini_racer_wire/fuzz"]
3
+ members = [
4
+ "crates/mini_racer_engine",
5
+ "crates/mini_racer_sync",
6
+ "crates/mini_racer_wire",
7
+ "ext/mini_racer_rs",
8
+ ]
9
+ resolver = "3"
10
+
11
+ [workspace.package]
12
+ edition = "2024"
13
+ license = "MIT"
14
+ publish = false
15
+ repository = "https://github.com/andymai/mini_racer_rs"
16
+ rust-version = "1.91"
17
+ version = "1.0.0"
18
+
19
+ [workspace.dependencies]
20
+ deno_core_icudata = "=0.78.0"
21
+ libc = "0.2"
22
+ loom = "0.7.2"
23
+ magnus = {version = "=0.9.2", features = ["rb-sys"]}
24
+ mini_racer_engine = {path = "crates/mini_racer_engine"}
25
+ mini_racer_sync = {path = "crates/mini_racer_sync"}
26
+ mini_racer_wire = {path = "crates/mini_racer_wire"}
27
+ proptest = {version = "1", default-features = false, features = ["std"]}
28
+ rb-sys = "=0.9.130"
29
+ thiserror = "2"
30
+ v8 = "=152.2.0"
31
+
32
+ [workspace.lints.rust]
33
+ unexpected_cfgs = {level = "warn", check-cfg = ["cfg(loom)"]}
34
+ unsafe_code = "deny"
35
+ unsafe_op_in_unsafe_fn = "deny"
36
+
37
+ [workspace.lints.clippy]
38
+ all = {level = "deny", priority = -1}
39
+ allow_attributes_without_reason = "deny"
40
+ dbg_macro = "deny"
41
+ exit = "deny"
42
+ expect_used = "deny"
43
+ mem_forget = "deny"
44
+ multiple_unsafe_ops_per_block = "deny"
45
+ nursery = {level = "warn", priority = -1}
46
+ panic = "deny"
47
+ pedantic = {level = "warn", priority = -1}
48
+ print_stderr = "deny"
49
+ print_stdout = "deny"
50
+ todo = "deny"
51
+ undocumented_unsafe_blocks = "deny"
52
+ unimplemented = "deny"
53
+ unwrap_used = "deny"
54
+ # Internal crates with no published API surface.
55
+ missing_errors_doc = "allow"
56
+ missing_panics_doc = "allow"
57
+ module_name_repetitions = "allow"
58
+
59
+ [profile.release]
60
+ debug = "line-tables-only"
61
+ # FFI guards (invariant I4) turn panics into errors, which needs unwinding.
62
+ panic = "unwind"
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Andy Aragon
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,254 @@
1
+ <div align="center">
2
+
3
+ # mini_racer_rs
4
+
5
+ A drop-in replacement for mini_racer, implemented in Rust on V8 15.2.
6
+
7
+ [![CI](https://github.com/andymai/mini_racer_rs/actions/workflows/ci.yml/badge.svg)](https://github.com/andymai/mini_racer_rs/actions/workflows/ci.yml)
8
+ [![Commit activity](https://img.shields.io/github/commit-activity/m/andymai/mini_racer_rs?label=commits%2Fmonth)](https://github.com/andymai/mini_racer_rs/commits/main)
9
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
10
+ [![Ruby 3.3 | 3.4 | 4.0](https://img.shields.io/badge/ruby-3.3%20%7C%203.4%20%7C%204.0-red.svg)](#scope)
11
+ [![V8 15.2](https://img.shields.io/badge/V8-15.2-4285F4.svg)](#how-it-works)
12
+ [![mini_racer 0.22.1 suite](https://img.shields.io/badge/mini__racer%200.22.1%20suite-passing-success.svg)](COMPATIBILITY.md)
13
+
14
+ **[Install](#install)** · **[Quick start](#quick-start)** · **[API](#api-at-a-glance)** · **[Switching](#switching-from-mini_racer)** · **[Benchmarks](#benchmarks)** · **[How it works](#how-it-works)** · **[Compatibility](COMPATIBILITY.md)** · **[Architecture](ARCHITECTURE.md)**
15
+
16
+ </div>
17
+
18
+ mini_racer_rs implements the `MiniRacer` Ruby API from mini_racer 0.22.1, with faster Ruby and JavaScript round trips, V8 15.2, prebuilt native gems, fiber scheduler support, and safer handling of difficult values. It defines the `MiniRacer` namespace and ships `lib/mini_racer.rb`, so `require "mini_racer"` and ExecJS continue to work.
19
+
20
+ > **Pre-release:** Nothing is published to RubyGems.org yet, and the release workflow is held. Install from the repository until the first release. mini_racer_rs is an independent project and is not affiliated with the Discourse staff who maintain mini_racer.
21
+
22
+ ## Highlights
23
+
24
+ - **4 to 5 times faster Ruby round trips.** An eval, call, or callback round trip takes 1.3 to 1.6 µs, against 6.1 to 6.6 µs in mini_racer. A call on a context with `timeout:` takes 1.6 µs instead of 21.1 µs because one watchdog serves the process.
25
+ - **1.9 times faster Babel transforms.** A transform through ExecJS takes 5.3 ms instead of 10.0 ms. Babel itself takes 5.3 ms inside V8 15.2, against 10.3 ms inside V8 13.6. Terser and React rendering take about the same time in both gems.
26
+ - **4.6 times faster production bundle loads.** Scripts of 1 MB or more compile from a process-wide V8 code cache. A 44 MB production server rendering bundle loads into a new context in 215 ms against 983 ms. The 8.2 MB MUI bundle loads and renders once in 121 ms against 167 ms, and startup plus Babel takes 14.8 ms against 44.8 ms.
27
+ - **Less memory for loaded and abandoned contexts.** A context holding the 44 MB bundle takes 136 MB against 301 MB, and one holding the MUI bundle takes 56 MB against 110 MB. When 20,000 contexts are left to Ruby's garbage collector, resident memory peaks at 102 MB against 2,916 MB.
28
+ - **Forks pause worker work, and children use their own threads.** For forks made through Ruby's `fork`, `IO.popen("-")`, or `Process.daemon`, mini_racer_rs waits for context runners, pauses its worker pool, and waits for running tasks; an async `WebAssembly.compile` in a new default-mode child completes on the child's own pool. On Ubuntu 24.04 with glibc 2.39, 20 of 20 mini_racer children aborted with signal 6 after loading and rendering React, while 20 of 20 mini_racer_rs children exited cleanly. On Fedora, mini_racer children hung within the first 50 forks of `test/stress/fork_busy.rb`, while mini_racer_rs completed 300 forks per run on Fedora and Ubuntu.
29
+ - **V8 15.2.124.1.** mini_racer 0.22.1 uses V8 13.6.233.17.
30
+ - **No compiler at release install.** Native gems cover glibc Linux on x86_64 and aarch64, plus macOS on arm64 and x86_64. Each carries extensions for Ruby 3.3, 3.4, and 4.0. mini_racer 0.22.1 ships as source and compiles C++ against libv8-node.
31
+ - **Fiber scheduler support.** A fiber waiting on JavaScript lets other fibers on its thread run. Three 100 ms busy loops in separate contexts and fibers finish in 100 ms, against 300 ms when mini_racer blocks the thread.
32
+ - **171 of 174 upstream tests pass.** The mini_racer suite runs unmodified, with its other 3 tests skipped by mini_racer itself. ExecJS passes 197 of 198 tests on its MiniRacer runtime, with 1 skipped.
33
+ - **Difficult values return safely.** A `Map` with a function key returns an error Hash instead of aborting the process with SIGABRT. `Object(1n)` returns `1`, and an offset typed array returns its buffer as a binary String.
34
+ - **Four checked layers.** A script enforces crate boundaries, `unsafe` is confined to four fenced modules, loom model-checks the thread handoff, fuzzing covers the wire codec, sanitizers run nightly, and results are differentially tested against mini_racer. The release pipeline runs the full suites for every released extension.
35
+
36
+ All performance numbers compare mini_racer_rs with mini_racer 0.22.1 on Ruby 3.4.2 and Linux on an AMD Ryzen 9 7950X3D, with both gems pinned to the same eight cores and the `performance` governor.
37
+
38
+ ## Install
39
+
40
+ The package switch is one Gemfile line:
41
+
42
+ ```diff
43
+ - gem "mini_racer"
44
+ + gem "mini_racer_rs"
45
+ ```
46
+
47
+ Run `bundle install`. Bundler is required because RubyGems may otherwise activate the original `mini_racer` first. Loading both gems raises a `LoadError` that names the conflict. Reverse the Gemfile change to switch back.
48
+
49
+ Until the first release publishes native gems, build from a checkout as described in [CONTRIBUTING.md](CONTRIBUTING.md). Run `bin/setup`. Source builds require Rust 1.91 or newer and libclang. Once published, installing a native gem will compile nothing.
50
+
51
+ ## Quick start
52
+
53
+ This example was run against both gems and produced exactly the outputs shown:
54
+
55
+ ```ruby check
56
+ require "mini_racer"
57
+
58
+ context = MiniRacer::Context.new(timeout: 500, max_memory: 50_000_000)
59
+
60
+ context.eval("function add(a, b) { return a + b }")
61
+ context.call("add", 2, 3) # => 5
62
+
63
+ context.attach("ruby.upcase", ->(text) { text.upcase })
64
+ context.eval("ruby.upcase('hello')") # => "HELLO"
65
+
66
+ context.eval_await("Promise.resolve(42)") # => 42
67
+
68
+ begin
69
+ context.eval("while (true) {}")
70
+ rescue MiniRacer::ScriptTerminatedError => e
71
+ e.message # => "terminated"
72
+ end
73
+ context.eval("add(1, 1)") # => 2
74
+
75
+ snapshot = MiniRacer::Snapshot.new("var greeting = 'hello from a snapshot'")
76
+ MiniRacer::Context.new(snapshot: snapshot).eval("greeting") # => "hello from a snapshot"
77
+ ```
78
+
79
+ ExecJS 2.10.2 uses it as its MiniRacer runtime:
80
+
81
+ ```ruby check
82
+ require "execjs"
83
+
84
+ ExecJS.runtime.name # => "mini_racer (V8)"
85
+ ExecJS.eval("[1, 2].map(x => x * 2)") # => [2, 4]
86
+ ```
87
+
88
+ ## API at a glance
89
+
90
+ | API | What it does |
91
+ |---|---|
92
+ | `Context.new(**options)` | Creates a context with its own V8 isolate and runner thread. |
93
+ | `Context#eval(source, filename: nil)` | Runs JavaScript and returns the result as Ruby values. |
94
+ | `Context#call(name, *args)` | Calls a global JavaScript function by dotted name with Ruby arguments. |
95
+ | `Context#eval_await`, `#call_await` | Like `eval` and `call`, but wait for a returned Promise to settle and return its value. A rejection raises `MiniRacer::RuntimeError`. |
96
+ | `Context#attach(name, callable)` | Exposes a Ruby proc or method under a dotted JavaScript name. |
97
+ | `Context#load(path)` | Evaluates a JavaScript file. |
98
+ | `Context#stop` | Terminates a running script from another thread. |
99
+ | `Context#dispose` | Frees the isolate. |
100
+ | `Context#heap_stats`, `#heap_snapshot`, `#write_heap_snapshot(path)`, `#low_memory_notification` | Inspects the heap, writes snapshots, or asks V8 to free memory. |
101
+ | `Context#pump_message_loop`, `#perform_microtask_checkpoint` | Runs pending V8 tasks and microtasks. |
102
+ | `Snapshot.new`, `#warmup!`, `#dump`, `Snapshot.load` | Creates, warms, serializes, and loads startup snapshots. |
103
+ | `Platform.set_flags!(*flags)` | Sets V8 flags before the first context. |
104
+
105
+ `Context.new` accepts these options:
106
+
107
+ | Option | Effect |
108
+ |---|---|
109
+ | `timeout:` | Maximum milliseconds for each eval or call before `MiniRacer::ScriptTerminatedError`. |
110
+ | `max_memory:` | V8 heap limit in bytes, checked after each garbage collection. An overage raises `MiniRacer::V8OutOfMemoryError`, and the context remains usable. |
111
+ | `ensure_gc_after_idle:` | Idle milliseconds before one full garbage collection. `0` disables it. |
112
+ | `snapshot:` | A `MiniRacer::Snapshot` used to initialize the context. |
113
+
114
+ Errors are `MiniRacer::ParseError`, `MiniRacer::RuntimeError` with JavaScript frames in its backtrace, `MiniRacer::ScriptTerminatedError`, `MiniRacer::V8OutOfMemoryError`, `MiniRacer::ContextDisposedError`, `MiniRacer::SnapshotError`, and `MiniRacer::PlatformAlreadyInitialized`. All descend from `MiniRacer::Error`.
115
+
116
+ ## Switching from mini_racer
117
+
118
+ ### What changes
119
+
120
+ The public API and require path stay the same. The main changes are native installation, faster small calls and Babel transforms, fiber-aware waiting, newer V8 behavior, and safer conversion of difficult values. Applications that make many small calls gain the most. See [Highlights](#highlights) for measurements.
121
+
122
+ ### Check before you ship
123
+
124
+ - You're affected if your tests or code compare exact JavaScript error messages or locale-formatted text. V8 15.2 can word them differently from V8 13.6.
125
+
126
+ - You're affected if an attached Ruby callback calls back into its own context from another fiber, such as inside `Enumerator#next`. mini_racer_rs raises `ThreadError`; mini_racer allows it.
127
+
128
+ - You're affected if a callback exits by `throw`, returns out of a proc, or is stopped by `Thread#kill`. The script ends with `Error: terminated`, then the jump continues after `eval` returns. mini_racer hangs.
129
+
130
+ - You're affected if JavaScript reads a non-ASCII message from a callback exception. mini_racer_rs passes UTF-8, such as `"café"`, while mini_racer passes `"café"`.
131
+
132
+ - You're affected if `fork`, `IO.popen("-")`, or `Process.daemon` runs after V8 starts. Both modes wait up to 500 ms for runners, interrupting JavaScript after 50 ms; both then pause the worker pool and wait up to 100 ms for running tasks. Uninterruptible operations and workers can outlast these best-effort waits, and the fork proceeds. Default children reject inherited contexts; new contexts run worker work on the child's own threads, but are best effort after either wait expires. `:single_threaded` children take over idle inherited contexts, reject busy ones (all after runner timeout), and accept new contexts.
133
+
134
+ - You're affected if, with `:single_threaded`, you fork in a way that bypasses Ruby's `fork`, such as a C-level `fork()`. Inherited contexts are unusable in the child.
135
+
136
+ - You're affected if another thread calls `dispose` while a call is running. It returns after the call finishes and the isolate is gone. mini_racer returns immediately and frees the isolate later.
137
+
138
+ - You're affected if your code references `MiniRacer::Loader`. It does not exist in mini_racer_rs.
139
+
140
+ See [COMPATIBILITY.md](COMPATIBILITY.md) for every difference.
141
+
142
+ ### Stay on mini_racer if
143
+
144
+ - You deploy on musl-based Linux such as Alpine. mini_racer supports it through libv8-node's musl gems, while mini_racer_rs does not install there. Neither gem supports Windows.
145
+
146
+ - You need a gem from RubyGems.org today. mini_racer_rs is pre-release, while mini_racer is the established gem maintained by Discourse staff.
147
+
148
+ - Download size matters more than compile time. The largest mini_racer_rs native gem is 65.6 MB because it includes extensions for three Ruby versions. The mini_racer download on x86_64 Linux is 47.1 MB, followed by compilation.
149
+
150
+ ### Gems that depend on mini_racer
151
+
152
+ If another gem brings in `mini_racer`, point Bundler at the repository's shim. It is an empty gem named `mini_racer`, version 0.22.1, that depends on the `mini_racer_rs` version from the same checkout.
153
+
154
+ ```ruby
155
+ gem "mini_racer_rs"
156
+ gem "mini_racer", github: "andymai/mini_racer_rs", tag: "vX.Y.Z", glob: "shim/*.gemspec"
157
+ ```
158
+
159
+ The repository tests shim resolution with `path:` sources and rtlcss in `test/consumers/transitive/`. The `github:` form has not been run end to end. A dependency whose `mini_racer` requirement excludes version 0.22.1 cannot use the shim.
160
+
161
+ ## Benchmarks
162
+
163
+ | Workload | mini_racer 0.22.1 | mini_racer_rs | Ratio |
164
+ |---|---:|---:|---:|
165
+ | Babel transform of a 3 KB JSX component, through ExecJS | 10.0 ms | 5.3 ms | 1.9x |
166
+ | Terser minify of a 78 KB library, through ExecJS | 51.6 ms | 51.2 ms | 1.0x |
167
+ | React server render of 1,000 items (17 KB of HTML), through ExecJS | 192 µs | 174 µs | 1.1x |
168
+ | React in a new context: load the bundle and render 1,000 items, through ExecJS | 12.0 ms | 12.1 ms | 1.0x |
169
+ | Handlebars render of 50 rows, through `Context#call` | 47.9 µs | 40.1 µs | 1.2x |
170
+ | Autoprefixer on Bootstrap's 280 KB stylesheet, through ExecJS | 27.2 ms | 23.7 ms | 1.1x |
171
+ | MUI dashboard server render of 200 rows and 299 icons (469 KB of HTML), through ExecJS | 32.2 ms | 30.5 ms | 1.1x |
172
+ | MUI in a new context: load the 8.2 MB bundle and render once, through ExecJS | 167 ms | 121 ms | 1.4x |
173
+ | Load a 44 MB production server rendering bundle into a new context, through ExecJS | 983 ms | 215 ms | 4.6x |
174
+
175
+ The table reports the eight library workloads plus the 44 MB production server rendering bundle load. Results are medians of 5 runs from the release build of commit `2513a48`, with every workload producing identical output in both gems. They were measured on Ruby 3.4.2 and Linux on an AMD Ryzen 9 7950X3D, with the CPU governor set to `performance` and both gems pinned to CPUs 0 through 7.
176
+
177
+ ```sh
178
+ bundle exec ruby bench/run.rb --cpus 0-7
179
+ ```
180
+
181
+ [BENCHMARKS.md](BENCHMARKS.md) describes the method, per-call costs, memory results, and mini_racer's own `benchmark/serde` suite.
182
+
183
+ <!-- compat-table:start -->
184
+ ## Compatibility
185
+
186
+ | Engine | Done | Planned | Deviation |
187
+ |---|---|---|---|
188
+ | CRuby | 54 | 0 | 25 |
189
+ | TruffleRuby | 58 | 0 | 0 |
190
+
191
+ See [COMPATIBILITY.md](COMPATIBILITY.md).
192
+ <!-- compat-table:end -->
193
+
194
+ ## How it works
195
+
196
+ mini_racer_rs uses the `v8` crate, rusty_v8 152.2.0, with Ruby bindings through magnus and rb-sys. Each context owns a V8 isolate and a native runner thread with a stack of at least 2 MiB, so V8 frames never use a Ruby thread's stack. A per-context lock permits one dispatch at a time.
197
+
198
+ ```text
199
+ Ruby thread runner thread (one per context)
200
+ ─────────── ───────────────────────────────
201
+ MiniRacer::Context#eval
202
+ │ encode request (V8 wire format)
203
+ ├──────────── channel ───────────▶ receive request
204
+ │ spin, then park (GVL released) run JavaScript in the V8 isolate
205
+ │ serialize the result
206
+ ◀──────────── channel ─────────────┤
207
+ │ decode straight into Ruby objects
208
+ ▼
209
+ result
210
+ ```
211
+
212
+ A waiting Ruby thread first spins briefly with the GVL held, then spins without it, then parks with the GVL released. Under a fiber scheduler, only the waiting fiber parks. Values use V8's serialization format and decode directly into Ruby objects without an intermediate tree. Repeated objects retain their identity.
213
+
214
+ One watchdog thread per process enforces every context's `timeout:`. In `:single_threaded` mode, a fork quiesces the runners, the child takes over contexts that were idle, and contexts in use raise there. In every process, mini_racer_rs points V8's platform at a copied vtable whose worker-thread slots post to a pool it owns. V8's platform has one worker thread, which never receives a task. A fork pauses the pool, and the parent resumes it when the fork returns; inherited contexts raise in the child in the default multi-threaded mode.
215
+
216
+ The implementation is split into four crates:
217
+
218
+ - `mini_racer_wire`: the wire codec, with no unsafe code.
219
+ - `mini_racer_sync`: the thread protocol, with no unsafe code and loom checking.
220
+ - `mini_racer_engine`: V8 process state and runners, and the only crate that names `v8`.
221
+ - `mini_racer_rs`: the Ruby extension, and the only crate that names magnus or rb-sys.
222
+
223
+ [ARCHITECTURE.md](ARCHITECTURE.md) follows a call end to end and covers the design in depth.
224
+
225
+ ## Scope
226
+
227
+ mini_racer_rs deliberately does not:
228
+
229
+ - Run on Windows or musl-based Linux such as Alpine. Supported platforms are glibc Linux on x86_64 and aarch64, plus macOS on arm64 and x86_64.
230
+
231
+ - Provide Node.js APIs. JavaScript runs in plain V8 without file system, network, or process access. JavaScript can reach only the Ruby callables you `attach`.
232
+
233
+ - Work reliably without Bundler. RubyGems may activate the original mini_racer first.
234
+
235
+ - Satisfy a dependency that pins a `mini_racer` version other than 0.22.1. This includes minibars with its `~> 0.6.4` requirement.
236
+
237
+ - Extend the `MiniRacer` API beyond mini_racer 0.22.1. Deliberate differences are recorded in [COMPATIBILITY.md](COMPATIBILITY.md).
238
+
239
+ Supported Ruby implementations are CRuby 3.3, 3.4, and 4.0, plus TruffleRuby through mini_racer's unchanged TruffleRuby backend.
240
+
241
+ ## Documentation
242
+
243
+ | Document | Covers |
244
+ |---|---|
245
+ | [ARCHITECTURE.md](ARCHITECTURE.md) | Crates, a call end to end, values, termination, callbacks, promises, snapshots, forks, fiber schedulers, safety rules, and verification. |
246
+ | [COMPATIBILITY.md](COMPATIBILITY.md) | Every public API item and deliberate difference from mini_racer 0.22.1, with its reason. |
247
+ | [BENCHMARKS.md](BENCHMARKS.md) | Benchmark method and full results. |
248
+ | [STABILITY.md](STABILITY.md) | Versioning, the public API, unstable interfaces, and support policy. |
249
+ | [SECURITY.md](SECURITY.md) | Vulnerability reporting, untrusted JavaScript, and supply-chain controls. |
250
+ | [CONTRIBUTING.md](CONTRIBUTING.md) | Setup, checks, tests, and conventions. |
251
+
252
+ ## License
253
+
254
+ mini_racer_rs is available under the [MIT License](LICENSE). [AI-DISCLOSURE.md](AI-DISCLOSURE.md) describes how AI tooling was used to build the project.