mini_racer 0.19.2 → 0.21.0
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/CHANGELOG +6 -0
- data/README.md +38 -0
- data/ext/mini_racer_extension/mini_racer_extension.c +33 -4
- data/ext/mini_racer_extension/serde.c +13 -0
- data/lib/mini_racer/truffleruby.rb +4 -1
- data/lib/mini_racer/version.rb +1 -1
- data/lib/mini_racer.rb +11 -0
- 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: 7cd44fe0925a58e264712d933d805af54b517d31cdbefc26455e7e139e234279
|
|
4
|
+
data.tar.gz: 17190bc083b885c838b0dfc3424c3fc08fb02df354972a3007a292ddf70afc22
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9cdd4cdf20f9be0e3ceb34daa48f7f1eeb93c4c11eada10c23e2c3e3dbd82cf7a13c8102e67140cc15c028c51a468c6e74ac518c76050cadceb33aa99fd71b7d
|
|
7
|
+
data.tar.gz: c7110aabb771e18bccd85283696e8f811970cf8d03bb0ef1f573a84f2fa7da2ef1014b5138513b09d80825de96ddebe7e6316f79efbe8adecbd1ff202b14e4d5
|
data/CHANGELOG
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
- 0.21.0 - 16-04-2026
|
|
2
|
+
- Add MiniRacer::Binary for returning Uint8Array to JavaScript from attached Ruby callbacks
|
|
3
|
+
|
|
4
|
+
- 0.20.0 - 24-02-2026
|
|
5
|
+
- Add Snapshot.load to restore snapshots from binary data, enabling disk persistence
|
|
6
|
+
|
|
1
7
|
- 0.19.2 - 24-12-2025
|
|
2
8
|
- upgrade to node 24.12.0
|
|
3
9
|
|
data/README.md
CHANGED
|
@@ -60,6 +60,25 @@ puts context.eval("array_and_hash()")
|
|
|
60
60
|
# => {"a" => 1, "b" => [1, {"a" => 1}]}
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
+
### Return binary data from Ruby to JavaScript
|
|
64
|
+
|
|
65
|
+
Attached Ruby functions can return binary data as `Uint8Array` using `MiniRacer::Binary`:
|
|
66
|
+
|
|
67
|
+
```ruby
|
|
68
|
+
require "digest"
|
|
69
|
+
|
|
70
|
+
context = MiniRacer::Context.new
|
|
71
|
+
context.attach("sha256_raw", ->(data) {
|
|
72
|
+
MiniRacer::Binary.new(Digest::SHA256.digest(data))
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
# Inside JavaScript the return value is a Uint8Array
|
|
76
|
+
context.eval("sha256_raw('hello') instanceof Uint8Array") # => true
|
|
77
|
+
context.eval("sha256_raw('hello').length") # => 32
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
This is useful when you need to pass raw bytes (e.g., cryptographic digests, compressed data, binary file contents) from Ruby to JavaScript. The `MiniRacer::Binary` wrapper tells the bridge to serialize the data as a `Uint8Array` on the JavaScript side rather than a string.
|
|
81
|
+
|
|
63
82
|
### GIL free JavaScript execution
|
|
64
83
|
|
|
65
84
|
The Ruby Global interpreter lock is released when scripts are executing:
|
|
@@ -193,6 +212,25 @@ context.eval("counter")
|
|
|
193
212
|
# => 1
|
|
194
213
|
```
|
|
195
214
|
|
|
215
|
+
Snapshots can also be persisted to disk for faster startup:
|
|
216
|
+
|
|
217
|
+
```ruby
|
|
218
|
+
# Save a snapshot to disk
|
|
219
|
+
snapshot = MiniRacer::Snapshot.new('var foo = "bar";')
|
|
220
|
+
File.binwrite("snapshot.bin", snapshot.dump)
|
|
221
|
+
|
|
222
|
+
# Load it back in a later process
|
|
223
|
+
blob = File.binread("snapshot.bin")
|
|
224
|
+
snapshot = MiniRacer::Snapshot.load(blob)
|
|
225
|
+
context = MiniRacer::Context.new(snapshot: snapshot)
|
|
226
|
+
context.eval("foo")
|
|
227
|
+
# => "bar"
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Note that snapshots are architecture and V8-version specific. A snapshot created on one platform (e.g., ARM64 macOS) cannot be loaded on a different platform (e.g., x86_64 Linux). Snapshots are best used for same-machine caching or homogeneous deployment environments.
|
|
231
|
+
|
|
232
|
+
**Security note:** Only load snapshots from trusted sources. V8 snapshots are not designed to be safely loaded from untrusted input—malformed or malicious snapshot data may cause crashes or memory corruption.
|
|
233
|
+
|
|
196
234
|
### Garbage collection
|
|
197
235
|
|
|
198
236
|
You can make the garbage collector more aggressive by defining the context with `MiniRacer::Context.new(ensure_gc_after_idle: 1000)`. Using this will ensure V8 will run a full GC using `context.low_memory_notification` 1 second after the last eval on the context. Low memory notifications ensure long living contexts use minimal amounts of memory.
|
|
@@ -193,6 +193,7 @@ static VALUE terminated_error;
|
|
|
193
193
|
static VALUE context_class;
|
|
194
194
|
static VALUE snapshot_class;
|
|
195
195
|
static VALUE date_time_class;
|
|
196
|
+
static VALUE binary_class;
|
|
196
197
|
static VALUE js_function_class;
|
|
197
198
|
|
|
198
199
|
static pthread_mutex_t flags_mtx = PTHREAD_MUTEX_INITIALIZER;
|
|
@@ -688,10 +689,17 @@ static int serialize1(Ser *s, VALUE refs, VALUE v)
|
|
|
688
689
|
// entirely new objects
|
|
689
690
|
if (rb_respond_to(v, rb_intern("to_time"))) {
|
|
690
691
|
v = rb_funcall(v, rb_intern("to_time"), 0);
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
692
|
+
if (rb_obj_is_kind_of(v, rb_cTime)) {
|
|
693
|
+
struct timeval tv = rb_time_timeval(v);
|
|
694
|
+
ser_date(s, tv.tv_sec*1e3 + tv.tv_usec/1e3);
|
|
695
|
+
} else {
|
|
696
|
+
snprintf(s->err, sizeof(s->err), "unsupported type %s", rb_class2name(CLASS_OF(v)));
|
|
697
|
+
return -1;
|
|
698
|
+
}
|
|
699
|
+
} else if (!NIL_P(binary_class) && rb_obj_is_kind_of(v, binary_class)) {
|
|
700
|
+
t = rb_ivar_get(v, rb_intern("@data"));
|
|
701
|
+
Check_Type(t, T_STRING);
|
|
702
|
+
ser_uint8array(s, RSTRING_PTR(t), RSTRING_LEN(t));
|
|
695
703
|
} else {
|
|
696
704
|
snprintf(s->err, sizeof(s->err), "unsupported type %s", rb_class2name(CLASS_OF(v)));
|
|
697
705
|
return -1;
|
|
@@ -1111,6 +1119,11 @@ static VALUE context_alloc(VALUE klass)
|
|
|
1111
1119
|
if (Qtrue == rb_funcall(rb_cObject, f, 1, a))
|
|
1112
1120
|
date_time_class = rb_const_get(rb_cObject, rb_intern("DateTime"));
|
|
1113
1121
|
}
|
|
1122
|
+
if (NIL_P(binary_class)) {
|
|
1123
|
+
VALUE m = rb_const_get(rb_cObject, rb_intern("MiniRacer"));
|
|
1124
|
+
if (Qtrue == rb_funcall(m, rb_intern("const_defined?"), 1, rb_str_new_cstr("Binary")))
|
|
1125
|
+
binary_class = rb_const_get(m, rb_intern("Binary"));
|
|
1126
|
+
}
|
|
1114
1127
|
c = ruby_xmalloc(sizeof(*c));
|
|
1115
1128
|
memset(c, 0, sizeof(*c));
|
|
1116
1129
|
c->exception = Qnil;
|
|
@@ -1690,6 +1703,20 @@ static VALUE snapshot_dump(VALUE self)
|
|
|
1690
1703
|
return ss->blob;
|
|
1691
1704
|
}
|
|
1692
1705
|
|
|
1706
|
+
static VALUE snapshot_load(VALUE klass, VALUE blob)
|
|
1707
|
+
{
|
|
1708
|
+
Snapshot *ss;
|
|
1709
|
+
VALUE self;
|
|
1710
|
+
|
|
1711
|
+
Check_Type(blob, T_STRING);
|
|
1712
|
+
self = snapshot_alloc(klass);
|
|
1713
|
+
TypedData_Get_Struct(self, Snapshot, &snapshot_type, ss);
|
|
1714
|
+
ss->blob = rb_str_dup(blob);
|
|
1715
|
+
rb_enc_associate(ss->blob, rb_ascii8bit_encoding());
|
|
1716
|
+
ENC_CODERANGE_SET(ss->blob, ENC_CODERANGE_VALID);
|
|
1717
|
+
return self;
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1693
1720
|
static VALUE snapshot_size0(VALUE self)
|
|
1694
1721
|
{
|
|
1695
1722
|
Snapshot *ss;
|
|
@@ -1742,11 +1769,13 @@ void Init_mini_racer_extension(void)
|
|
|
1742
1769
|
rb_define_method(c, "warmup!", snapshot_warmup, 1);
|
|
1743
1770
|
rb_define_method(c, "dump", snapshot_dump, 0);
|
|
1744
1771
|
rb_define_method(c, "size", snapshot_size0, 0);
|
|
1772
|
+
rb_define_singleton_method(c, "load", snapshot_load, 1);
|
|
1745
1773
|
rb_define_alloc_func(c, snapshot_alloc);
|
|
1746
1774
|
|
|
1747
1775
|
c = rb_define_class_under(m, "Platform", rb_cObject);
|
|
1748
1776
|
rb_define_singleton_method(c, "set_flags!", platform_set_flags, -1);
|
|
1749
1777
|
|
|
1750
1778
|
date_time_class = Qnil; // lazy init
|
|
1779
|
+
binary_class = Qnil; // lazy init
|
|
1751
1780
|
js_function_class = rb_define_class_under(m, "JavaScriptFunction", rb_cObject);
|
|
1752
1781
|
}
|
|
@@ -322,6 +322,19 @@ static void ser_string16(Ser *s, const void *p, size_t n)
|
|
|
322
322
|
w(s, p, n);
|
|
323
323
|
}
|
|
324
324
|
|
|
325
|
+
// Uint8Array: ArrayBuffer header + data + typed array view descriptor
|
|
326
|
+
static void ser_uint8array(Ser *s, const void *p, size_t n)
|
|
327
|
+
{
|
|
328
|
+
w_byte(s, 'B'); // ArrayBuffer tag
|
|
329
|
+
w_varint(s, n); // byte length
|
|
330
|
+
w(s, p, n); // raw bytes
|
|
331
|
+
w_byte(s, 'V'); // typed array view tag
|
|
332
|
+
w_byte(s, 'B'); // Uint8Array type
|
|
333
|
+
w_varint(s, 0); // byteOffset
|
|
334
|
+
w_varint(s, n); // byteLength
|
|
335
|
+
w_varint(s, 0); // flags
|
|
336
|
+
}
|
|
337
|
+
|
|
325
338
|
static void ser_object_begin(Ser *s)
|
|
326
339
|
{
|
|
327
340
|
w_byte(s, 'o');
|
|
@@ -102,7 +102,7 @@ module MiniRacer
|
|
|
102
102
|
else
|
|
103
103
|
@snapshot = nil
|
|
104
104
|
end
|
|
105
|
-
@is_object_or_array_func, @is_map_func, @is_map_iterator_func, @is_time_func, @js_date_to_time_func, @is_symbol_func, @js_symbol_to_symbol_func, @js_new_date_func, @js_new_array_func = eval_in_context <<-CODE
|
|
105
|
+
@is_object_or_array_func, @is_map_func, @is_map_iterator_func, @is_time_func, @js_date_to_time_func, @is_symbol_func, @js_symbol_to_symbol_func, @js_new_date_func, @js_new_array_func, @js_new_uint8array_func = eval_in_context <<-CODE
|
|
106
106
|
[
|
|
107
107
|
(x) => { return (x instanceof Object || x instanceof Array) && !(x instanceof Date) && !(x instanceof Function) },
|
|
108
108
|
(x) => { return x instanceof Map },
|
|
@@ -113,6 +113,7 @@ module MiniRacer
|
|
|
113
113
|
(x) => { var r = x.description; return r === undefined ? 'undefined' : r },
|
|
114
114
|
(x) => { return new Date(x) },
|
|
115
115
|
(x) => { return new Array(x) },
|
|
116
|
+
(x) => { return new Uint8Array(x) },
|
|
116
117
|
]
|
|
117
118
|
CODE
|
|
118
119
|
end
|
|
@@ -329,6 +330,8 @@ module MiniRacer
|
|
|
329
330
|
js_new_date(value.to_f * 1000)
|
|
330
331
|
when DateTime
|
|
331
332
|
js_new_date(value.to_time.to_f * 1000)
|
|
333
|
+
when MiniRacer::Binary
|
|
334
|
+
@js_new_uint8array_func.call(value.data.bytes)
|
|
332
335
|
else
|
|
333
336
|
"Undefined Conversion"
|
|
334
337
|
end
|
data/lib/mini_racer/version.rb
CHANGED
data/lib/mini_racer.rb
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
require "mini_racer/version"
|
|
2
2
|
require "pathname"
|
|
3
3
|
|
|
4
|
+
module MiniRacer
|
|
5
|
+
class Binary
|
|
6
|
+
attr_reader :data
|
|
7
|
+
|
|
8
|
+
def initialize(data)
|
|
9
|
+
raise TypeError, "wrong argument type #{data.class} (expected String)" unless data.is_a?(String)
|
|
10
|
+
@data = data
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
4
15
|
if RUBY_ENGINE == "truffleruby"
|
|
5
16
|
require "mini_racer/truffleruby"
|
|
6
17
|
else
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mini_racer
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.21.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sam Saffron
|
|
@@ -136,9 +136,9 @@ licenses:
|
|
|
136
136
|
- MIT
|
|
137
137
|
metadata:
|
|
138
138
|
bug_tracker_uri: https://github.com/discourse/mini_racer/issues
|
|
139
|
-
changelog_uri: https://github.com/discourse/mini_racer/blob/v0.
|
|
140
|
-
documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.
|
|
141
|
-
source_code_uri: https://github.com/discourse/mini_racer/tree/v0.
|
|
139
|
+
changelog_uri: https://github.com/discourse/mini_racer/blob/v0.21.0/CHANGELOG
|
|
140
|
+
documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.21.0
|
|
141
|
+
source_code_uri: https://github.com/discourse/mini_racer/tree/v0.21.0
|
|
142
142
|
rdoc_options: []
|
|
143
143
|
require_paths:
|
|
144
144
|
- lib
|
|
@@ -154,7 +154,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
154
154
|
- !ruby/object:Gem::Version
|
|
155
155
|
version: '0'
|
|
156
156
|
requirements: []
|
|
157
|
-
rubygems_version:
|
|
157
|
+
rubygems_version: 4.0.6
|
|
158
158
|
specification_version: 4
|
|
159
159
|
summary: Minimal embedded v8 for Ruby
|
|
160
160
|
test_files: []
|