mini_racer-csim 0.21.1.0 → 0.21.1.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8c0d4cbd8123f6177c8411ad1d9e32ff4e8a1a0de0515eb5f030672dd11f3f44
4
- data.tar.gz: faba5c80bf42ca1638d37c20f5f7849cd406e10fe5f00bc926fd696824237cdf
3
+ metadata.gz: 8674864924ab7e282736f327fc6bda583750e6cd3e61df57d4dd09005966812b
4
+ data.tar.gz: 6e98c37b07f580c5b9ad2d5af887ec0a420ec95b2c4f58f929eb8aae031ca5ac
5
5
  SHA512:
6
- metadata.gz: c0d8f9dcc469cd52b999e31e2c677a205aabe343804b70990a7978b2302d2df505c595a38b5de1db16e70358fabea42c950f2ff3b2509f9a94f87f178b0df229
7
- data.tar.gz: 5dc7c4ff2147e0a8815eb63d024350db82b2eaade805bb8c5d4834deff7a950a8f44b73cb9d3943f948ce1fda51c82aba464f2384a4e333c5bcfbabad5f6ca6c
6
+ metadata.gz: 72b2991b309086e5e089a1bb9e3a07517a417c6435444e457d1b5ca42ab095290b2c13f1559ac0c8c384794b240acaf063ca0691724dc9bd3647677bb8c88261
7
+ data.tar.gz: 4b7ffa6b04dcca1c2771ef3993095a36210638a120b341eddde6e3615d9bccd3845d98c92271f58cd3d9671ba364442689df0792179963c8efab9815046e30db
data/CHANGELOG CHANGED
@@ -1,4 +1,11 @@
1
- - (Unreleased)
1
+ - 0.21.1.1 - 07-06-2026
2
+ - Drop the TruffleRuby backend: mini_racer-csim is now a hard fork targeting CRuby/V8 only (the GraalJS/TruffleRuby shims and their tests are removed)
3
+ - Add per-frame realms via `Context#create_realm`, returning a `MiniRacer::Realm` (multiple V8 realms in one isolate, browser-iframe semantics) with `Realm#eval` / `call` / `attach` / `dispose` / `disposed?`
4
+ - all realms share one security token so they can reach each other's globals (same-origin iframe model)
5
+ - `__mr_realmGlobal(id)` returns a realm's live `globalThis` cross-realm; `__mr_realmOf(fn)` reports the realm a callback was created in ([[Realm]]) for WebIDL-style error attribution
6
+ - unhandled promise rejections are tracked and delivered per realm via `globalThis.__mr_emitUnhandledRejection(reason, promise)`; the queue is purged when a realm is disposed or reset so a stale rejection never fires against a fresh realm
7
+
8
+ - 0.21.1.0 - 06-06-2026
2
9
  - Add `Context#perform_microtask_checkpoint` to synchronously drain the V8 microtask queue, useful for spec-compliant `dispatchEvent` sequencing inside Ruby callbacks
3
10
  - Expose V8 ScriptCompiler::CachedData via Context#compile / MiniRacer::Script (#411)
4
11
  - script = ctx.compile(src, filename:, cached_data:, produce_cache:) → Script handle
data/README.md CHANGED
@@ -31,7 +31,7 @@ The fork is periodically rebased on upstream `mini_racer` to pick up V8 / `libv8
31
31
 
32
32
  ## Supported Ruby Versions & Troubleshooting
33
33
 
34
- MiniRacer only supports non-EOL versions of Ruby. See [Ruby Maintenance Branches](https://www.ruby-lang.org/en/downloads/branches/) for the list of non-EOL Rubies. If you require support for older versions of Ruby install an older version of the gem. [TruffleRuby](https://github.com/oracle/truffleruby) is also supported.
34
+ MiniRacer only supports non-EOL versions of Ruby. See [Ruby Maintenance Branches](https://www.ruby-lang.org/en/downloads/branches/) for the list of non-EOL Rubies. If you require support for older versions of Ruby install an older version of the gem. (The `mini_racer-csim` fork is CRuby/V8 only — the TruffleRuby backend that upstream supports has been removed.)
35
35
 
36
36
  MiniRacer **does not support**
37
37
 
@@ -228,10 +228,6 @@ Notes:
228
228
  reuse still works under `:single_threaded`. If you need both cross-process
229
229
  reuse and `:single_threaded` (e.g. for fork-safety reasons), disable
230
230
  `:single_threaded` for the path that produces / consumes the cache.
231
- - On TruffleRuby, `Script` is implemented as source replay (GraalJS has no
232
- equivalent per-script bytecode cache reachable from `Polyglot::InnerContext`),
233
- so `cached_data` and `produce_cache` are silently ignored and `cached_data`
234
- always returns `nil`, and `MiniRacer::V8_CACHED_DATA_VERSION_TAG` is `0`.
235
231
 
236
232
  ### Fork Safety
237
233
 
@@ -501,7 +497,7 @@ context.eval("log")
501
497
  # => ["before", "microtask", "after"]
502
498
  ```
503
499
 
504
- `host_namespace:` accepts a String (the global name to use — it must be a valid JavaScript identifier), `true` (the default name `"MiniRacer"`), or `nil`/`false` (the default — inject nothing). The namespace object is defined non-enumerable so it does not appear in `Object.keys(globalThis)`, while its methods are ordinary properties discoverable via `Object.keys(MiniRacer)`. Like `perform_microtask_checkpoint`, `drainMicrotasks()` is a no-op while a microtask checkpoint is already in progress, and it lets watchdog/out-of-memory termination propagate to the enclosing `eval`/`call`. (The host namespace is V8-only; it is not installed on the TruffleRuby backend.)
500
+ `host_namespace:` accepts a String (the global name to use — it must be a valid JavaScript identifier), `true` (the default name `"MiniRacer"`), or `nil`/`false` (the default — inject nothing). The namespace object is defined non-enumerable so it does not appear in `Object.keys(globalThis)`, while its methods are ordinary properties discoverable via `Object.keys(MiniRacer)`. Like `perform_microtask_checkpoint`, `drainMicrotasks()` is a no-op while a microtask checkpoint is already in progress, and it lets watchdog/out-of-memory termination propagate to the enclosing `eval`/`call`.
505
501
 
506
502
  ### ES modules
507
503
 
@@ -570,9 +566,6 @@ Notes:
570
566
  accumulate handles until `Context#dispose` clears them.
571
567
  - Top-level await is not yet supported; `evaluate` raises if the
572
568
  module's evaluation promise stays pending after the microtask drain.
573
- - On TruffleRuby, `Context#compile_module` raises `NotImplementedError`
574
- — GraalJS has its own module-loading mechanism that doesn't map onto
575
- this handle-based API. PRs to bridge are welcome.
576
569
 
577
570
  ## Performance
578
571
 
@@ -2,11 +2,6 @@ require 'mkmf'
2
2
 
3
3
  $srcs = ["mini_racer_extension.c", "mini_racer_v8.cc"]
4
4
 
5
- if RUBY_ENGINE == "truffleruby"
6
- File.write("Makefile", dummy_makefile($srcdir).join(""))
7
- return
8
- end
9
-
10
5
  require_relative '../../lib/mini_racer/version'
11
6
  gem 'libv8-node', MiniRacer::LIBV8_NODE_VERSION
12
7
  require 'libv8-node'
@@ -204,6 +204,15 @@ typedef struct Script {
204
204
  int disposed;
205
205
  } Script;
206
206
 
207
+ // A per-frame realm (extra v8::Context in the same isolate); see
208
+ // Context#create_realm. Like Script/Module, the C++ realm is freed eagerly via
209
+ // Realm#dispose or at Context teardown — realm_free can't send a dispose RPC.
210
+ typedef struct Realm {
211
+ VALUE context; // parent Context VALUE (kept alive via mark)
212
+ int32_t id; // realm id (0 = main, never wrapped as a Realm)
213
+ int disposed;
214
+ } Realm;
215
+
207
216
  static void context_destroy(Context *c);
208
217
  static void context_free(void *arg);
209
218
  static void context_mark(void *arg);
@@ -257,6 +266,19 @@ static const rb_data_type_t module_type = {
257
266
  },
258
267
  };
259
268
 
269
+ static void realm_free(void *arg);
270
+ static void realm_mark(void *arg);
271
+ static size_t realm_size(const void *arg);
272
+
273
+ static const rb_data_type_t realm_type = {
274
+ .wrap_struct_name = "mini_racer/realm",
275
+ .function = {
276
+ .dfree = realm_free,
277
+ .dmark = realm_mark,
278
+ .dsize = realm_size,
279
+ },
280
+ };
281
+
260
282
  static VALUE platform_init_error;
261
283
  static VALUE context_disposed_error;
262
284
  static VALUE parse_error;
@@ -270,6 +292,7 @@ static VALUE context_class;
270
292
  static VALUE snapshot_class;
271
293
  static VALUE script_class;
272
294
  static VALUE module_class;
295
+ static VALUE realm_class;
273
296
  static VALUE date_time_class;
274
297
  static VALUE binary_class;
275
298
  static VALUE js_function_class;
@@ -885,6 +908,7 @@ static void dispatch1(Context *c, const uint8_t *p, size_t n)
885
908
  assert(n > 0);
886
909
  switch (*p) {
887
910
  case 'A': return v8_attach(c->pst, p+1, n-1);
911
+ case 'B': return v8_create_realm(c->pst); // (B)uild realm
888
912
  case 'C': return v8_timedwait(c, p+1, n-1, v8_call);
889
913
  case 'D': return v8_dispose_script(c->pst, p+1, n-1);
890
914
  case 'E': return v8_timedwait(c, p+1, n-1, v8_eval);
@@ -903,6 +927,8 @@ static void dispatch1(Context *c, const uint8_t *p, size_t n)
903
927
  case 'U': return v8_module_status(c->pst, p+1, n-1); // (U) module status — non-blocking
904
928
  case 'V': return v8_timedwait(c, p+1, n-1, v8_evaluate_module); // e(V)aluate
905
929
  case 'W': return v8_warmup(c->pst, p+1, n-1);
930
+ case 'X': return v8_dispose_realm(c->pst, p+1, n-1); // e(X)punge realm
931
+ case '@': return v8_timedwait(c, p+1, n-1, v8_realm_dispatch); // realm-scoped op
906
932
  case 'Z': return v8_dispose_module(c->pst, p+1, n-1); // (Z) dispose module handle
907
933
  case 'L':
908
934
  b = 0;
@@ -1896,6 +1922,197 @@ static VALUE context_reset_realm(VALUE self)
1896
1922
  return Qnil;
1897
1923
  }
1898
1924
 
1925
+ // -- MiniRacer::Realm (per-frame realm = extra v8::Context in this isolate) --
1926
+
1927
+ static void realm_free(void *arg)
1928
+ {
1929
+ // Like module_free: no dispose RPC from a finalizer. State::~State()
1930
+ // tears every realm down at Context teardown; use Realm#dispose to free
1931
+ // a realm eagerly mid-lifetime.
1932
+ ruby_xfree(arg);
1933
+ }
1934
+
1935
+ static void realm_mark(void *arg)
1936
+ {
1937
+ Realm *r = arg;
1938
+ rb_gc_mark(r->context);
1939
+ }
1940
+
1941
+ static size_t realm_size(const void *arg)
1942
+ {
1943
+ (void)arg;
1944
+ return sizeof(Realm);
1945
+ }
1946
+
1947
+ static VALUE realm_alloc(VALUE klass)
1948
+ {
1949
+ Realm *r;
1950
+ return TypedData_Make_Struct(klass, Realm, &realm_type, r);
1951
+ }
1952
+
1953
+ static VALUE realm_initialize(int argc, VALUE *argv, VALUE self)
1954
+ {
1955
+ (void)argc; (void)argv; (void)self;
1956
+ rb_raise(runtime_error, "MiniRacer::Realm must be created via Context#create_realm");
1957
+ }
1958
+
1959
+ // Resolve the parent Context, raising if the realm or its context is disposed.
1960
+ static Context *realm_live_context(VALUE self, Realm **out)
1961
+ {
1962
+ Realm *r;
1963
+ Context *c;
1964
+
1965
+ TypedData_Get_Struct(self, Realm, &realm_type, r);
1966
+ if (r->disposed)
1967
+ rb_raise(runtime_error, "disposed realm");
1968
+ TypedData_Get_Struct(r->context, Context, &context_type, c);
1969
+ if (atomic_load(&c->quit))
1970
+ rb_raise(context_disposed_error, "disposed context");
1971
+ *out = r;
1972
+ return c;
1973
+ }
1974
+
1975
+ // Wrap an inner request (built with ser_init1(op)...) as a realm-scoped request
1976
+ // '@' + id(4 raw bytes) + inner, and dispatch it. Frees |inner|; the reply has
1977
+ // the same shape as the inner op's reply.
1978
+ static VALUE realm_rendezvous(Context *c, int32_t id, Ser *inner)
1979
+ {
1980
+ Buf req;
1981
+ buf_init(&req);
1982
+ buf_putc(&req, '@');
1983
+ buf_put(&req, &id, sizeof(id)); // same process => host endianness ok
1984
+ buf_put(&req, inner->b.buf, inner->b.len);
1985
+ ser_reset(inner); // release the inner buffer
1986
+ return rendezvous(c, &req); // takes ownership of |req|
1987
+ }
1988
+
1989
+ static VALUE realm_id(VALUE self)
1990
+ {
1991
+ Realm *r;
1992
+ TypedData_Get_Struct(self, Realm, &realm_type, r);
1993
+ return INT2NUM(r->id);
1994
+ }
1995
+
1996
+ static VALUE realm_disposed_p(VALUE self)
1997
+ {
1998
+ Realm *r;
1999
+ TypedData_Get_Struct(self, Realm, &realm_type, r);
2000
+ return r->disposed ? Qtrue : Qfalse;
2001
+ }
2002
+
2003
+ static VALUE realm_eval(int argc, VALUE *argv, VALUE self)
2004
+ {
2005
+ VALUE a, e, source, filename, kwargs;
2006
+ Realm *r;
2007
+ Context *c = realm_live_context(self, &r);
2008
+ Ser s;
2009
+
2010
+ filename = Qnil;
2011
+ rb_scan_args(argc, argv, "1:", &source, &kwargs);
2012
+ Check_Type(source, T_STRING);
2013
+ if (!NIL_P(kwargs))
2014
+ filename = rb_hash_aref(kwargs, rb_id2sym(rb_intern("filename")));
2015
+ if (NIL_P(filename))
2016
+ filename = rb_str_new_cstr("<eval>");
2017
+ Check_Type(filename, T_STRING);
2018
+ ser_init1(&s, 'E');
2019
+ ser_array_begin(&s, 2);
2020
+ add_string(&s, filename);
2021
+ add_string(&s, source);
2022
+ ser_array_end(&s, 2);
2023
+ a = realm_rendezvous(c, r->id, &s);
2024
+ e = rb_ary_pop(a);
2025
+ handle_exception(e);
2026
+ return rb_ary_pop(a);
2027
+ }
2028
+
2029
+ static VALUE realm_call(int argc, VALUE *argv, VALUE self)
2030
+ {
2031
+ VALUE name, args, a, e;
2032
+ Realm *r;
2033
+ Context *c = realm_live_context(self, &r);
2034
+ Ser s;
2035
+
2036
+ rb_scan_args(argc, argv, "1*", &name, &args);
2037
+ Check_Type(name, T_STRING);
2038
+ rb_ary_unshift(args, name);
2039
+ ser_init1(&s, 'C');
2040
+ if (serialize(&s, args)) {
2041
+ ser_reset(&s);
2042
+ rb_raise(runtime_error, "Realm#call: %s", s.err);
2043
+ }
2044
+ a = realm_rendezvous(c, r->id, &s);
2045
+ e = rb_ary_pop(a);
2046
+ handle_exception(e);
2047
+ return rb_ary_pop(a);
2048
+ }
2049
+
2050
+ static VALUE realm_attach(VALUE self, VALUE name, VALUE proc)
2051
+ {
2052
+ VALUE e;
2053
+ Realm *r;
2054
+ Context *c = realm_live_context(self, &r);
2055
+ Ser s;
2056
+
2057
+ ser_init1(&s, 'A');
2058
+ ser_array_begin(&s, 2);
2059
+ add_string(&s, name);
2060
+ ser_int(&s, RARRAY_LENINT(c->procs));
2061
+ ser_array_end(&s, 2);
2062
+ rb_ary_push(c->procs, proc);
2063
+ e = realm_rendezvous(c, r->id, &s);
2064
+ handle_exception(e);
2065
+ return Qnil;
2066
+ }
2067
+
2068
+ static VALUE realm_dispose(VALUE self)
2069
+ {
2070
+ Realm *r;
2071
+ Context *c;
2072
+ Ser s;
2073
+
2074
+ TypedData_Get_Struct(self, Realm, &realm_type, r);
2075
+ if (r->disposed)
2076
+ return Qnil;
2077
+ TypedData_Get_Struct(r->context, Context, &context_type, c);
2078
+ // Mark disposed before the rendezvous: once we have committed to expunging
2079
+ // the realm, disposed? must report true even if the rendezvous below raises
2080
+ // (e.g. the context is concurrently disposed). The realm is unusable either
2081
+ // way, and leaving the flag clear would re-send the request on a retry.
2082
+ r->disposed = 1;
2083
+ if (!atomic_load(&c->quit)) {
2084
+ ser_init1(&s, 'X'); // e(X)punge realm, payload [id]
2085
+ ser_int(&s, r->id);
2086
+ rendezvous(c, &s.b); // reply is an empty string; ignore
2087
+ }
2088
+ return Qnil;
2089
+ }
2090
+
2091
+ static VALUE context_create_realm(VALUE self)
2092
+ {
2093
+ Context *c;
2094
+ VALUE a, e, result, realm_v;
2095
+ Realm *r;
2096
+ Buf b;
2097
+
2098
+ TypedData_Get_Struct(self, Context, &context_type, c);
2099
+ if (atomic_load(&c->quit))
2100
+ rb_raise(context_disposed_error, "disposed context");
2101
+ buf_init(&b);
2102
+ buf_putc(&b, 'B'); // (B)uild realm, returns [id, err]
2103
+ a = rendezvous(c, &b); // takes ownership of |b|
2104
+ e = rb_ary_pop(a);
2105
+ handle_exception(e);
2106
+ result = rb_ary_pop(a);
2107
+
2108
+ realm_v = rb_obj_alloc(realm_class); // skip the raising initialize
2109
+ TypedData_Get_Struct(realm_v, Realm, &realm_type, r);
2110
+ r->context = self;
2111
+ r->id = NUM2INT(result);
2112
+ r->disposed = 0;
2113
+ return realm_v;
2114
+ }
2115
+
1899
2116
  static VALUE context_pump_message_loop(VALUE self)
1900
2117
  {
1901
2118
  Context *c;
@@ -2855,6 +3072,7 @@ void Init_mini_racer_extension(void)
2855
3072
  rb_define_method(c, "heap_snapshot", context_heap_snapshot, 0);
2856
3073
  rb_define_method(c, "perform_microtask_checkpoint", context_perform_microtask_checkpoint, 0);
2857
3074
  rb_define_method(c, "reset_realm", context_reset_realm, 0);
3075
+ rb_define_method(c, "create_realm", context_create_realm, 0);
2858
3076
  rb_define_method(c, "pump_message_loop", context_pump_message_loop, 0);
2859
3077
  rb_define_method(c, "low_memory_notification", context_low_memory_notification, 0);
2860
3078
  rb_define_method(c, "dynamic_import_resolver", context_get_dynamic_import_resolver, 0);
@@ -2882,6 +3100,16 @@ void Init_mini_racer_extension(void)
2882
3100
  rb_define_method(c, "disposed?", module_disposed_p, 0);
2883
3101
  rb_define_alloc_func(c, module_alloc);
2884
3102
 
3103
+ c = realm_class = rb_define_class_under(m, "Realm", rb_cObject);
3104
+ rb_define_method(c, "initialize", realm_initialize, -1);
3105
+ rb_define_method(c, "id", realm_id, 0);
3106
+ rb_define_method(c, "eval", realm_eval, -1);
3107
+ rb_define_method(c, "call", realm_call, -1);
3108
+ rb_define_method(c, "attach", realm_attach, 2);
3109
+ rb_define_method(c, "dispose", realm_dispose, 0);
3110
+ rb_define_method(c, "disposed?", realm_disposed_p, 0);
3111
+ rb_define_alloc_func(c, realm_alloc);
3112
+
2885
3113
  c = snapshot_class = rb_define_class_under(m, "Snapshot", rb_cObject);
2886
3114
  rb_define_method(c, "initialize", snapshot_initialize, -1);
2887
3115
  rb_define_method(c, "warmup!", snapshot_warmup, 1);