quickjs 0.18.0 → 0.19.0.pre2
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 +5 -4
- data/README.md +21 -16
- data/Rakefile +0 -7
- data/ext/quickjsrb/extconf.rb +0 -5
- data/ext/quickjsrb/quickjsrb.c +38 -18
- data/ext/quickjsrb/quickjsrb.h +0 -4
- data/lib/quickjs/polyfills.rb +68 -0
- data/lib/quickjs/version.rb +1 -1
- data/lib/quickjs.rb +1 -0
- data/polyfills/package-lock.json +2 -85
- data/polyfills/package.json +2 -10
- data/polyfills/rolldown.config.mjs +0 -8
- data/sig/quickjs.rbs +2 -1
- metadata +2 -4
- data/ext/quickjsrb/vendor/polyfill-intl-en.min.js +0 -4
- data/polyfills/check-licenses.mjs +0 -72
- data/polyfills/src/intl-en.js +0 -15
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b2c190dd6bdb5b0e3ef78d98bd5d229110da724acad4803cd7bc67648b1031fa
|
|
4
|
+
data.tar.gz: ce726c43ede82fbfaf22cea2de7ad8c79d2993476ee6a84bf9c82bfe4f94be13
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 214339d3fba982acb1b8732938ee472bbb629a9977973f1d390ece197c34f2828beef7f7e9368fe9965ea4d3adb91a71aaeceeea2cbcda0f684d84ccb3387a42
|
|
7
|
+
data.tar.gz: 9b9a3fb5eb9120c3318086cdf1039be02e3c150ee4f18d7d489e53a85016e774cb06f95b4d2ea18fbb1292a8feddce58ab08d007cbc0227297e906600b472fc7
|
data/CLAUDE.md
CHANGED
|
@@ -14,7 +14,7 @@ bundle exec rake compile # Compile C extension only
|
|
|
14
14
|
bundle exec rake test # Run all tests
|
|
15
15
|
bundle exec ruby -Itest:lib test/quickjs_test.rb # Run single test file
|
|
16
16
|
bundle exec ruby -Itest:lib test/quickjs_test.rb -n test_name # Run single test
|
|
17
|
-
rake polyfills:build # Rebuild
|
|
17
|
+
rake polyfills:build # Rebuild polyfill bundles (Blob/File, Encoding, URL) — requires npm
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
QuickJS source lives as a git submodule under `ext/quickjsrb/quickjs/` — clone with `--recurse-submodules`.
|
|
@@ -43,17 +43,18 @@ QuickJS source lives as a git submodule under `ext/quickjsrb/quickjs/` — clone
|
|
|
43
43
|
**Feature flags** (passed to `VM.new` via `features:` array):
|
|
44
44
|
- `:feature_std`, `:feature_os` — QuickJS std/os modules
|
|
45
45
|
- `:feature_timeout` — setTimeout/setInterval via CRuby threads
|
|
46
|
-
- `:feature_polyfill_intl` — Intl API polyfill (DateTimeFormat, NumberFormat, PluralRules, Locale)
|
|
47
46
|
|
|
48
47
|
**Polyfills** (`polyfills/`):
|
|
49
|
-
-
|
|
48
|
+
- Hand-written W3C-spec polyfills (Blob/File, TextEncoder/Decoder, URL) bundled via rolldown into minified JS embedded as C source
|
|
50
49
|
- Polyfill version must match gem version (enforced during `rake release`)
|
|
50
|
+
- Intl APIs live in a separate companion gem ([`quickjs-polyfill-intl`](https://github.com/hmsk/quickjs-polyfill-intl)) registered via `Quickjs.register_polyfill`
|
|
51
51
|
|
|
52
52
|
## Testing
|
|
53
53
|
|
|
54
54
|
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
|
-
- `test/quickjs_polyfill_test.rb` —
|
|
56
|
+
- `test/quickjs_polyfill_test.rb` — Blob/File, Encoding, URL, Crypto polyfill tests
|
|
57
|
+
- `test/quickjs_register_polyfill_test.rb` — `Quickjs.register_polyfill` API tests
|
|
57
58
|
|
|
58
59
|
## Release Process
|
|
59
60
|
|
data/README.md
CHANGED
|
@@ -65,7 +65,6 @@ Quickjs.eval_code(code, features: [::Quickjs::MODULE_STD, ::Quickjs::POLYFILL_FI
|
|
|
65
65
|
| `MODULE_STD` | QuickJS [`std` module](https://bellard.org/quickjs/quickjs.html#std-module) |
|
|
66
66
|
| `MODULE_OS` | QuickJS [`os` module](https://bellard.org/quickjs/quickjs.html#os-module) |
|
|
67
67
|
| `FEATURE_TIMEOUT` | `setTimeout` / `setInterval` managed by CRuby |
|
|
68
|
-
| `POLYFILL_INTL` | Intl API (DateTimeFormat, NumberFormat, PluralRules, Locale) |
|
|
69
68
|
| `POLYFILL_FILE` | W3C File API (Blob and File) |
|
|
70
69
|
| `POLYFILL_ENCODING` | Encoding API (TextEncoder and TextDecoder) |
|
|
71
70
|
| `POLYFILL_URL` | URL API (URL and URLSearchParams) |
|
|
@@ -95,7 +94,7 @@ runnable = Quickjs::VM.new.compile(File.read('big_bundle.js'), filename: 'big_bu
|
|
|
95
94
|
vm = Quickjs::VM.new
|
|
96
95
|
runnable.run(on: vm) # use the given VM (no parse cost)
|
|
97
96
|
runnable.run # spin up a fresh VM with default options
|
|
98
|
-
runnable.run(on: { features: [::Quickjs::
|
|
97
|
+
runnable.run(on: { features: [::Quickjs::POLYFILL_FILE] }) # ad-hoc VM with options
|
|
99
98
|
```
|
|
100
99
|
|
|
101
100
|
`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.
|
|
@@ -346,7 +345,7 @@ By default, the `JSRuntime` / `JSContext` behind a `Quickjs::VM` lives until Rub
|
|
|
346
345
|
`dispose!` frees the runtime immediately and marks the VM unusable:
|
|
347
346
|
|
|
348
347
|
```rb
|
|
349
|
-
vm = Quickjs::VM.new(features: [::Quickjs::
|
|
348
|
+
vm = Quickjs::VM.new(features: [::Quickjs::POLYFILL_FILE])
|
|
350
349
|
vm.eval_code('…')
|
|
351
350
|
vm.dispose! # frees JSContext + JSRuntime now
|
|
352
351
|
vm.disposed? #=> true
|
|
@@ -392,6 +391,25 @@ Useful when porting JS that assumed V8's implicit-drain semantics — V8 (and th
|
|
|
392
391
|
| `File` | → | `Quickjs::File` — `.name`, `.last_modified` + Blob attrs | requires `POLYFILL_FILE` |
|
|
393
392
|
| `File` proxy | ← | `::File` | requires `POLYFILL_FILE`; applies to `define_function` return values |
|
|
394
393
|
|
|
394
|
+
## Extending: registering polyfills
|
|
395
|
+
|
|
396
|
+
`Quickjs.register_polyfill(name, source:, init: nil)` adds a polyfill to a process-wide registry. Any VM constructed with `name` in its `features:` list runs the registered bundle on top of the JS runtime. Companion gems use this hook to ship additional polyfills (e.g. `Intl.Collator`, `DisplayNames`) without bundling them into the main gem.
|
|
397
|
+
|
|
398
|
+
```rb
|
|
399
|
+
Quickjs.register_polyfill(
|
|
400
|
+
:polyfill_my_thing,
|
|
401
|
+
source: File.read('vendor/my-polyfill.min.js'),
|
|
402
|
+
init: 'globalThis.MyThing ||= {};' # optional, runs before the bundle
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
vm = Quickjs::VM.new(features: [:polyfill_my_thing])
|
|
406
|
+
vm.eval_code('MyThing.greet("hi")')
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
The first VM with a given polyfill pays the parse cost (the source is compiled to QuickJS bytecode on a disposable VM with a generous timeout); subsequent VMs reuse the cached bytecode. The polyfill body runs without consuming the user VM's `timeout_msec` budget — that's reserved for user code.
|
|
410
|
+
|
|
411
|
+
Intl APIs (Collator, DateTimeFormat, NumberFormat, PluralRules, Locale, etc.) live in a separate companion gem: [`quickjs-polyfill-intl`](https://github.com/hmsk/quickjs-polyfill-intl). Granular, dependency-aware, opt-in per API.
|
|
412
|
+
|
|
395
413
|
## Acknowledgements
|
|
396
414
|
|
|
397
415
|
- [@ursm](https://github.com/ursm) — for continuous contributions improving performance and developer experience
|
|
@@ -401,18 +419,5 @@ Useful when porting JS that assumed V8's implicit-drain semantics — V8 (and th
|
|
|
401
419
|
|
|
402
420
|
- `ext/quickjsrb/quickjs`
|
|
403
421
|
- [MIT License Copyright (c) 2017-2021 by Fabrice Bellard and Charlie Gordon](https://github.com/bellard/quickjs/blob/6e2e68fd0896957f92eb6c242a2e048c1ef3cae0/LICENSE).
|
|
404
|
-
- `ext/quickjsrb/vendor/polyfill-intl-en.min.js` ([bundled and minified from `polyfills/`](https://github.com/hmsk/quickjs.rb/tree/main/polyfills))
|
|
405
|
-
- MIT License Copyright (c) 2022 FormatJS
|
|
406
|
-
- [@formatjs/intl-supportedvaluesof](https://github.com/formatjs/formatjs/blob/main/packages/intl-supportedvaluesof/LICENSE.md)
|
|
407
|
-
- MIT License Copyright (c) 2023 FormatJS
|
|
408
|
-
- [@formatjs/intl-getcanonicallocales](https://github.com/formatjs/formatjs/blob/main/packages/intl-getcanonicallocales/LICENSE.md)
|
|
409
|
-
- [@formatjs/intl-locale](https://github.com/formatjs/formatjs/blob/main/packages/intl-locale/LICENSE.md)
|
|
410
|
-
- [@formatjs/intl-pluralrules](https://github.com/formatjs/formatjs/blob/main/packages/intl-pluralrules/LICENSE.md)
|
|
411
|
-
- [@formatjs/intl-numberformat](https://github.com/formatjs/formatjs/blob/main/packages/intl-numberformat/LICENSE.md)
|
|
412
|
-
- [@formatjs/intl-datetimeformat](https://github.com/formatjs/formatjs/blob/main/packages/intl-datetimeformat/LICENSE.md)
|
|
413
|
-
- [@formatjs/fast-memoize](https://github.com/formatjs/formatjs/blob/main/packages/fast-memoize/LICENSE.md)
|
|
414
|
-
- [@formatjs/intl-localematcher](https://github.com/formatjs/formatjs/blob/main/packages/intl-localematcher/LICENSE.md)
|
|
415
|
-
- MIT License Copyright (c) 2026 FormatJS
|
|
416
|
-
- [@formatjs/bigdecimal](https://github.com/formatjs/formatjs/blob/main/packages/bigdecimal/LICENSE.md)
|
|
417
422
|
|
|
418
423
|
Otherwise, [the MIT License, Copyright 2024 by Kengo Hamasaki](/LICENSE).
|
data/Rakefile
CHANGED
|
@@ -54,13 +54,6 @@ namespace :polyfills do
|
|
|
54
54
|
Rake::Task[:compile].invoke
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
-
desc 'Check licenses of bundled polyfill dependencies'
|
|
58
|
-
task :check_licenses do
|
|
59
|
-
Dir.chdir(File.expand_path('polyfills', __dir__)) do
|
|
60
|
-
sh 'npm run check-licenses'
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
|
|
64
57
|
namespace :version do
|
|
65
58
|
task :check do
|
|
66
59
|
check_polyfill_version! do |pkg_v, gem_v|
|
data/ext/quickjsrb/extconf.rb
CHANGED
|
@@ -12,7 +12,6 @@ $srcs = [
|
|
|
12
12
|
'cutils.c',
|
|
13
13
|
'quickjs.c',
|
|
14
14
|
'quickjs-libc.c',
|
|
15
|
-
'polyfill-intl-en.min.c',
|
|
16
15
|
'polyfill-file.min.c',
|
|
17
16
|
'polyfill-encoding.min.c',
|
|
18
17
|
'polyfill-url.min.c',
|
|
@@ -62,10 +61,6 @@ POLYFILL_OPTS=-fno-string-normalize -fno-typedarray -fno-typedarray -fno-eval -f
|
|
|
62
61
|
|
|
63
62
|
qjsc: ./qjsc.o $(QJS_LIB_OBJS)
|
|
64
63
|
$(CC) -g -o $@ $^ -lm -ldl -lpthread
|
|
65
|
-
polyfill-intl-en.min.js:
|
|
66
|
-
$(COPY) $(srcdir)/vendor/$@ $@
|
|
67
|
-
polyfill-intl-en.min.c: ./qjsc polyfill-intl-en.min.js
|
|
68
|
-
./qjsc $(POLYFILL_OPTS) -c -M polyfill/intl-en.so,intlen -m -o $@ polyfill-intl-en.min.js
|
|
69
64
|
polyfill-file.min.js:
|
|
70
65
|
$(COPY) $(srcdir)/vendor/$@ $@
|
|
71
66
|
polyfill-file.min.c: ./qjsc polyfill-file.min.js
|
data/ext/quickjsrb/quickjsrb.c
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
const char *featureStdId = "feature_std";
|
|
6
6
|
const char *featureOsId = "feature_os";
|
|
7
7
|
const char *featureTimeoutId = "feature_timeout";
|
|
8
|
-
const char *featurePolyfillIntlId = "feature_polyfill_intl";
|
|
9
8
|
const char *featurePolyfillFileId = "feature_polyfill_file";
|
|
10
9
|
const char *featurePolyfillEncodingId = "feature_polyfill_encoding";
|
|
11
10
|
const char *featurePolyfillUrlId = "feature_polyfill_url";
|
|
@@ -983,14 +982,13 @@ static JSValue js_console_error(JSContext *ctx, JSValueConst this, int argc, JSV
|
|
|
983
982
|
return js_quickjsrb_log(ctx, this, argc, argv, "error");
|
|
984
983
|
}
|
|
985
984
|
|
|
986
|
-
//
|
|
987
|
-
//
|
|
988
|
-
//
|
|
989
|
-
// pool in parallel with the main thread on multi-core hosts.
|
|
985
|
+
// Run polyfill bytecode load + eval without the GVL so a background
|
|
986
|
+
// warmer thread can populate a VM pool in parallel with the main thread
|
|
987
|
+
// on multi-core hosts.
|
|
990
988
|
//
|
|
991
|
-
// Releasing the GVL here is only safe because the polyfills are
|
|
992
|
-
//
|
|
993
|
-
//
|
|
989
|
+
// Releasing the GVL here is only safe because the bundled polyfills are
|
|
990
|
+
// pure JS (file / encoding / url) and no Ruby-bridged callbacks have
|
|
991
|
+
// been registered on globalThis yet at this point in vm_m_initialize.
|
|
994
992
|
// If the order ever changes — e.g. moving define_function setup ahead of
|
|
995
993
|
// the polyfill loads — the polyfill bytecode could re-enter Ruby without
|
|
996
994
|
// the GVL held. Keep host callback registration after polyfill loading.
|
|
@@ -1077,16 +1075,6 @@ static VALUE vm_m_initialize(int argc, VALUE *argv, VALUE r_self)
|
|
|
1077
1075
|
JS_NewCFunction(data->context, js_quickjsrb_set_timeout, "setTimeout", 2));
|
|
1078
1076
|
}
|
|
1079
1077
|
|
|
1080
|
-
if (RTEST(rb_funcall(r_features, rb_intern("include?"), 1, QUICKJSRB_SYM(featurePolyfillIntlId))))
|
|
1081
|
-
{
|
|
1082
|
-
const char *defineIntl = "Object.defineProperty(globalThis, 'Intl', { value:{} });\n";
|
|
1083
|
-
JSValue j_defineIntl = JS_Eval(data->context, defineIntl, strlen(defineIntl), vmInternalFilename, JS_EVAL_TYPE_GLOBAL);
|
|
1084
|
-
JS_FreeValue(data->context, j_defineIntl);
|
|
1085
|
-
|
|
1086
|
-
JSValue j_polyfillIntlResult = load_polyfill_bytecode(data->context, &qjsc_polyfill_intl_en_min, qjsc_polyfill_intl_en_min_size);
|
|
1087
|
-
JS_FreeValue(data->context, j_polyfillIntlResult);
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1090
1078
|
if (RTEST(rb_funcall(r_features, rb_intern("include?"), 1, QUICKJSRB_SYM(featurePolyfillFileId))))
|
|
1091
1079
|
{
|
|
1092
1080
|
JSValue j_polyfillFileResult = load_polyfill_bytecode(data->context, &qjsc_polyfill_file_min, qjsc_polyfill_file_min_size);
|
|
@@ -1317,6 +1305,37 @@ static VALUE vm_m_evalBytecode(VALUE r_self, VALUE r_bytecode)
|
|
|
1317
1305
|
return to_rb_return_value(data->context, j_returnedValue);
|
|
1318
1306
|
}
|
|
1319
1307
|
|
|
1308
|
+
// Loads pre-compiled polyfill bytecode without arming the eval timer.
|
|
1309
|
+
// The user's `timeout_msec` is a budget for *their* code; running a
|
|
1310
|
+
// multi-MB polyfill bundle (e.g. the companion `quickjs-polyfill-intl`
|
|
1311
|
+
// gem registered via Quickjs.register_polyfill) under that budget would
|
|
1312
|
+
// interrupt the load on tight defaults. Unlike load_polyfill_bytecode
|
|
1313
|
+
// above we hold the GVL through JS_ReadObject + JS_EvalFunction: the
|
|
1314
|
+
// bytecode buffer is a Ruby String, so releasing would let GC compact
|
|
1315
|
+
// the backing storage out from under us. The static-symbol path can
|
|
1316
|
+
// release safely; this path cannot.
|
|
1317
|
+
static VALUE vm_m_loadPolyfillBytecode(VALUE r_self, VALUE r_bytecode)
|
|
1318
|
+
{
|
|
1319
|
+
VMData *data;
|
|
1320
|
+
TypedData_Get_Struct(r_self, VMData, &vm_type, data);
|
|
1321
|
+
|
|
1322
|
+
check_disposed(data);
|
|
1323
|
+
StringValue(r_bytecode);
|
|
1324
|
+
|
|
1325
|
+
JSValue j_func = JS_ReadObject(data->context,
|
|
1326
|
+
(const uint8_t *)RSTRING_PTR(r_bytecode),
|
|
1327
|
+
(size_t)RSTRING_LEN(r_bytecode),
|
|
1328
|
+
JS_READ_OBJ_BYTECODE);
|
|
1329
|
+
if (JS_IsException(j_func))
|
|
1330
|
+
return to_rb_value(data->context, j_func); // raises
|
|
1331
|
+
|
|
1332
|
+
JSValue j_result = JS_EvalFunction(data->context, j_func); // frees j_func
|
|
1333
|
+
if (JS_IsException(j_result))
|
|
1334
|
+
return to_rb_value(data->context, j_result); // raises
|
|
1335
|
+
JS_FreeValue(data->context, j_result);
|
|
1336
|
+
return Qnil;
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1320
1339
|
static VALUE vm_m_defineGlobalFunction(int argc, VALUE *argv, VALUE r_self)
|
|
1321
1340
|
{
|
|
1322
1341
|
rb_need_block();
|
|
@@ -1728,6 +1747,7 @@ RUBY_FUNC_EXPORTED void Init_quickjsrb(void)
|
|
|
1728
1747
|
rb_define_method(r_class_vm, "eval_code", vm_m_evalCode, -1);
|
|
1729
1748
|
rb_define_private_method(r_class_vm, "_compile_to_bytecode", vm_m_compile, -1);
|
|
1730
1749
|
rb_define_private_method(r_class_vm, "_run_bytecode", vm_m_evalBytecode, 1);
|
|
1750
|
+
rb_define_private_method(r_class_vm, "_load_polyfill_bytecode", vm_m_loadPolyfillBytecode, 1);
|
|
1731
1751
|
rb_define_method(r_class_vm, "call", vm_m_callGlobalFunction, -1);
|
|
1732
1752
|
rb_define_method(r_class_vm, "define_function", vm_m_defineGlobalFunction, -1);
|
|
1733
1753
|
rb_define_method(r_class_vm, "import", vm_m_import, -1);
|
data/ext/quickjsrb/quickjsrb.h
CHANGED
|
@@ -14,8 +14,6 @@
|
|
|
14
14
|
#include <string.h>
|
|
15
15
|
#include <time.h>
|
|
16
16
|
|
|
17
|
-
extern const uint32_t qjsc_polyfill_intl_en_min_size;
|
|
18
|
-
extern const uint8_t qjsc_polyfill_intl_en_min;
|
|
19
17
|
extern const uint32_t qjsc_polyfill_file_min_size;
|
|
20
18
|
extern const uint8_t qjsc_polyfill_file_min;
|
|
21
19
|
extern const uint32_t qjsc_polyfill_encoding_min_size;
|
|
@@ -26,7 +24,6 @@ extern const uint8_t qjsc_polyfill_url_min;
|
|
|
26
24
|
extern const char *featureStdId;
|
|
27
25
|
extern const char *featureOsId;
|
|
28
26
|
extern const char *featureTimeoutId;
|
|
29
|
-
extern const char *featurePolyfillIntlId;
|
|
30
27
|
extern const char *featurePolyfillFileId;
|
|
31
28
|
extern const char *featurePolyfillEncodingId;
|
|
32
29
|
extern const char *featurePolyfillUrlId;
|
|
@@ -214,7 +211,6 @@ static void r_define_constants(VALUE r_parent_class)
|
|
|
214
211
|
rb_define_const(r_parent_class, "MODULE_STD", QUICKJSRB_SYM(featureStdId));
|
|
215
212
|
rb_define_const(r_parent_class, "MODULE_OS", QUICKJSRB_SYM(featureOsId));
|
|
216
213
|
rb_define_const(r_parent_class, "FEATURE_TIMEOUT", QUICKJSRB_SYM(featureTimeoutId));
|
|
217
|
-
rb_define_const(r_parent_class, "POLYFILL_INTL", QUICKJSRB_SYM(featurePolyfillIntlId));
|
|
218
214
|
rb_define_const(r_parent_class, "POLYFILL_FILE", QUICKJSRB_SYM(featurePolyfillFileId));
|
|
219
215
|
rb_define_const(r_parent_class, "POLYFILL_ENCODING", QUICKJSRB_SYM(featurePolyfillEncodingId));
|
|
220
216
|
rb_define_const(r_parent_class, "POLYFILL_URL", QUICKJSRB_SYM(featurePolyfillUrlId));
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Quickjs
|
|
4
|
+
# Process-wide registry; entries are lazily compiled to bytecode on
|
|
5
|
+
# first use and the bytecode is reused across VMs.
|
|
6
|
+
@_polyfills = {}
|
|
7
|
+
|
|
8
|
+
# `source:` accepts either a `String` (eager) or a `Proc` returning one
|
|
9
|
+
# (lazy). The lazy form lets a companion gem call `register_polyfill`
|
|
10
|
+
# at require time without paying the file-read cost unless a VM
|
|
11
|
+
# actually opts into the feature.
|
|
12
|
+
def self.register_polyfill(name, source:, init: nil)
|
|
13
|
+
raise ::TypeError, "name must be a Symbol, got #{name.class}" unless name.is_a?(Symbol)
|
|
14
|
+
raise ::TypeError, "source: must be a String or Proc, got #{source.class}" unless source.is_a?(String) || source.is_a?(Proc)
|
|
15
|
+
raise ::TypeError, "init: must be a String or nil, got #{init.class}" unless init.nil? || init.is_a?(String)
|
|
16
|
+
|
|
17
|
+
@_polyfills[name] = {source: source, init: init&.freeze, bytecode: nil}
|
|
18
|
+
nil
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self._polyfill_for(name)
|
|
22
|
+
@_polyfills[name]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self._unregister_polyfill(name)
|
|
26
|
+
@_polyfills.delete(name)
|
|
27
|
+
nil
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self._apply_registered_polyfills(vm, features)
|
|
31
|
+
features.each do |feature|
|
|
32
|
+
next unless (entry = @_polyfills[feature])
|
|
33
|
+
# `||=` isn't atomic — `_precompile_polyfill` releases the GVL inside
|
|
34
|
+
# `Quickjs.compile`, so two threads racing to construct VMs with the
|
|
35
|
+
# same polyfill can both see nil and both compile. The bytecode write
|
|
36
|
+
# is harmless because both threads produce identical bytes; the only
|
|
37
|
+
# observable cost is wasted compile work, and (for `source: Proc`) a
|
|
38
|
+
# second Proc invocation — so register Procs that are safe to call
|
|
39
|
+
# more than once.
|
|
40
|
+
entry[:bytecode] ||= _precompile_polyfill(entry, feature)
|
|
41
|
+
vm.send(:_load_polyfill_bytecode, entry[:bytecode])
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Compiled once per process per polyfill on a disposable VM whose
|
|
46
|
+
# generous timeout covers parsing multi-MB bundles (e.g. the companion
|
|
47
|
+
# `quickjs-polyfill-intl` gem's FormatJS bundles run a couple MB). The
|
|
48
|
+
# user's per-VM `timeout_msec` is for their own JS — it would otherwise
|
|
49
|
+
# interrupt our infrastructure on tight defaults. `features: []` skips
|
|
50
|
+
# applying any registered polyfills to the temp VM (no recursion / no
|
|
51
|
+
# wasted polyfill loads).
|
|
52
|
+
def self._precompile_polyfill(entry, feature)
|
|
53
|
+
source = entry[:source]
|
|
54
|
+
source = source.call if source.is_a?(Proc)
|
|
55
|
+
combined = entry[:init] ? "#{entry[:init]}\n#{source}" : source
|
|
56
|
+
Quickjs.compile(combined, filename: feature.to_s, timeout_msec: 60_000, features: []).to_s
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
module PolyfillLoader
|
|
60
|
+
def initialize(features: [], **opts)
|
|
61
|
+
super
|
|
62
|
+
Quickjs._apply_registered_polyfills(self, features)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Guard against double-prepend if this file is reloaded (e.g. Rails dev).
|
|
67
|
+
VM.prepend(PolyfillLoader) unless VM.ancestors.include?(PolyfillLoader)
|
|
68
|
+
end
|
data/lib/quickjs/version.rb
CHANGED
data/lib/quickjs.rb
CHANGED
data/polyfills/package-lock.json
CHANGED
|
@@ -1,19 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "quickjs-rb-polyfills",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0.pre2",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "quickjs-rb-polyfills",
|
|
9
|
-
"version": "0.
|
|
10
|
-
"dependencies": {
|
|
11
|
-
"@formatjs/intl-datetimeformat": "^7.3.1",
|
|
12
|
-
"@formatjs/intl-getcanonicallocales": "^3.2.2",
|
|
13
|
-
"@formatjs/intl-locale": "^5.3.1",
|
|
14
|
-
"@formatjs/intl-numberformat": "^9.3.1",
|
|
15
|
-
"@formatjs/intl-pluralrules": "^6.3.1"
|
|
16
|
-
},
|
|
9
|
+
"version": "0.19.0.pre2",
|
|
17
10
|
"devDependencies": {
|
|
18
11
|
"rolldown": "^1.0.0-rc.14"
|
|
19
12
|
}
|
|
@@ -52,82 +45,6 @@
|
|
|
52
45
|
"tslib": "^2.4.0"
|
|
53
46
|
}
|
|
54
47
|
},
|
|
55
|
-
"node_modules/@formatjs/bigdecimal": {
|
|
56
|
-
"version": "0.2.5",
|
|
57
|
-
"resolved": "https://registry.npmjs.org/@formatjs/bigdecimal/-/bigdecimal-0.2.5.tgz",
|
|
58
|
-
"integrity": "sha512-2XTKNrZRaCUyXK2976wfutqxMBuPO/S/zbJnQdysLI2Zy5mWPVNVEkE6tsTcSVWSE7DgO88t8DtBy+uf3I8bxg==",
|
|
59
|
-
"license": "MIT"
|
|
60
|
-
},
|
|
61
|
-
"node_modules/@formatjs/fast-memoize": {
|
|
62
|
-
"version": "3.1.5",
|
|
63
|
-
"resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-3.1.5.tgz",
|
|
64
|
-
"integrity": "sha512-KLi3fan6WnCHmigd9pmEEN8Hid0v4wiFBW576M/d07KMWYecf1CvyMI3n34vCmHT4AoVqG2n702kiHbXjzZX2A==",
|
|
65
|
-
"license": "MIT"
|
|
66
|
-
},
|
|
67
|
-
"node_modules/@formatjs/intl-datetimeformat": {
|
|
68
|
-
"version": "7.4.6",
|
|
69
|
-
"resolved": "https://registry.npmjs.org/@formatjs/intl-datetimeformat/-/intl-datetimeformat-7.4.6.tgz",
|
|
70
|
-
"integrity": "sha512-2g2NXRc1Q4DpG4lls2ImDCWpEJyMJLJBbt08w8Gk6ACr6Y00jvKwMS9MWVNOWNoy3UQp1oGANC5/RDCBqiWhfg==",
|
|
71
|
-
"license": "MIT",
|
|
72
|
-
"dependencies": {
|
|
73
|
-
"@formatjs/bigdecimal": "0.2.5",
|
|
74
|
-
"@formatjs/intl-localematcher": "0.8.8"
|
|
75
|
-
}
|
|
76
|
-
},
|
|
77
|
-
"node_modules/@formatjs/intl-getcanonicallocales": {
|
|
78
|
-
"version": "3.2.9",
|
|
79
|
-
"resolved": "https://registry.npmjs.org/@formatjs/intl-getcanonicallocales/-/intl-getcanonicallocales-3.2.9.tgz",
|
|
80
|
-
"integrity": "sha512-b1dLzhFSeccG5X1PY2KodVFRGd0p6tBPc7pRBJaW6A7+fMeZnwHOAJLkMsGNDyvM9XailkkstE4jzmNa1GVh5w==",
|
|
81
|
-
"license": "MIT"
|
|
82
|
-
},
|
|
83
|
-
"node_modules/@formatjs/intl-locale": {
|
|
84
|
-
"version": "5.3.8",
|
|
85
|
-
"resolved": "https://registry.npmjs.org/@formatjs/intl-locale/-/intl-locale-5.3.8.tgz",
|
|
86
|
-
"integrity": "sha512-pJgqQlg4zFoyCiVMXlKPBWpY7fAxvsUiShNmB/l8BW2ZwmFMefMWCrDzgocSH9WR+Z3pkihfjh1XbalCZhXc1Q==",
|
|
87
|
-
"license": "MIT",
|
|
88
|
-
"dependencies": {
|
|
89
|
-
"@formatjs/intl-getcanonicallocales": "3.2.9",
|
|
90
|
-
"@formatjs/intl-supportedvaluesof": "2.3.7"
|
|
91
|
-
}
|
|
92
|
-
},
|
|
93
|
-
"node_modules/@formatjs/intl-localematcher": {
|
|
94
|
-
"version": "0.8.8",
|
|
95
|
-
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.8.8.tgz",
|
|
96
|
-
"integrity": "sha512-pBr2hVKWvkHVnfXegW+53NT9U2uaVQCc+EgzLPCCwXqBA3nvM5fPbK9IcJlNjV+NMKGyZ2F3ZSG78iGdxAAqbA==",
|
|
97
|
-
"license": "MIT",
|
|
98
|
-
"dependencies": {
|
|
99
|
-
"@formatjs/fast-memoize": "3.1.5"
|
|
100
|
-
}
|
|
101
|
-
},
|
|
102
|
-
"node_modules/@formatjs/intl-numberformat": {
|
|
103
|
-
"version": "9.3.9",
|
|
104
|
-
"resolved": "https://registry.npmjs.org/@formatjs/intl-numberformat/-/intl-numberformat-9.3.9.tgz",
|
|
105
|
-
"integrity": "sha512-VV0fPzmoA7j4NCdZ3M7xQY0iRDOtKvM2+Wi1/wLN41yhqjDV3BAIPbAIyAa4ZuyyQHWNDY3J+TciBQl66o1qhg==",
|
|
106
|
-
"license": "MIT",
|
|
107
|
-
"dependencies": {
|
|
108
|
-
"@formatjs/bigdecimal": "0.2.5",
|
|
109
|
-
"@formatjs/intl-localematcher": "0.8.8"
|
|
110
|
-
}
|
|
111
|
-
},
|
|
112
|
-
"node_modules/@formatjs/intl-pluralrules": {
|
|
113
|
-
"version": "6.3.8",
|
|
114
|
-
"resolved": "https://registry.npmjs.org/@formatjs/intl-pluralrules/-/intl-pluralrules-6.3.8.tgz",
|
|
115
|
-
"integrity": "sha512-v51wZCnKNoFkQH8WhjdY6MCzySk80ppZ/qExBCdm0eiM/xMoIE0VgiTKcv0NOnE7V8+yML68LLnjZmxiPq1aZw==",
|
|
116
|
-
"license": "MIT",
|
|
117
|
-
"dependencies": {
|
|
118
|
-
"@formatjs/bigdecimal": "0.2.5",
|
|
119
|
-
"@formatjs/intl-localematcher": "0.8.8"
|
|
120
|
-
}
|
|
121
|
-
},
|
|
122
|
-
"node_modules/@formatjs/intl-supportedvaluesof": {
|
|
123
|
-
"version": "2.3.7",
|
|
124
|
-
"resolved": "https://registry.npmjs.org/@formatjs/intl-supportedvaluesof/-/intl-supportedvaluesof-2.3.7.tgz",
|
|
125
|
-
"integrity": "sha512-ZTXEKijV5H08z19odngZPsXlmGndfukHuVnjd9qyER77XSk/yJwDMiSjoJYY5C5WxFs2UN4s/EMtu75e6Tp3ug==",
|
|
126
|
-
"license": "MIT",
|
|
127
|
-
"dependencies": {
|
|
128
|
-
"@formatjs/fast-memoize": "3.1.5"
|
|
129
|
-
}
|
|
130
|
-
},
|
|
131
48
|
"node_modules/@napi-rs/wasm-runtime": {
|
|
132
49
|
"version": "1.1.4",
|
|
133
50
|
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz",
|
data/polyfills/package.json
CHANGED
|
@@ -1,19 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "quickjs-rb-polyfills",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0.pre2",
|
|
4
4
|
"private": true,
|
|
5
5
|
"scripts": {
|
|
6
|
-
"build": "rolldown -c rolldown.config.mjs"
|
|
7
|
-
"check-licenses": "node check-licenses.mjs"
|
|
6
|
+
"build": "rolldown -c rolldown.config.mjs"
|
|
8
7
|
},
|
|
9
8
|
"devDependencies": {
|
|
10
9
|
"rolldown": "^1.0.0-rc.14"
|
|
11
|
-
},
|
|
12
|
-
"dependencies": {
|
|
13
|
-
"@formatjs/intl-getcanonicallocales": "^3.2.2",
|
|
14
|
-
"@formatjs/intl-locale": "^5.3.1",
|
|
15
|
-
"@formatjs/intl-pluralrules": "^6.3.1",
|
|
16
|
-
"@formatjs/intl-numberformat": "^9.3.1",
|
|
17
|
-
"@formatjs/intl-datetimeformat": "^7.3.1"
|
|
18
10
|
}
|
|
19
11
|
}
|
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
import { defineConfig } from "rolldown";
|
|
2
2
|
|
|
3
3
|
export default [
|
|
4
|
-
defineConfig({
|
|
5
|
-
input: "src/intl-en.js",
|
|
6
|
-
output: {
|
|
7
|
-
file: "../ext/quickjsrb/vendor/polyfill-intl-en.min.js",
|
|
8
|
-
format: "iife",
|
|
9
|
-
minify: true,
|
|
10
|
-
},
|
|
11
|
-
}),
|
|
12
4
|
defineConfig({
|
|
13
5
|
input: "src/file.js",
|
|
14
6
|
output: {
|
data/sig/quickjs.rbs
CHANGED
|
@@ -4,7 +4,6 @@ module Quickjs
|
|
|
4
4
|
MODULE_STD: Symbol
|
|
5
5
|
MODULE_OS: Symbol
|
|
6
6
|
FEATURE_TIMEOUT: Symbol
|
|
7
|
-
POLYFILL_INTL: Symbol
|
|
8
7
|
POLYFILL_FILE: Symbol
|
|
9
8
|
POLYFILL_HTML_BASE64: Symbol
|
|
10
9
|
POLYFILL_ENCODING: Symbol
|
|
@@ -14,6 +13,8 @@ module Quickjs
|
|
|
14
13
|
def self.eval_code: (String code, ?Hash[Symbol, untyped] overwrite_opts) -> untyped
|
|
15
14
|
def self.compile: (String source, ?filename: String, **untyped) -> Quickjs::Runnable
|
|
16
15
|
|
|
16
|
+
def self.register_polyfill: (Symbol name, source: String | ^() -> String, ?init: String?) -> nil
|
|
17
|
+
|
|
17
18
|
class Value
|
|
18
19
|
UNDEFINED: Symbol
|
|
19
20
|
NAN: Symbol
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: quickjs
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.19.0.pre2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- hmsk
|
|
@@ -86,21 +86,19 @@ files:
|
|
|
86
86
|
- ext/quickjsrb/quickjsrb_file.h
|
|
87
87
|
- ext/quickjsrb/vendor/polyfill-encoding.min.js
|
|
88
88
|
- ext/quickjsrb/vendor/polyfill-file.min.js
|
|
89
|
-
- ext/quickjsrb/vendor/polyfill-intl-en.min.js
|
|
90
89
|
- ext/quickjsrb/vendor/polyfill-url.min.js
|
|
91
90
|
- lib/quickjs.rb
|
|
92
91
|
- lib/quickjs/crypto_key.rb
|
|
93
92
|
- lib/quickjs/function.rb
|
|
93
|
+
- lib/quickjs/polyfills.rb
|
|
94
94
|
- lib/quickjs/runnable.rb
|
|
95
95
|
- lib/quickjs/subtle_crypto.rb
|
|
96
96
|
- lib/quickjs/version.rb
|
|
97
|
-
- polyfills/check-licenses.mjs
|
|
98
97
|
- polyfills/package-lock.json
|
|
99
98
|
- polyfills/package.json
|
|
100
99
|
- polyfills/rolldown.config.mjs
|
|
101
100
|
- polyfills/src/encoding.js
|
|
102
101
|
- polyfills/src/file.js
|
|
103
|
-
- polyfills/src/intl-en.js
|
|
104
102
|
- polyfills/src/url.js
|
|
105
103
|
- sig/quickjs.rbs
|
|
106
104
|
homepage: https://github.com/hmsk/quickjs.rb
|