rusty_racer 0.1.1 → 0.1.3
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.
- checksums.yaml +4 -4
- data/README.md +73 -0
- data/ext/rusty_racer/Cargo.toml +1 -1
- data/ext/rusty_racer/src/lib.rs +137 -2232
- data/ext/rusty_racer/src/marshal.rs +798 -0
- data/ext/rusty_racer/src/ops.rs +1061 -0
- data/ext/rusty_racer/src/stack.rs +292 -0
- data/ext/rusty_racer/src/watchdog.rs +236 -0
- data/lib/rusty_racer/execjs.rb +118 -0
- data/lib/rusty_racer/version.rb +1 -1
- data/lib/rusty_racer.rb +13 -3
- metadata +13 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 746bc2e7e881d601b38f6052fb45f4f87d72d4580a46ea738e2c716af6641f82
|
|
4
|
+
data.tar.gz: 4fcc456589eabd593e506d7c941b7fe95635400225aac9807908a008f63e73ad
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 20aeb8c39cc3f321b5015b78ff2c4ae54553babd54735bbb256aeaea16c2428cccc03141a55c179ebad34822243d2d39508517df33f34d6236a7a368cbe274b5
|
|
7
|
+
data.tar.gz: 5ff495f94287dfd8464855d5e20c84c4743b91a3d320760decf17244dad5efa67c503b74dd2994792059e0f7236b10992157bfd5c4cb5681b02fe162b47a8a4d
|
data/README.md
CHANGED
|
@@ -9,6 +9,38 @@ Embed [V8](https://v8.dev/) in Ruby, built on [rusty_v8](https://crates.io/crate
|
|
|
9
9
|
> is **thread-confined**: every operation must happen on that owner thread, or it
|
|
10
10
|
> raises. `Isolate#terminate` is the exception — it is safe from any thread.
|
|
11
11
|
|
|
12
|
+
## Highlights
|
|
13
|
+
|
|
14
|
+
- **ES modules**, including dynamic `import()` with an embedder-owned resolver —
|
|
15
|
+
not just classic scripts.
|
|
16
|
+
- **Faithful value marshalling**: `BigInt`, `Date`, `Map`, `Set`, typed binary
|
|
17
|
+
(`Uint8Array`/`ArrayBuffer` ↔ binary `String`), and shared/cyclic object
|
|
18
|
+
graphs all round-trip — no lossy JSON hop.
|
|
19
|
+
- **In-thread execution** — V8 runs on the calling Ruby thread, with no dedicated
|
|
20
|
+
V8 thread and no per-op thread hop; fast when you run many small ops.
|
|
21
|
+
- **Drop-in [ExecJS](#execjs) runtime** — any ExecJS consumer switches with no
|
|
22
|
+
code change.
|
|
23
|
+
- **Snapshots, realms (`Context`s), host callbacks, and a bytecode cache.**
|
|
24
|
+
- **Resource limits on both axes** — a `timeout_ms` (time) and a `memory_limit`
|
|
25
|
+
(space), each catchable: a runaway script fails just its own `eval`, leaving
|
|
26
|
+
the isolate usable, instead of aborting the process.
|
|
27
|
+
- **Precompiled gems** bundle V8 for Linux/macOS × Ruby 3.3–4.0 — no V8 build,
|
|
28
|
+
no Rust toolchain.
|
|
29
|
+
|
|
30
|
+
### Compared to mini_racer
|
|
31
|
+
|
|
32
|
+
[mini_racer](https://github.com/rubyjs/mini_racer) is the mature, widely-deployed
|
|
33
|
+
incumbent — if you want a battle-tested binding or **Windows** support, reach for
|
|
34
|
+
it. rusty_racer differs where it counts for some workloads: native **ES modules +
|
|
35
|
+
dynamic import** (mini_racer is eval/classic-script oriented); **richer
|
|
36
|
+
marshalling** (the types above round-trip natively instead of through a
|
|
37
|
+
JSON-shaped projection); and **in-thread execution** with no per-op thread hop,
|
|
38
|
+
which is faster for overhead-dominated workloads (lots of tiny `eval`/`call`) and
|
|
39
|
+
at parity once the per-op JS work dominates. Both axes of resource limiting are
|
|
40
|
+
covered — a `timeout_ms` (time) and a `memory_limit` (space), each catchable. It
|
|
41
|
+
is also younger and **experimental** — fewer miles, no Windows yet. Parity with
|
|
42
|
+
mini_racer is not a goal; the overlap is convergent evolution, not a port.
|
|
43
|
+
|
|
12
44
|
## What it can do
|
|
13
45
|
|
|
14
46
|
Names follow V8's: an `Isolate` is the VM; it hands out `Context`s (v8::Context,
|
|
@@ -55,6 +87,25 @@ app.namespace["r"] # => 42
|
|
|
55
87
|
|
|
56
88
|
Classic `<script>`s work the same way: `ctx.compile("1 + 1").run` # => 2.
|
|
57
89
|
|
|
90
|
+
### Resource limits
|
|
91
|
+
|
|
92
|
+
An isolate can cap untrusted code on both axes. Each limit terminates the
|
|
93
|
+
offending op and raises a catchable error — the isolate stays usable afterward,
|
|
94
|
+
so one runaway script fails just its own `eval`, not the whole process.
|
|
95
|
+
|
|
96
|
+
```ruby
|
|
97
|
+
# Time: timeout_ms caps each eval/call (per-call override on Context#eval).
|
|
98
|
+
iso = RustyRacer::Isolate.new(timeout_ms: 1000)
|
|
99
|
+
iso.context.eval("for (;;) {}") # raises RustyRacer::ScriptTerminatedError
|
|
100
|
+
|
|
101
|
+
# Space: memory_limit caps the V8 heap in bytes (a soft limit, enforced at GC
|
|
102
|
+
# granularity). The isolate forces a GC and resets the ceiling on recovery.
|
|
103
|
+
iso = RustyRacer::Isolate.new(memory_limit: 64 * 1024 * 1024)
|
|
104
|
+
iso.context.eval("a = []; for (;;) a.push(new Array(1e6))")
|
|
105
|
+
# raises RustyRacer::V8OutOfMemoryError
|
|
106
|
+
iso.context.eval("1 + 1") # => 2 (still usable)
|
|
107
|
+
```
|
|
108
|
+
|
|
58
109
|
### Bytecode caching
|
|
59
110
|
|
|
60
111
|
V8 compiles lazily: the top level up front, each function body on first call.
|
|
@@ -122,6 +173,28 @@ warm isolate — a per-visit reset that avoids rebuilding the VM. Its contract:
|
|
|
122
173
|
suspended on the V8 stack (e.g. resetting a realm from inside one of its own
|
|
123
174
|
host fns).
|
|
124
175
|
|
|
176
|
+
## ExecJS
|
|
177
|
+
|
|
178
|
+
rusty_racer ships an optional [ExecJS](https://github.com/rails/execjs) runtime,
|
|
179
|
+
so any ExecJS consumer (asset pipelines, CoffeeScript/Babel/Uglify wrappers, …)
|
|
180
|
+
can run on V8-in-Ruby with no code change:
|
|
181
|
+
|
|
182
|
+
```ruby
|
|
183
|
+
require "rusty_racer/execjs"
|
|
184
|
+
ExecJS.runtime = RustyRacer::ExecJSRuntime.new
|
|
185
|
+
|
|
186
|
+
ExecJS.eval("'foo bar'.toUpperCase()") # => "FOO BAR"
|
|
187
|
+
ctx = ExecJS.compile("function add(a, b) { return a + b }")
|
|
188
|
+
ctx.call("add", 1, 2) # => 3
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
The adapter is **opt-in** — `rusty_racer` never requires `execjs` itself, so it
|
|
192
|
+
stays a non-dependency; `require "rusty_racer/execjs"` pulls it in only when you
|
|
193
|
+
ask. Values cross with ExecJS's JSON semantics (functions and `undefined` drop
|
|
194
|
+
out, Dates become ISO strings), matching what ExecJS's external runtimes give, so
|
|
195
|
+
results are identical whatever runtime a library picked. The integration is
|
|
196
|
+
verified against ExecJS's own runtime contract suite (`test/execjs_test.rb`).
|
|
197
|
+
|
|
125
198
|
## Threading
|
|
126
199
|
|
|
127
200
|
An `Isolate` runs V8 **in-thread** on the Ruby thread that created it, and is
|