mini_racer 0.19.0 → 0.19.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 +4 -4
- data/CHANGELOG +5 -0
- data/ext/mini_racer_extension/mini_racer_extension.c +104 -50
- data/ext/mini_racer_extension/mini_racer_v8.cc +29 -2
- data/ext/mini_racer_extension/serde.c +7 -3
- data/lib/mini_racer/truffleruby.rb +4 -0
- data/lib/mini_racer/version.rb +1 -1
- data/lib/mini_racer.rb +13 -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: 8c2110c8c14882bd775ad556afdeb79e1ab42bfabddc24101b82f8390a5be830
|
4
|
+
data.tar.gz: 8c70e50ac0edbe545ddd0a17737c045f21ebd6a2aa47f8d730a7f7ced79248b3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e8b17ae50c4455ed2c3db185f5e03d9554bb30096fc1540989cc7aa0e56f471ade5a7d0f815f1a24b3e492cd9c8c7db80bb5146bcf52dfdcb707caaf7323daaa
|
7
|
+
data.tar.gz: f1ef6b7225779091ab4c488f0177da8b7d8dd19e779d80a99ae307e9efbfd49e654c645f76315c4b766090cc50ce7f2ec2568586432a122d7d7aef3f7ba93d0d
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
- 0.19.1 - 20-10-2025
|
2
|
+
- JS code can now catch ruby exceptions - Ben Noordhuis
|
3
|
+
- Retain string encoding when raising exceptions - Ben Noordhuis
|
4
|
+
- Fix object identity bug with Ruby to JS conversion - Benjamin Wood
|
5
|
+
|
1
6
|
- 0.19.0 - 24-06-2025
|
2
7
|
- upgrade to node 24.1.0
|
3
8
|
|
@@ -13,6 +13,13 @@
|
|
13
13
|
#include "serde.c"
|
14
14
|
#include "mini_racer_v8.h"
|
15
15
|
|
16
|
+
// for debugging
|
17
|
+
#define RB_PUTS(v) \
|
18
|
+
do { \
|
19
|
+
fflush(stdout); \
|
20
|
+
rb_funcall(rb_mKernel, rb_intern("puts"), 1, v); \
|
21
|
+
} while (0)
|
22
|
+
|
16
23
|
#if RUBY_API_VERSION_CODE < 3*10000+4*100 // 3.4.0
|
17
24
|
static inline void rb_thread_lock_native_thread(void)
|
18
25
|
{
|
@@ -154,6 +161,7 @@ static VALUE platform_init_error;
|
|
154
161
|
static VALUE context_disposed_error;
|
155
162
|
static VALUE parse_error;
|
156
163
|
static VALUE memory_error;
|
164
|
+
static VALUE script_error;
|
157
165
|
static VALUE runtime_error;
|
158
166
|
static VALUE internal_error;
|
159
167
|
static VALUE snapshot_error;
|
@@ -482,12 +490,36 @@ static void des_object_ref(void *arg, uint32_t id)
|
|
482
490
|
|
483
491
|
static void des_error_begin(void *arg)
|
484
492
|
{
|
485
|
-
push(arg,
|
493
|
+
push(arg, rb_ary_new());
|
486
494
|
}
|
487
495
|
|
488
496
|
static void des_error_end(void *arg)
|
489
497
|
{
|
490
|
-
|
498
|
+
VALUE *a, h, message, stack, cause, newline;
|
499
|
+
DesCtx *c;
|
500
|
+
|
501
|
+
c = arg;
|
502
|
+
if (*c->err)
|
503
|
+
return;
|
504
|
+
if (c->tos == c->stack) {
|
505
|
+
snprintf(c->err, sizeof(c->err), "stack underflow");
|
506
|
+
return;
|
507
|
+
}
|
508
|
+
a = &c->tos->a;
|
509
|
+
h = rb_ary_pop(*a);
|
510
|
+
message = rb_hash_aref(h, rb_str_new_cstr("message"));
|
511
|
+
stack = rb_hash_aref(h, rb_str_new_cstr("stack"));
|
512
|
+
cause = rb_hash_aref(h, rb_str_new_cstr("cause"));
|
513
|
+
if (NIL_P(message))
|
514
|
+
message = rb_str_new_cstr("JS exception");
|
515
|
+
if (!NIL_P(stack)) {
|
516
|
+
newline = rb_str_new_cstr("\n");
|
517
|
+
message = rb_funcall(message, rb_intern("concat"), 2, newline, stack);
|
518
|
+
}
|
519
|
+
*a = rb_class_new_instance(1, &message, script_error);
|
520
|
+
if (!NIL_P(cause))
|
521
|
+
rb_iv_set(*a, "@cause", cause);
|
522
|
+
pop(c);
|
491
523
|
}
|
492
524
|
|
493
525
|
static int collect(VALUE k, VALUE v, VALUE a)
|
@@ -527,52 +559,58 @@ static int serialize1(Ser *s, VALUE refs, VALUE v)
|
|
527
559
|
return -1;
|
528
560
|
switch (TYPE(v)) {
|
529
561
|
case T_ARRAY:
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
562
|
+
{
|
563
|
+
VALUE obj_id = rb_obj_id(v);
|
564
|
+
id = rb_hash_lookup(refs, obj_id);
|
565
|
+
if (NIL_P(id)) {
|
566
|
+
n = RARRAY_LENINT(v);
|
567
|
+
i = rb_hash_size_num(refs);
|
568
|
+
rb_hash_aset(refs, obj_id, LONG2FIX(i));
|
569
|
+
ser_array_begin(s, n);
|
570
|
+
for (i = 0; i < n; i++)
|
571
|
+
if (serialize1(s, refs, rb_ary_entry(v, i)))
|
572
|
+
return -1;
|
573
|
+
ser_array_end(s, n);
|
574
|
+
} else {
|
575
|
+
ser_object_ref(s, FIX2LONG(id));
|
576
|
+
}
|
542
577
|
}
|
543
578
|
break;
|
544
579
|
case T_HASH:
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
switch (TYPE(t)) {
|
555
|
-
case T_FIXNUM:
|
556
|
-
case T_STRING:
|
557
|
-
case T_SYMBOL:
|
558
|
-
continue;
|
559
|
-
}
|
560
|
-
break;
|
561
|
-
}
|
562
|
-
if (i == 2*n) {
|
563
|
-
ser_object_begin(s);
|
580
|
+
{
|
581
|
+
VALUE obj_id = rb_obj_id(v);
|
582
|
+
id = rb_hash_lookup(refs, obj_id);
|
583
|
+
if (NIL_P(id)) {
|
584
|
+
a = rb_ary_new();
|
585
|
+
i = rb_hash_size_num(refs);
|
586
|
+
n = rb_hash_size_num(v);
|
587
|
+
rb_hash_aset(refs, obj_id, LONG2FIX(i));
|
588
|
+
rb_hash_foreach(v, collect, a);
|
564
589
|
for (i = 0; i < 2*n; i += 2) {
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
590
|
+
t = rb_ary_entry(a, i);
|
591
|
+
switch (TYPE(t)) {
|
592
|
+
case T_FIXNUM:
|
593
|
+
case T_STRING:
|
594
|
+
case T_SYMBOL:
|
595
|
+
continue;
|
596
|
+
}
|
597
|
+
break;
|
598
|
+
}
|
599
|
+
if (i == 2*n) {
|
600
|
+
ser_object_begin(s);
|
601
|
+
for (i = 0; i < 2*n; i += 2) {
|
602
|
+
if (serialize1(s, refs, rb_ary_entry(a, i+0)))
|
603
|
+
return -1;
|
604
|
+
if (serialize1(s, refs, rb_ary_entry(a, i+1)))
|
605
|
+
return -1;
|
606
|
+
}
|
607
|
+
ser_object_end(s, n);
|
608
|
+
} else {
|
609
|
+
return bail(&s->err, "TODO serialize as Map");
|
569
610
|
}
|
570
|
-
ser_object_end(s, n);
|
571
611
|
} else {
|
572
|
-
|
612
|
+
ser_object_ref(s, FIX2LONG(id));
|
573
613
|
}
|
574
|
-
} else {
|
575
|
-
ser_object_ref(s, FIX2LONG(id));
|
576
614
|
}
|
577
615
|
break;
|
578
616
|
case T_DATA:
|
@@ -888,6 +926,7 @@ static VALUE rendezvous_callback_do(VALUE arg)
|
|
888
926
|
static void *rendezvous_callback(void *arg)
|
889
927
|
{
|
890
928
|
struct rendezvous_nogvl *a;
|
929
|
+
const char *err;
|
891
930
|
Context *c;
|
892
931
|
int exc;
|
893
932
|
VALUE r;
|
@@ -911,7 +950,12 @@ out:
|
|
911
950
|
buf_move(&s.b, a->req);
|
912
951
|
return NULL;
|
913
952
|
fail:
|
914
|
-
|
953
|
+
ser_init0(&s); // ruby exception pending
|
954
|
+
w_byte(&s, 'e'); // send ruby error message to v8 thread
|
955
|
+
r = rb_funcall(c->exception, rb_intern("to_s"), 0);
|
956
|
+
err = StringValueCStr(r);
|
957
|
+
if (err)
|
958
|
+
w(&s, err, strlen(err));
|
915
959
|
goto out;
|
916
960
|
}
|
917
961
|
|
@@ -969,6 +1013,13 @@ static VALUE rendezvous1(Context *c, Buf *req, DesCtx *d)
|
|
969
1013
|
int exc;
|
970
1014
|
|
971
1015
|
rendezvous_no_des(c, req, &res); // takes ownership of |req|
|
1016
|
+
r = c->exception;
|
1017
|
+
c->exception = Qnil;
|
1018
|
+
// if js land didn't handle exception from ruby callback, re-raise it now
|
1019
|
+
if (res.len == 1 && *res.buf == 'e') {
|
1020
|
+
assert(!NIL_P(r));
|
1021
|
+
rb_exc_raise(r);
|
1022
|
+
}
|
972
1023
|
r = rb_protect(deserialize, (VALUE)&(struct rendezvous_des){d, &res}, &exc);
|
973
1024
|
buf_reset(&res);
|
974
1025
|
if (exc) {
|
@@ -976,11 +1027,6 @@ static VALUE rendezvous1(Context *c, Buf *req, DesCtx *d)
|
|
976
1027
|
rb_set_errinfo(Qnil);
|
977
1028
|
rb_exc_raise(r);
|
978
1029
|
}
|
979
|
-
if (!NIL_P(c->exception)) {
|
980
|
-
r = c->exception;
|
981
|
-
c->exception = Qnil;
|
982
|
-
rb_exc_raise(r);
|
983
|
-
}
|
984
1030
|
return r;
|
985
1031
|
}
|
986
1032
|
|
@@ -999,8 +1045,8 @@ static void handle_exception(VALUE e)
|
|
999
1045
|
|
1000
1046
|
if (NIL_P(e))
|
1001
1047
|
return;
|
1002
|
-
StringValue(e);
|
1003
|
-
s =
|
1048
|
+
e = StringValue(e);
|
1049
|
+
s = StringValueCStr(e);
|
1004
1050
|
switch (*s) {
|
1005
1051
|
case NO_ERROR:
|
1006
1052
|
return;
|
@@ -1022,7 +1068,7 @@ static void handle_exception(VALUE e)
|
|
1022
1068
|
default:
|
1023
1069
|
rb_raise(internal_error, "bad error class %02x", *s);
|
1024
1070
|
}
|
1025
|
-
|
1071
|
+
rb_enc_raise(rb_enc_get(e), klass, "%s", s+1);
|
1026
1072
|
}
|
1027
1073
|
|
1028
1074
|
static VALUE context_alloc(VALUE klass)
|
@@ -1628,6 +1674,11 @@ static VALUE snapshot_size0(VALUE self)
|
|
1628
1674
|
return LONG2FIX(RSTRING_LENINT(ss->blob));
|
1629
1675
|
}
|
1630
1676
|
|
1677
|
+
static VALUE script_error_cause(VALUE self)
|
1678
|
+
{
|
1679
|
+
return rb_iv_get(self, "@cause");
|
1680
|
+
}
|
1681
|
+
|
1631
1682
|
__attribute__((visibility("default")))
|
1632
1683
|
void Init_mini_racer_extension(void)
|
1633
1684
|
{
|
@@ -1642,10 +1693,13 @@ void Init_mini_racer_extension(void)
|
|
1642
1693
|
c = rb_define_class_under(m, "EvalError", c);
|
1643
1694
|
parse_error = rb_define_class_under(m, "ParseError", c);
|
1644
1695
|
memory_error = rb_define_class_under(m, "V8OutOfMemoryError", c);
|
1696
|
+
script_error = rb_define_class_under(m, "ScriptError", c);
|
1645
1697
|
runtime_error = rb_define_class_under(m, "RuntimeError", c);
|
1646
1698
|
internal_error = rb_define_class_under(m, "InternalError", c);
|
1647
1699
|
terminated_error = rb_define_class_under(m, "ScriptTerminatedError", c);
|
1648
1700
|
|
1701
|
+
rb_define_method(script_error, "cause", script_error_cause, 0);
|
1702
|
+
|
1649
1703
|
c = context_class = rb_define_class_under(m, "Context", rb_cObject);
|
1650
1704
|
rb_define_method(c, "initialize", context_initialize, -1);
|
1651
1705
|
rb_define_method(c, "attach", context_attach, 2);
|
@@ -86,6 +86,7 @@ struct State
|
|
86
86
|
v8::Persistent<v8::Context> persistent_context; // single-thread mode only
|
87
87
|
v8::Persistent<v8::Context> persistent_safe_context; // single-thread mode only
|
88
88
|
v8::Persistent<v8::Function> persistent_safe_context_function; // single-thread mode only
|
89
|
+
v8::Persistent<v8::Value> ruby_exception;
|
89
90
|
Context *ruby_context;
|
90
91
|
int64_t max_memory;
|
91
92
|
int err_reason;
|
@@ -122,6 +123,20 @@ struct Serialized
|
|
122
123
|
}
|
123
124
|
};
|
124
125
|
|
126
|
+
bool bubble_up_ruby_exception(State& st, v8::TryCatch *try_catch)
|
127
|
+
{
|
128
|
+
auto exception = try_catch->Exception();
|
129
|
+
if (exception.IsEmpty()) return false;
|
130
|
+
auto ruby_exception = v8::Local<v8::Value>::New(st.isolate, st.ruby_exception);
|
131
|
+
if (ruby_exception.IsEmpty()) return false;
|
132
|
+
if (!ruby_exception->SameValue(exception)) return false;
|
133
|
+
// signal that the ruby thread should reraise the exception
|
134
|
+
// that it caught earlier when executing a js->ruby callback
|
135
|
+
uint8_t c = 'e';
|
136
|
+
v8_reply(st.ruby_context, &c, 1);
|
137
|
+
return true;
|
138
|
+
}
|
139
|
+
|
125
140
|
// throws JS exception on serialization error
|
126
141
|
bool reply(State& st, v8::Local<v8::Value> v)
|
127
142
|
{
|
@@ -388,8 +403,17 @@ void v8_api_callback(const v8::FunctionCallbackInfo<v8::Value>& info)
|
|
388
403
|
v8_roundtrip(st.ruby_context, &p, &n);
|
389
404
|
if (*p == 'c') // callback reply
|
390
405
|
break;
|
391
|
-
if (*p == 'e') // ruby exception pending
|
392
|
-
|
406
|
+
if (*p == 'e') { // ruby exception pending
|
407
|
+
v8::Local<v8::String> message;
|
408
|
+
auto type = v8::NewStringType::kNormal;
|
409
|
+
if (!v8::String::NewFromOneByte(st.isolate, p+1, type, n-1).ToLocal(&message)) {
|
410
|
+
message = v8::String::NewFromUtf8Literal(st.isolate, "Ruby exception");
|
411
|
+
}
|
412
|
+
auto exception = v8::Exception::Error(message);
|
413
|
+
st.ruby_exception.Reset(st.isolate, exception);
|
414
|
+
st.isolate->ThrowException(exception);
|
415
|
+
return;
|
416
|
+
}
|
393
417
|
v8_dispatch(st.ruby_context);
|
394
418
|
}
|
395
419
|
v8::ValueDeserializer des(st.isolate, p+1, n-1);
|
@@ -523,6 +547,7 @@ fail:
|
|
523
547
|
cause = st.err_reason ? st.err_reason : TERMINATED_ERROR;
|
524
548
|
st.err_reason = NO_ERROR;
|
525
549
|
}
|
550
|
+
if (bubble_up_ruby_exception(st, &try_catch)) return;
|
526
551
|
if (!cause && try_catch.HasCaught()) cause = RUNTIME_ERROR;
|
527
552
|
if (cause) result = v8::Undefined(st.isolate);
|
528
553
|
auto err = to_error(st, &try_catch, cause);
|
@@ -571,6 +596,7 @@ fail:
|
|
571
596
|
cause = st.err_reason ? st.err_reason : TERMINATED_ERROR;
|
572
597
|
st.err_reason = NO_ERROR;
|
573
598
|
}
|
599
|
+
if (bubble_up_ruby_exception(st, &try_catch)) return;
|
574
600
|
if (!cause && try_catch.HasCaught()) cause = RUNTIME_ERROR;
|
575
601
|
if (cause) result = v8::Undefined(st.isolate);
|
576
602
|
auto err = to_error(st, &try_catch, cause);
|
@@ -895,6 +921,7 @@ State::~State()
|
|
895
921
|
v8::Isolate::Scope isolate_scope(isolate);
|
896
922
|
persistent_safe_context.Reset();
|
897
923
|
persistent_context.Reset();
|
924
|
+
ruby_exception.Reset();
|
898
925
|
}
|
899
926
|
isolate->Dispose();
|
900
927
|
for (Callback *cb : callbacks)
|
@@ -194,17 +194,21 @@ static inline int r_zigzag(const uint8_t **p, const uint8_t *pe, int64_t *r)
|
|
194
194
|
return 0;
|
195
195
|
}
|
196
196
|
|
197
|
-
static
|
197
|
+
static void ser_init0(Ser *s)
|
198
198
|
{
|
199
199
|
memset(s, 0, sizeof(*s));
|
200
200
|
buf_init(&s->b);
|
201
|
+
}
|
202
|
+
|
203
|
+
static inline void ser_init(Ser *s)
|
204
|
+
{
|
205
|
+
ser_init0(s);
|
201
206
|
w(s, "\xFF\x0F", 2);
|
202
207
|
}
|
203
208
|
|
204
209
|
static void ser_init1(Ser *s, uint8_t c)
|
205
210
|
{
|
206
|
-
|
207
|
-
buf_init(&s->b);
|
211
|
+
ser_init0(s);
|
208
212
|
w_byte(s, c);
|
209
213
|
w(s, "\xFF\x0F", 2);
|
210
214
|
}
|
@@ -246,6 +246,10 @@ module MiniRacer
|
|
246
246
|
js_map_to_hash(value)
|
247
247
|
elsif map_iterator?(value)
|
248
248
|
value.map { |e| convert_js_to_ruby(e) }
|
249
|
+
elsif Polyglot::ForeignException === value
|
250
|
+
exc = MiniRacer::ScriptError.new(value.message)
|
251
|
+
exc.set_backtrace(value.backtrace)
|
252
|
+
exc
|
249
253
|
else
|
250
254
|
object = value
|
251
255
|
h = {}
|
data/lib/mini_racer/version.rb
CHANGED
data/lib/mini_racer.rb
CHANGED
@@ -51,6 +51,19 @@ module MiniRacer
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
+
class ScriptError < EvalError
|
55
|
+
def initialize(message)
|
56
|
+
message, *@frames = message.split("\n")
|
57
|
+
@frames.map! { "JavaScript #{_1.strip}" }
|
58
|
+
super(message)
|
59
|
+
end
|
60
|
+
|
61
|
+
def backtrace
|
62
|
+
frames = super || []
|
63
|
+
@frames + frames
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
54
67
|
class SnapshotError < Error
|
55
68
|
def initialize(message)
|
56
69
|
message, *@frames = message.split("\n")
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mini_racer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.19.
|
4
|
+
version: 0.19.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Saffron
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-10-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -137,9 +137,9 @@ licenses:
|
|
137
137
|
- MIT
|
138
138
|
metadata:
|
139
139
|
bug_tracker_uri: https://github.com/discourse/mini_racer/issues
|
140
|
-
changelog_uri: https://github.com/discourse/mini_racer/blob/v0.19.
|
141
|
-
documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.19.
|
142
|
-
source_code_uri: https://github.com/discourse/mini_racer/tree/v0.19.
|
140
|
+
changelog_uri: https://github.com/discourse/mini_racer/blob/v0.19.1/CHANGELOG
|
141
|
+
documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.19.1
|
142
|
+
source_code_uri: https://github.com/discourse/mini_racer/tree/v0.19.1
|
143
143
|
post_install_message:
|
144
144
|
rdoc_options: []
|
145
145
|
require_paths:
|