mini_racer 0.17.0.pre8 → 0.17.0.pre10
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/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:
|