rusty_racer 0.1.7 → 0.1.8
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 +5 -2
- data/ext/rusty_racer/Cargo.toml +1 -1
- data/ext/rusty_racer/src/lib.rs +7 -0
- data/ext/rusty_racer/src/marshal.rs +54 -6
- data/lib/rusty_racer/version.rb +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: af5578c6ac8ba4eb6d367097be2faef786fdc0cb077ac5c76c14f1ec50106d41
|
|
4
|
+
data.tar.gz: 5cc05af64db35a3202c3fd72d2b0809e621ee7fa59c3db4e913ea537d835c438
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ab88c79367db640e4144c0844a2ffcc0168f353de01ad7c225902c756d350f7a48188d9a3c0890ec6da2a61dc83e6a2484e519ccb082bb171d4fa795f89a4e7f
|
|
7
|
+
data.tar.gz: da6ca243ceb13b6d353ce15c6b59e725d617c0712e146a13017f2bbb2d7715dad0705849c4ce36b1b941b0dcb1a2a1750390d557b80c3e7a196809a66a5d4efa
|
data/README.md
CHANGED
|
@@ -15,7 +15,10 @@ Embed [V8](https://v8.dev/) in Ruby, built on [rusty_v8](https://crates.io/crate
|
|
|
15
15
|
not just classic scripts.
|
|
16
16
|
- **Faithful value marshalling**: `BigInt`, `Date`, `Map`, `Set`, typed binary
|
|
17
17
|
(`Uint8Array`/`ArrayBuffer` ↔ binary `String`), and shared/cyclic object
|
|
18
|
-
graphs all round-trip — no lossy JSON hop.
|
|
18
|
+
graphs all round-trip — no lossy JSON hop. A JS `Map` surfaces in Ruby as a
|
|
19
|
+
`RustyRacer::JSMap` (a `Hash` subclass — it reads like a `Hash` but keeps its
|
|
20
|
+
non-string keys) and marshals back to a JS `Map`; a plain `Hash` still maps to a
|
|
21
|
+
JS object.
|
|
19
22
|
- **In-thread execution** — V8 runs on the calling Ruby thread, with no dedicated
|
|
20
23
|
V8 thread and no per-op thread hop; fast when you run many small ops.
|
|
21
24
|
- **Drop-in [ExecJS](#execjs) runtime** — any ExecJS consumer switches with no
|
|
@@ -58,7 +61,7 @@ ctx.eval("1 + 1") # => 2
|
|
|
58
61
|
ctx.eval("({a: 1, b: [true, 'x']})") # => {"a"=>1, "b"=>[true, "x"]}
|
|
59
62
|
|
|
60
63
|
# Call a JS function with marshalled args (BigInt/Date/Map/Set/shared refs
|
|
61
|
-
# all round-trip faithfully).
|
|
64
|
+
# all round-trip faithfully; a JS Map surfaces as a RustyRacer::JSMap).
|
|
62
65
|
ctx.eval("function add(a, b) { return a + b }")
|
|
63
66
|
ctx.call("add", 20, 22) # => 42
|
|
64
67
|
ctx.call_void("doSideEffect") # runs it; never marshals the return
|
data/ext/rusty_racer/Cargo.toml
CHANGED
data/ext/rusty_racer/src/lib.rs
CHANGED
|
@@ -3019,6 +3019,13 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
|
|
|
3019
3019
|
jsmodule.define_method("dispose", method!(JsModule::dispose, 0))?;
|
|
3020
3020
|
jsmodule.define_method("disposed?", method!(JsModule::disposed, 0))?;
|
|
3021
3021
|
|
|
3022
|
+
// A JS Map marshals to this Hash subclass (not a plain Hash) so the reverse
|
|
3023
|
+
// direction can tell it apart from a JS Object — also a Hash — and rebuild a
|
|
3024
|
+
// JS Map instead of a plain object. It behaves exactly like a Hash (so
|
|
3025
|
+
// `assert_kind_of Hash` / `h[k]` still work); users may also construct one to
|
|
3026
|
+
// hand a JS Map to JS. See marshal.rs.
|
|
3027
|
+
module.define_class("JSMap", ruby.class_object().const_get::<_, magnus::RClass>("Hash")?)?;
|
|
3028
|
+
|
|
3022
3029
|
let platform = module.define_module("Platform")?;
|
|
3023
3030
|
platform.define_singleton_method("set_flags!", function!(platform_set_flags, -1))?;
|
|
3024
3031
|
|
|
@@ -48,8 +48,11 @@ pub(crate) enum JsVal {
|
|
|
48
48
|
Array { id: u32, items: Vec<JsVal> },
|
|
49
49
|
// JS object / Ruby Hash with string keys. Insertion order preserved.
|
|
50
50
|
Obj { id: u32, entries: Vec<(String, JsVal)> },
|
|
51
|
-
// JS Map <-> Ruby Hash. Keys are arbitrary
|
|
52
|
-
// this is distinct from Obj. Insertion order
|
|
51
|
+
// JS Map <-> Ruby RustyRacer::JSMap (a Hash subclass). Keys are arbitrary
|
|
52
|
+
// values (not just strings), so this is distinct from Obj. Insertion order
|
|
53
|
+
// preserved. The JSMap subclass is what lets a Map round-trip: a plain Ruby
|
|
54
|
+
// Hash marshals to Obj (a JS Object), a JSMap to Map — the return leg tells
|
|
55
|
+
// them apart by class (Ruby has no native Map type).
|
|
53
56
|
Map { id: u32, pairs: Vec<(JsVal, JsVal)> },
|
|
54
57
|
// JS Set <-> Ruby Set (stdlib).
|
|
55
58
|
Set { id: u32, items: Vec<JsVal> },
|
|
@@ -431,6 +434,15 @@ pub(crate) fn jsval_to_ruby(ruby: &Ruby, val: &JsVal) -> Result<Value, Error> {
|
|
|
431
434
|
// reference. This invariant is load-bearing: do NOT refactor an arm to stash a
|
|
432
435
|
// value in `built` without keeping it rooted by a live local until it's grafted.
|
|
433
436
|
|
|
437
|
+
// The RustyRacer::JSMap class (a Hash subclass), defined in init. A JS Map
|
|
438
|
+
// marshals to an instance of it so the reverse direction can distinguish it from
|
|
439
|
+
// a plain Hash (a JS Object) and rebuild a JS Map. Errors only if init didn't run.
|
|
440
|
+
fn js_map_class(ruby: &Ruby) -> Result<magnus::RClass, Error> {
|
|
441
|
+
ruby.class_object()
|
|
442
|
+
.const_get::<_, magnus::RModule>("RustyRacer")?
|
|
443
|
+
.const_get::<_, magnus::RClass>("JSMap")
|
|
444
|
+
}
|
|
445
|
+
|
|
434
446
|
fn jsval_to_ruby_d(
|
|
435
447
|
ruby: &Ruby,
|
|
436
448
|
val: &JsVal,
|
|
@@ -494,16 +506,27 @@ fn jsval_to_ruby_d(
|
|
|
494
506
|
}
|
|
495
507
|
h.as_value()
|
|
496
508
|
}
|
|
497
|
-
// JS Map ->
|
|
509
|
+
// JS Map -> RustyRacer::JSMap, a Hash subclass: arbitrary marshalled keys
|
|
510
|
+
// (not just strings) AND distinguishable from a JS Object (a plain Hash),
|
|
511
|
+
// so ruby_to_jsval can rebuild a JS Map rather than a plain object. Build
|
|
512
|
+
// empty then aset so a cyclic Map (a value referring back to the Map)
|
|
513
|
+
// resolves through the Ref table.
|
|
514
|
+
//
|
|
515
|
+
// LIMITATION: keys collapse by Ruby Hash equality (eql?/hash), but a JS
|
|
516
|
+
// Map keys by identity (SameValueZero). Primitive keys (string/number/
|
|
517
|
+
// bool/bigint) round-trip exactly; two distinct-but-structurally-equal
|
|
518
|
+
// OBJECT keys (e.g. `{}` and `{}`) become ONE entry — inherent to a Hash
|
|
519
|
+
// representation, and object identity can't survive copy-marshalling anyway.
|
|
498
520
|
JsVal::Map { id, pairs } => {
|
|
499
|
-
let
|
|
500
|
-
built.insert(*id,
|
|
521
|
+
let map_obj: Value = js_map_class(ruby)?.funcall("new", ())?;
|
|
522
|
+
built.insert(*id, map_obj);
|
|
523
|
+
let h = RHash::from_value(map_obj).expect("JSMap is a Hash");
|
|
501
524
|
for (k, v) in pairs {
|
|
502
525
|
let kk = jsval_to_ruby_d(ruby, k, built)?;
|
|
503
526
|
let vv = jsval_to_ruby_d(ruby, v, built)?;
|
|
504
527
|
let _ = h.aset(kk, vv);
|
|
505
528
|
}
|
|
506
|
-
|
|
529
|
+
map_obj
|
|
507
530
|
}
|
|
508
531
|
// JS Set -> Ruby Set (stdlib); build empty then add so a cyclic Set
|
|
509
532
|
// (a Set containing itself) resolves through the Ref table.
|
|
@@ -726,6 +749,31 @@ fn ruby_to_jsval_d(val: Value, seen: &mut RbSeen, depth: u32) -> Result<JsVal, E
|
|
|
726
749
|
}
|
|
727
750
|
return Ok(JsVal::Array { id, items });
|
|
728
751
|
}
|
|
752
|
+
// RustyRacer::JSMap (a Hash subclass) -> JS Map, preserving arbitrary keys as
|
|
753
|
+
// real JS values (NOT string-coerced like a plain Hash -> JS Object). Must come
|
|
754
|
+
// BEFORE the Hash branch, since a JSMap IS-A Hash. This is what makes a JS Map
|
|
755
|
+
// round-trip (JS Map -> JSMap -> JS Map) instead of degrading to an object.
|
|
756
|
+
if let Ok(js_map) = js_map_class(&ruby) {
|
|
757
|
+
if val.is_kind_of(js_map) {
|
|
758
|
+
let id = match rb_container_id(seen, val, depth)? {
|
|
759
|
+
RbId::New(id) => id,
|
|
760
|
+
RbId::Reuse(jv) => return Ok(jv),
|
|
761
|
+
};
|
|
762
|
+
let hash = RHash::from_value(val).expect("JSMap is a Hash");
|
|
763
|
+
let pairs = RefCell::new(Vec::new());
|
|
764
|
+
hash.foreach(|k: Value, v: Value| {
|
|
765
|
+
pairs.borrow_mut().push((
|
|
766
|
+
ruby_to_jsval_d(k, seen, depth + 1)?,
|
|
767
|
+
ruby_to_jsval_d(v, seen, depth + 1)?,
|
|
768
|
+
));
|
|
769
|
+
Ok(magnus::r_hash::ForEach::Continue)
|
|
770
|
+
})?;
|
|
771
|
+
return Ok(JsVal::Map {
|
|
772
|
+
id,
|
|
773
|
+
pairs: pairs.into_inner(),
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
}
|
|
729
777
|
if let Ok(hash) = RHash::try_convert(val) {
|
|
730
778
|
let id = match rb_container_id(seen, val, depth)? {
|
|
731
779
|
RbId::New(id) => id,
|
data/lib/rusty_racer/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rusty_racer
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.8
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Keita Urashima
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-06-
|
|
11
|
+
date: 2026-06-15 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rb_sys
|
|
@@ -54,7 +54,7 @@ metadata:
|
|
|
54
54
|
source_code_uri: https://github.com/ursm/rusty_racer
|
|
55
55
|
bug_tracker_uri: https://github.com/ursm/rusty_racer/issues
|
|
56
56
|
rubygems_mfa_required: 'true'
|
|
57
|
-
post_install_message:
|
|
57
|
+
post_install_message:
|
|
58
58
|
rdoc_options: []
|
|
59
59
|
require_paths:
|
|
60
60
|
- lib
|
|
@@ -70,7 +70,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
70
70
|
version: '0'
|
|
71
71
|
requirements: []
|
|
72
72
|
rubygems_version: 3.5.22
|
|
73
|
-
signing_key:
|
|
73
|
+
signing_key:
|
|
74
74
|
specification_version: 4
|
|
75
75
|
summary: Embed V8 in Ruby via rusty_v8 + Magnus (rb-sys)
|
|
76
76
|
test_files: []
|