mini_racer 0.17.0.pre8 → 0.17.0.pre10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +6 -0
- data/ext/mini_racer_extension/mini_racer_extension.c +88 -25
- data/ext/mini_racer_extension/mini_racer_v8.cc +95 -2
- data/ext/mini_racer_extension/serde.c +4 -2
- data/lib/mini_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: f09948c878aaefbc5e47285a3ee17db34e00de5d72e441cbb4ef45dd15e45f06
|
4
|
+
data.tar.gz: 95aa4a36b7612535524ff9ef6ca3116bde6dad9aa474c1c4b1af9427c0204969
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 76a9a93bcb3bdfc0f7073e6a5501d8c96bd993bb85fe321ac54114154f015c0b0f1e2ba21067f79787831cff604f34e82079913b593dab559f4cb74e54781f17
|
7
|
+
data.tar.gz: ea1da1f7e66915a147fe614cff9a6de80e2d5a2953eabb0de422c0ad2839db940cc9dbf2c5c642604b03fbd5ab71a6c0fd26e15abb38d4f4652273243a41221f
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
- 0.17.0.pr10 - 20-01-2025
|
2
|
+
- Added back support for partially deserialized objects (objects that do not translate across boundaries are returned as Error properties) - Ben Noordhuis
|
3
|
+
|
4
|
+
- 0.17.0.pre9 - 13-01-2025
|
5
|
+
- For backwards compatibility convert v8 return values to UTF-8 (invalidly encoded string still get returned using V8 encoding)
|
6
|
+
|
1
7
|
- 0.17.0.pre8 - 11-01-2025
|
2
8
|
- Fix handling of UTF 32 LE and Ascii encoding strings - Ben Noordhuis
|
3
9
|
- Handle rare edge case in V8 serialization - Ben Noordhuis
|
@@ -164,12 +164,6 @@ static VALUE js_function_class;
|
|
164
164
|
static pthread_mutex_t flags_mtx = PTHREAD_MUTEX_INITIALIZER;
|
165
165
|
static Buf flags; // protected by |flags_mtx|
|
166
166
|
|
167
|
-
struct rendezvous_nogvl
|
168
|
-
{
|
169
|
-
Context *context;
|
170
|
-
Buf *req, *res;
|
171
|
-
};
|
172
|
-
|
173
167
|
// arg == &(struct rendezvous_nogvl){...}
|
174
168
|
static void *rendezvous_callback(void *arg);
|
175
169
|
|
@@ -177,23 +171,38 @@ static void *rendezvous_callback(void *arg);
|
|
177
171
|
typedef struct State
|
178
172
|
{
|
179
173
|
VALUE a, b;
|
174
|
+
uint8_t verbatim_keys:1;
|
180
175
|
} State;
|
181
176
|
|
182
177
|
// note: must be stack-allocated or VALUEs won't be visible to ruby's GC
|
183
178
|
typedef struct DesCtx
|
184
179
|
{
|
185
180
|
State *tos;
|
186
|
-
VALUE refs; // array
|
181
|
+
VALUE refs; // object refs array
|
182
|
+
uint8_t transcode_latin1:1;
|
187
183
|
char err[64];
|
188
184
|
State stack[512];
|
189
185
|
} DesCtx;
|
190
186
|
|
187
|
+
struct rendezvous_nogvl
|
188
|
+
{
|
189
|
+
Context *context;
|
190
|
+
Buf *req, *res;
|
191
|
+
};
|
192
|
+
|
193
|
+
struct rendezvous_des
|
194
|
+
{
|
195
|
+
DesCtx *d;
|
196
|
+
Buf *res;
|
197
|
+
};
|
198
|
+
|
191
199
|
static void DesCtx_init(DesCtx *c)
|
192
200
|
{
|
193
201
|
c->tos = c->stack;
|
194
202
|
c->refs = rb_ary_new();
|
195
|
-
*c->tos = (State){Qundef, Qundef};
|
203
|
+
*c->tos = (State){Qundef, Qundef, /*verbatim_keys*/0};
|
196
204
|
*c->err = '\0';
|
205
|
+
c->transcode_latin1 = 1; // convert to utf8
|
197
206
|
}
|
198
207
|
|
199
208
|
static void put(DesCtx *c, VALUE v)
|
@@ -212,7 +221,8 @@ static void put(DesCtx *c, VALUE v)
|
|
212
221
|
if (*b == Qundef) {
|
213
222
|
*b = v;
|
214
223
|
} else {
|
215
|
-
|
224
|
+
if (!c->tos->verbatim_keys)
|
225
|
+
*b = rb_funcall(*b, rb_intern("to_s"), 0);
|
216
226
|
rb_hash_aset(*a, *b, v);
|
217
227
|
*b = Qundef;
|
218
228
|
}
|
@@ -234,7 +244,7 @@ static void push(DesCtx *c, VALUE v)
|
|
234
244
|
snprintf(c->err, sizeof(c->err), "stack overflow");
|
235
245
|
return;
|
236
246
|
}
|
237
|
-
*++c->tos = (State){v, Qundef};
|
247
|
+
*++c->tos = (State){v, Qundef, /*verbatim_keys*/0};
|
238
248
|
rb_ary_push(c->refs, v);
|
239
249
|
}
|
240
250
|
|
@@ -321,9 +331,22 @@ static void des_string(void *arg, const char *s, size_t n)
|
|
321
331
|
put(arg, rb_utf8_str_new(s, n));
|
322
332
|
}
|
323
333
|
|
334
|
+
static VALUE str_encode_bang(VALUE v)
|
335
|
+
{
|
336
|
+
// TODO cache these? this function can get called often
|
337
|
+
return rb_funcall(v, rb_intern("encode!"), 1, rb_str_new_cstr("UTF-8"));
|
338
|
+
}
|
339
|
+
|
324
340
|
static void des_string8(void *arg, const uint8_t *s, size_t n)
|
325
341
|
{
|
326
|
-
|
342
|
+
DesCtx *c;
|
343
|
+
VALUE v;
|
344
|
+
|
345
|
+
c = arg;
|
346
|
+
v = rb_enc_str_new((char *)s, n, rb_ascii8bit_encoding());
|
347
|
+
if (c->transcode_latin1)
|
348
|
+
v = str_encode_bang(v); // cannot fail
|
349
|
+
put(c, v);
|
327
350
|
}
|
328
351
|
|
329
352
|
// des_string16: |s| is not word aligned
|
@@ -331,7 +354,9 @@ static void des_string8(void *arg, const uint8_t *s, size_t n)
|
|
331
354
|
static void des_string16(void *arg, const void *s, size_t n)
|
332
355
|
{
|
333
356
|
rb_encoding *e;
|
357
|
+
VALUE v, r;
|
334
358
|
DesCtx *c;
|
359
|
+
int exc;
|
335
360
|
|
336
361
|
c = arg;
|
337
362
|
if (*c->err)
|
@@ -344,7 +369,15 @@ static void des_string16(void *arg, const void *s, size_t n)
|
|
344
369
|
snprintf(c->err, sizeof(c->err), "no UTF16-LE encoding");
|
345
370
|
return;
|
346
371
|
}
|
347
|
-
|
372
|
+
v = rb_enc_str_new((char *)s, n, e);
|
373
|
+
// JS strings can contain unmatched or illegal surrogate pairs
|
374
|
+
// that Ruby won't decode; return the string as-is in that case
|
375
|
+
r = rb_protect(str_encode_bang, v, &exc);
|
376
|
+
if (exc) {
|
377
|
+
rb_set_errinfo(Qnil);
|
378
|
+
r = v;
|
379
|
+
}
|
380
|
+
put(c, r);
|
348
381
|
}
|
349
382
|
|
350
383
|
// ruby doesn't really have a concept of a byte array so store it as
|
@@ -395,6 +428,20 @@ static void des_object_end(void *arg)
|
|
395
428
|
pop(arg);
|
396
429
|
}
|
397
430
|
|
431
|
+
static void des_map_begin(void *arg)
|
432
|
+
{
|
433
|
+
DesCtx *c;
|
434
|
+
|
435
|
+
c = arg;
|
436
|
+
push(c, rb_hash_new());
|
437
|
+
c->tos->verbatim_keys = 1; // don't stringify or intern keys
|
438
|
+
}
|
439
|
+
|
440
|
+
static void des_map_end(void *arg)
|
441
|
+
{
|
442
|
+
pop(arg);
|
443
|
+
}
|
444
|
+
|
398
445
|
static void des_object_ref(void *arg, uint32_t id)
|
399
446
|
{
|
400
447
|
DesCtx *c;
|
@@ -740,25 +787,25 @@ static void *v8_thread_start(void *arg)
|
|
740
787
|
return NULL;
|
741
788
|
}
|
742
789
|
|
743
|
-
static VALUE deserialize1(const uint8_t *p, size_t n)
|
790
|
+
static VALUE deserialize1(DesCtx *d, const uint8_t *p, size_t n)
|
744
791
|
{
|
745
792
|
char err[64];
|
746
|
-
DesCtx d;
|
747
793
|
|
748
|
-
|
749
|
-
if (des(&err, p, n, &d))
|
794
|
+
if (des(&err, p, n, d))
|
750
795
|
rb_raise(runtime_error, "%s", err);
|
751
|
-
if (d
|
796
|
+
if (d->tos != d->stack) // should not happen
|
752
797
|
rb_raise(runtime_error, "parse stack not empty");
|
753
|
-
return d
|
798
|
+
return d->tos->a;
|
754
799
|
}
|
755
800
|
|
756
801
|
static VALUE deserialize(VALUE arg)
|
757
802
|
{
|
803
|
+
struct rendezvous_des *a;
|
758
804
|
Buf *b;
|
759
805
|
|
760
|
-
|
761
|
-
|
806
|
+
a = (void *)arg;
|
807
|
+
b = a->res;
|
808
|
+
return deserialize1(a->d, b->buf, b->len);
|
762
809
|
}
|
763
810
|
|
764
811
|
// called with |rr_mtx| and GVL held; can raise exception
|
@@ -767,6 +814,7 @@ static VALUE rendezvous_callback_do(VALUE arg)
|
|
767
814
|
struct rendezvous_nogvl *a;
|
768
815
|
VALUE func, args;
|
769
816
|
Context *c;
|
817
|
+
DesCtx d;
|
770
818
|
Buf *b;
|
771
819
|
|
772
820
|
a = (void *)arg;
|
@@ -774,7 +822,8 @@ static VALUE rendezvous_callback_do(VALUE arg)
|
|
774
822
|
c = a->context;
|
775
823
|
assert(b->len > 0);
|
776
824
|
assert(*b->buf == 'c');
|
777
|
-
|
825
|
+
DesCtx_init(&d);
|
826
|
+
args = deserialize1(&d, b->buf+1, b->len-1); // skip 'c' marker
|
778
827
|
func = rb_ary_pop(args); // callback id
|
779
828
|
func = rb_ary_entry(c->procs, FIX2LONG(func));
|
780
829
|
return rb_funcall2(func, rb_intern("call"), RARRAY_LENINT(args), RARRAY_PTR(args));
|
@@ -866,14 +915,14 @@ static void rendezvous_no_des(Context *c, Buf *req, Buf *res)
|
|
866
915
|
|
867
916
|
// send request to & receive reply from v8 thread; takes ownership of |req|
|
868
917
|
// can raise exceptions and longjmp away but won't leak |req|
|
869
|
-
static VALUE
|
918
|
+
static VALUE rendezvous1(Context *c, Buf *req, DesCtx *d)
|
870
919
|
{
|
871
920
|
VALUE r;
|
872
921
|
Buf res;
|
873
922
|
int exc;
|
874
923
|
|
875
924
|
rendezvous_no_des(c, req, &res); // takes ownership of |req|
|
876
|
-
r = rb_protect(deserialize, (VALUE)&res, &exc);
|
925
|
+
r = rb_protect(deserialize, (VALUE)&(struct rendezvous_des){d, &res}, &exc);
|
877
926
|
buf_reset(&res);
|
878
927
|
if (exc) {
|
879
928
|
r = rb_errinfo();
|
@@ -888,6 +937,14 @@ static VALUE rendezvous(Context *c, Buf *req)
|
|
888
937
|
return r;
|
889
938
|
}
|
890
939
|
|
940
|
+
static VALUE rendezvous(Context *c, Buf *req)
|
941
|
+
{
|
942
|
+
DesCtx d;
|
943
|
+
|
944
|
+
DesCtx_init(&d);
|
945
|
+
return rendezvous1(c, req, &d);
|
946
|
+
}
|
947
|
+
|
891
948
|
static void handle_exception(VALUE e)
|
892
949
|
{
|
893
950
|
const char *s;
|
@@ -1469,6 +1526,7 @@ static VALUE snapshot_initialize(int argc, VALUE *argv, VALUE self)
|
|
1469
1526
|
VALUE a, e, code, cv;
|
1470
1527
|
Snapshot *ss;
|
1471
1528
|
Context *c;
|
1529
|
+
DesCtx d;
|
1472
1530
|
Ser s;
|
1473
1531
|
|
1474
1532
|
TypedData_Get_Struct(self, Snapshot, &snapshot_type, ss);
|
@@ -1483,7 +1541,9 @@ static VALUE snapshot_initialize(int argc, VALUE *argv, VALUE self)
|
|
1483
1541
|
ser_init1(&s, 'T');
|
1484
1542
|
add_string(&s, code);
|
1485
1543
|
// response is [arraybuffer, error]
|
1486
|
-
|
1544
|
+
DesCtx_init(&d);
|
1545
|
+
d.transcode_latin1 = 0; // don't mangle snapshot binary data
|
1546
|
+
a = rendezvous1(c, &s.b, &d);
|
1487
1547
|
e = rb_ary_pop(a);
|
1488
1548
|
context_dispose(cv);
|
1489
1549
|
if (*RSTRING_PTR(e))
|
@@ -1497,6 +1557,7 @@ static VALUE snapshot_warmup(VALUE self, VALUE arg)
|
|
1497
1557
|
VALUE a, e, cv;
|
1498
1558
|
Snapshot *ss;
|
1499
1559
|
Context *c;
|
1560
|
+
DesCtx d;
|
1500
1561
|
Ser s;
|
1501
1562
|
|
1502
1563
|
TypedData_Get_Struct(self, Snapshot, &snapshot_type, ss);
|
@@ -1511,7 +1572,9 @@ static VALUE snapshot_warmup(VALUE self, VALUE arg)
|
|
1511
1572
|
add_string(&s, arg);
|
1512
1573
|
ser_array_end(&s, 2);
|
1513
1574
|
// response is [arraybuffer, error]
|
1514
|
-
|
1575
|
+
DesCtx_init(&d);
|
1576
|
+
d.transcode_latin1 = 0; // don't mangle snapshot binary data
|
1577
|
+
a = rendezvous1(c, &s.b, &d);
|
1515
1578
|
e = rb_ary_pop(a);
|
1516
1579
|
context_dispose(cv);
|
1517
1580
|
if (*RSTRING_PTR(e))
|
@@ -11,6 +11,56 @@
|
|
11
11
|
#include <cstring>
|
12
12
|
#include <vector>
|
13
13
|
|
14
|
+
// note: the filter function gets called inside the safe context,
|
15
|
+
// i.e., the context that has not been tampered with by user JS
|
16
|
+
// convention: $-prefixed identifiers signify objects from the
|
17
|
+
// user JS context and should be handled with special care
|
18
|
+
static const char safe_context_script_source[] = R"js(
|
19
|
+
;(function($globalThis) {
|
20
|
+
const {Map: $Map, Set: $Set} = $globalThis
|
21
|
+
const sentinel = {}
|
22
|
+
return function filter(v) {
|
23
|
+
if (typeof v === "function")
|
24
|
+
return sentinel
|
25
|
+
if (typeof v !== "object" || v === null)
|
26
|
+
return v
|
27
|
+
if (v instanceof $Map) {
|
28
|
+
const m = new Map()
|
29
|
+
for (let [k, t] of Map.prototype.entries.call(v)) {
|
30
|
+
t = filter(t)
|
31
|
+
if (t !== sentinel)
|
32
|
+
m.set(k, t)
|
33
|
+
}
|
34
|
+
return m
|
35
|
+
} else if (v instanceof $Set) {
|
36
|
+
const s = new Set()
|
37
|
+
for (let t of Set.prototype.values.call(v)) {
|
38
|
+
t = filter(t)
|
39
|
+
if (t !== sentinel)
|
40
|
+
s.add(t)
|
41
|
+
}
|
42
|
+
return s
|
43
|
+
} else {
|
44
|
+
const o = Array.isArray(v) ? [] : {}
|
45
|
+
const pds = Object.getOwnPropertyDescriptors(v)
|
46
|
+
for (const [k, d] of Object.entries(pds)) {
|
47
|
+
if (!d.enumerable)
|
48
|
+
continue
|
49
|
+
let t = d.value
|
50
|
+
if (d.get) {
|
51
|
+
// *not* d.get.call(...), may have been tampered with
|
52
|
+
t = Function.prototype.call.call(d.get, v, k)
|
53
|
+
}
|
54
|
+
t = filter(t)
|
55
|
+
if (t !== sentinel)
|
56
|
+
Object.defineProperty(o, k, {value: t, enumerable: true})
|
57
|
+
}
|
58
|
+
return o
|
59
|
+
}
|
60
|
+
}
|
61
|
+
})
|
62
|
+
)js";
|
63
|
+
|
14
64
|
struct Callback
|
15
65
|
{
|
16
66
|
struct State *st;
|
@@ -32,8 +82,10 @@ struct State
|
|
32
82
|
// extra context for when we need access to built-ins like Array
|
33
83
|
// and want to be sure they haven't been tampered with by JS code
|
34
84
|
v8::Local<v8::Context> safe_context;
|
35
|
-
v8::
|
36
|
-
v8::Persistent<v8::Context>
|
85
|
+
v8::Local<v8::Function> safe_context_function;
|
86
|
+
v8::Persistent<v8::Context> persistent_context; // single-thread mode only
|
87
|
+
v8::Persistent<v8::Context> persistent_safe_context; // single-thread mode only
|
88
|
+
v8::Persistent<v8::Function> persistent_safe_context_function; // single-thread mode only
|
37
89
|
Context *ruby_context;
|
38
90
|
int64_t max_memory;
|
39
91
|
int err_reason;
|
@@ -73,6 +125,23 @@ struct Serialized
|
|
73
125
|
// throws JS exception on serialization error
|
74
126
|
bool reply(State& st, v8::Local<v8::Value> v)
|
75
127
|
{
|
128
|
+
v8::TryCatch try_catch(st.isolate);
|
129
|
+
{
|
130
|
+
Serialized serialized(st, v);
|
131
|
+
if (serialized.data) {
|
132
|
+
v8_reply(st.ruby_context, serialized.data, serialized.size);
|
133
|
+
return true;
|
134
|
+
}
|
135
|
+
}
|
136
|
+
if (!try_catch.CanContinue()) {
|
137
|
+
try_catch.ReThrow();
|
138
|
+
return false;
|
139
|
+
}
|
140
|
+
auto recv = v8::Undefined(st.isolate);
|
141
|
+
if (!st.safe_context_function->Call(st.safe_context, recv, 1, &v).ToLocal(&v)) {
|
142
|
+
try_catch.ReThrow();
|
143
|
+
return false;
|
144
|
+
}
|
76
145
|
Serialized serialized(st, v);
|
77
146
|
if (serialized.data)
|
78
147
|
v8_reply(st.ruby_context, serialized.data, serialized.size);
|
@@ -240,7 +309,29 @@ extern "C" State *v8_thread_init(Context *c, const uint8_t *snapshot_buf,
|
|
240
309
|
st.safe_context = v8::Context::New(st.isolate);
|
241
310
|
st.context = v8::Context::New(st.isolate);
|
242
311
|
v8::Context::Scope context_scope(st.context);
|
312
|
+
{
|
313
|
+
v8::Context::Scope context_scope(st.safe_context);
|
314
|
+
auto source = v8::String::NewFromUtf8Literal(st.isolate, safe_context_script_source);
|
315
|
+
auto filename = v8::String::NewFromUtf8Literal(st.isolate, "safe_context_script.js");
|
316
|
+
v8::ScriptOrigin origin(filename);
|
317
|
+
auto script =
|
318
|
+
v8::Script::Compile(st.safe_context, source, &origin)
|
319
|
+
.ToLocalChecked();
|
320
|
+
auto function_v = script->Run(st.safe_context).ToLocalChecked();
|
321
|
+
auto function = v8::Function::Cast(*function_v);
|
322
|
+
auto recv = v8::Undefined(st.isolate);
|
323
|
+
v8::Local<v8::Value> arg = st.context->Global();
|
324
|
+
// grant the safe context access to the user context's globalThis
|
325
|
+
st.safe_context->SetSecurityToken(st.context->GetSecurityToken());
|
326
|
+
function_v =
|
327
|
+
function->Call(st.safe_context, recv, 1, &arg)
|
328
|
+
.ToLocalChecked();
|
329
|
+
// revoke access again now that the script did its one-time setup
|
330
|
+
st.safe_context->UseDefaultSecurityToken();
|
331
|
+
st.safe_context_function = v8::Local<v8::Function>::Cast(function_v);
|
332
|
+
}
|
243
333
|
if (single_threaded) {
|
334
|
+
st.persistent_safe_context_function.Reset(st.isolate, st.safe_context_function);
|
244
335
|
st.persistent_safe_context.Reset(st.isolate, st.safe_context);
|
245
336
|
st.persistent_context.Reset(st.isolate, st.context);
|
246
337
|
return pst; // intentionally returning early and keeping alive
|
@@ -792,12 +883,14 @@ extern "C" void v8_single_threaded_enter(State *pst, Context *c, void (*f)(Conte
|
|
792
883
|
v8::Isolate::Scope isolate_scope(st.isolate);
|
793
884
|
v8::HandleScope handle_scope(st.isolate);
|
794
885
|
{
|
886
|
+
st.safe_context_function = v8::Local<v8::Function>::New(st.isolate, st.persistent_safe_context_function);
|
795
887
|
st.safe_context = v8::Local<v8::Context>::New(st.isolate, st.persistent_safe_context);
|
796
888
|
st.context = v8::Local<v8::Context>::New(st.isolate, st.persistent_context);
|
797
889
|
v8::Context::Scope context_scope(st.context);
|
798
890
|
f(c);
|
799
891
|
st.context = v8::Local<v8::Context>();
|
800
892
|
st.safe_context = v8::Local<v8::Context>();
|
893
|
+
st.safe_context_function = v8::Local<v8::Function>();
|
801
894
|
}
|
802
895
|
}
|
803
896
|
|
@@ -31,6 +31,8 @@ static void des_named_props_begin(void *arg);
|
|
31
31
|
static void des_named_props_end(void *arg);
|
32
32
|
static void des_object_begin(void *arg);
|
33
33
|
static void des_object_end(void *arg);
|
34
|
+
static void des_map_begin(void *arg);
|
35
|
+
static void des_map_end(void *arg);
|
34
36
|
static void des_object_ref(void *arg, uint32_t id);
|
35
37
|
// des_error_begin: followed by des_object_begin + des_object_end calls
|
36
38
|
static void des_error_begin(void *arg);
|
@@ -642,7 +644,7 @@ again:
|
|
642
644
|
des_object_end(arg);
|
643
645
|
break;
|
644
646
|
case ';': // Map
|
645
|
-
|
647
|
+
des_map_begin(arg);
|
646
648
|
for (u = 0; /*empty*/; u++) {
|
647
649
|
if (*p >= pe)
|
648
650
|
goto too_short;
|
@@ -658,7 +660,7 @@ again:
|
|
658
660
|
goto bad_varint;
|
659
661
|
if (t != 2*u)
|
660
662
|
return bail(err, "map element count mismatch");
|
661
|
-
|
663
|
+
des_map_end(arg);
|
662
664
|
break;
|
663
665
|
case '\'': // Set
|
664
666
|
des_array_begin(arg);
|
data/lib/mini_racer/version.rb
CHANGED
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.17.0.
|
4
|
+
version: 0.17.0.pre10
|
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-01-
|
11
|
+
date: 2025-01-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -123,9 +123,9 @@ licenses:
|
|
123
123
|
- MIT
|
124
124
|
metadata:
|
125
125
|
bug_tracker_uri: https://github.com/discourse/mini_racer/issues
|
126
|
-
changelog_uri: https://github.com/discourse/mini_racer/blob/v0.17.0.
|
127
|
-
documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.17.0.
|
128
|
-
source_code_uri: https://github.com/discourse/mini_racer/tree/v0.17.0.
|
126
|
+
changelog_uri: https://github.com/discourse/mini_racer/blob/v0.17.0.pre10/CHANGELOG
|
127
|
+
documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.17.0.pre10
|
128
|
+
source_code_uri: https://github.com/discourse/mini_racer/tree/v0.17.0.pre10
|
129
129
|
post_install_message:
|
130
130
|
rdoc_options: []
|
131
131
|
require_paths:
|