quickjs 0.15.1 → 0.17.0.pre
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/CLAUDE.md +8 -0
- data/README.md +66 -0
- data/ext/quickjsrb/quickjsrb.c +361 -57
- data/ext/quickjsrb/quickjsrb.h +31 -2
- data/ext/quickjsrb/vendor/polyfill-intl-en.min.js +4 -3
- data/lib/quickjs/function.rb +1 -11
- data/lib/quickjs/runnable.rb +23 -0
- data/lib/quickjs/version.rb +1 -1
- data/lib/quickjs.rb +16 -0
- data/polyfills/package-lock.json +126 -142
- data/polyfills/package.json +1 -1
- data/sig/quickjs.rbs +22 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7b233c0e106654a50f4af274fef6a4dd77f0b53cc0a703473cda844da6ba3763
|
|
4
|
+
data.tar.gz: 50dfeaf56422ee74e8cb8711a58e1678c8141ac4bb7256a4d8861fa1a9415c93
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9f77f8b802055b1ec67ad9b4f45fb54cbdb1349a473457572c1c1b72115283bed9b99b15029e101256147ce0bbc72b64371fcbdd0ede6e8e9ba5e8d4a0bb0376
|
|
7
|
+
data.tar.gz: 9387e82c41c0242caa67c16a67b48a204c32512b116c5e4cc61d83e892bec053393facaf7ac2553642b876cacecb3622ddb37a846fd1eea959c21ea7df70b569
|
data/CLAUDE.md
CHANGED
|
@@ -55,6 +55,14 @@ Tests use minitest with `describe`/`it` blocks. Key test files:
|
|
|
55
55
|
- `test/quickjs_test.rb` — Main test suite (value conversion, errors, VM features, ESM imports, function definitions)
|
|
56
56
|
- `test/quickjs_polyfill_test.rb` — Intl polyfill tests
|
|
57
57
|
|
|
58
|
+
## Release Process
|
|
59
|
+
|
|
60
|
+
1. On `main`, run `bundle exec rake polyfills:build` — rebuilds polyfill bundles and syncs `polyfills/package.json` version to match the gem version
|
|
61
|
+
2. Bump `lib/quickjs/version.rb` and `Gemfile.lock` to the new version
|
|
62
|
+
3. Commit `lib/quickjs/version.rb`, `Gemfile.lock`, `polyfills/package.json`, `polyfills/package-lock.json` as `"prepare vX.Y.Z"` — do NOT push; `rake release` handles that
|
|
63
|
+
4. Run `bundle exec rake release` — tags, pushes to GitHub, and publishes to RubyGems (human step; do not run this as Claude)
|
|
64
|
+
5. Create a GitHub release via `gh release create` with notes following the pattern of previous releases
|
|
65
|
+
|
|
58
66
|
## Build Notes
|
|
59
67
|
|
|
60
68
|
- `extconf.rb` compiles with `-DNDEBUG` to avoid conflicts with Ruby 4.0 GC assertions
|
data/README.md
CHANGED
|
@@ -85,6 +85,21 @@ vm.eval_code('a.b = "d";')
|
|
|
85
85
|
vm.eval_code('a.b;') #=> "d"
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
+
#### `Quickjs::VM#compile`: 🚀 Cache parsed bundles as a `Quickjs::Runnable`
|
|
89
|
+
|
|
90
|
+
Parsing large JS bundles is the dominant cost of a fresh evaluation. `compile` parses once and returns a `Quickjs::Runnable` wrapping the serialized bytecode; `run(on:)` executes it on any VM of the same QuickJS build, skipping the parser. Useful when the same bundle is evaluated repeatedly across short-lived VMs (test environments, page-per-VM web emulators).
|
|
91
|
+
|
|
92
|
+
```rb
|
|
93
|
+
runnable = Quickjs::VM.new.compile(File.read('big_bundle.js'), filename: 'big_bundle.js')
|
|
94
|
+
|
|
95
|
+
vm = Quickjs::VM.new
|
|
96
|
+
runnable.run(on: vm) # use the given VM (no parse cost)
|
|
97
|
+
runnable.run # spin up a fresh VM with default options
|
|
98
|
+
runnable.run(on: { features: [::Quickjs::POLYFILL_INTL] }) # ad-hoc VM with options
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`Runnable#to_s` returns the underlying bytecode as a frozen ASCII-8BIT `String`, suitable for caching to memory or disk. `Quickjs::Runnable.new(bytecode_string)` reconstructs a `Runnable` from that blob — validation happens lazily at `run` time, so a corrupt or wrong-build blob surfaces as `Quickjs::RuntimeError` when executed. The bytecode format is tied to the QuickJS build, so include the gem version in your cache key if you persist across upgrades.
|
|
102
|
+
|
|
88
103
|
#### `Quickjs::VM#call`: ⚡ Call a JS function directly with Ruby arguments
|
|
89
104
|
|
|
90
105
|
```rb
|
|
@@ -129,6 +144,26 @@ vm.import('DefaultExport', from: File.read('exports.esm.js'))
|
|
|
129
144
|
vm.import('* as all', from: File.read('exports.esm.js'))
|
|
130
145
|
```
|
|
131
146
|
|
|
147
|
+
#### `Quickjs::VM#module_loader=`: 🧩 Resolve `import` specifiers from Ruby
|
|
148
|
+
|
|
149
|
+
By default, `import` specifiers that aren't already loaded fall through to QuickJS's filesystem loader. Set a `module_loader` Proc to resolve specifiers in-memory instead — useful when the source code lives in a database, an importmap, or a virtual filesystem.
|
|
150
|
+
|
|
151
|
+
```rb
|
|
152
|
+
vm = Quickjs::VM.new
|
|
153
|
+
modules = {
|
|
154
|
+
'a' => "import { b } from 'b'; export const a = () => `a-${b()}`;",
|
|
155
|
+
'b' => "export const b = () => 'b-result';"
|
|
156
|
+
}
|
|
157
|
+
vm.module_loader = ->(name) { modules[name] }
|
|
158
|
+
|
|
159
|
+
vm.import(['a'], filename: 'a')
|
|
160
|
+
vm.eval_code('a()') #=> 'a-b-result'
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
The Proc receives the (already normalized) module specifier and returns the module source as a `String`, or `nil` to signal "not found" (which raises `Quickjs::ReferenceError` on the JS side). Pass `nil` to clear a previously set loader.
|
|
164
|
+
|
|
165
|
+
When `module_loader=` is set, pass `filename:` to `import` instead of `from:` to resolve a named specifier directly through the loader — no inline bridge source needed.
|
|
166
|
+
|
|
132
167
|
#### `Quickjs::VM#define_function`: 💎 Define a global function for JS by Ruby
|
|
133
168
|
|
|
134
169
|
```rb
|
|
@@ -193,6 +228,37 @@ vm.eval_code('console.log("hello", 42)')
|
|
|
193
228
|
# log.raw #=> Array of raw Ruby values
|
|
194
229
|
```
|
|
195
230
|
|
|
231
|
+
#### Memory management: 🔍 Inspect and control VM memory
|
|
232
|
+
|
|
233
|
+
```rb
|
|
234
|
+
vm = Quickjs::VM.new
|
|
235
|
+
|
|
236
|
+
vm.memory_usage
|
|
237
|
+
# => { malloc_size: Integer, malloc_limit: Integer, memory_used_size: Integer,
|
|
238
|
+
# atom_count: Integer, str_count: Integer, obj_count: Integer,
|
|
239
|
+
# prop_count: Integer, shape_count: Integer,
|
|
240
|
+
# js_func_count: Integer, js_func_code_size: Integer,
|
|
241
|
+
# c_func_count: Integer, array_count: Integer }
|
|
242
|
+
|
|
243
|
+
vm.gc! # trigger a QuickJS GC cycle; returns nil
|
|
244
|
+
|
|
245
|
+
vm.memory_poisoned? #=> false (true once the VM has hit out-of-memory)
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
When the JS heap exhausts its memory limit, QuickJS enters a fragile state where further evaluation can segfault the process. `memory_poisoned?` flips to `true` after such an event, and subsequent `eval_code` / `call` calls raise `Quickjs::RuntimeError` immediately instead of risking a crash. Rescue it and recreate the VM.
|
|
249
|
+
|
|
250
|
+
```rb
|
|
251
|
+
vm = Quickjs::VM.new(memory_limit: 256 * 1024 * 1024)
|
|
252
|
+
|
|
253
|
+
begin
|
|
254
|
+
vm.eval_code(js)
|
|
255
|
+
rescue Quickjs::RuntimeError => e
|
|
256
|
+
raise unless vm.memory_poisoned?
|
|
257
|
+
vm = Quickjs::VM.new(memory_limit: 256 * 1024 * 1024)
|
|
258
|
+
retry
|
|
259
|
+
end
|
|
260
|
+
```
|
|
261
|
+
|
|
196
262
|
### Value Conversion
|
|
197
263
|
|
|
198
264
|
| JavaScript | | Ruby | Note |
|