mini_racer 0.21.3 → 0.21.4
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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f2e290a77bd6b38286dc4e4224d71579a1e8e41bbb8fd70029584f518383ba7f
|
|
4
|
+
data.tar.gz: 479d9793ade9c46075986dd53c0a7ebacf62e1afb199fc6983e5b7c4fb4e0faa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dc69216098ec6076dc90cf731fc5fd229526a4c21d3383fdb94448aace72068f36fc6bf6910cb09d8f42bf57c6926bf1527a2fd0871c11c5b08df820643cc23e
|
|
7
|
+
data.tar.gz: b7b4b851b4cbc9ad35c56e39db1d5c7ccc19b23dabd9dd87f084ba37f0a59fb584c833128827d58ecdb0c99f75969383333d3b304d00f816ba544e83e5436639
|
data/CHANGELOG
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
- 0.21.4 - 24-06-2026
|
|
2
|
+
- Fix stale V8 termination state after interrupts/timeouts so contexts remain usable after cancelled evaluations
|
|
3
|
+
- Let Ruby interrupts wake MiniRacer calls without immediately terminating V8, allowing signal traps and nested callbacks to unwind safely
|
|
4
|
+
- Add benchmark suite covering eval, serialization/deserialization, and transpilation workloads
|
|
5
|
+
|
|
1
6
|
- 0.21.3 - 18-06-2026
|
|
2
7
|
- Fix `:single_threaded` contexts inherited across `fork` by recovering idle reusable native runners in the child process without falling back to per-dispatch native thread spawning
|
|
3
8
|
- Avoid intermittent heap corruption during `:single_threaded` context finalization, especially when forked children exit normally after touching inherited contexts
|
|
@@ -137,6 +137,8 @@ typedef struct Context
|
|
|
137
137
|
VALUE procs; // array of js -> ruby callbacks
|
|
138
138
|
VALUE exception; // pending exception or Qnil
|
|
139
139
|
Buf req, res; // ruby->v8 request/response, mediated by |mtx| and |cv|
|
|
140
|
+
Buf v8_req; // stable v8-side copy of a request returned by v8_roundtrip
|
|
141
|
+
int res_ready; // protected by |mtx|; response may be filled before ready
|
|
140
142
|
Buf snapshot;
|
|
141
143
|
pthread_t single_threaded_thr;
|
|
142
144
|
pid_t single_threaded_pid;
|
|
@@ -230,6 +232,8 @@ struct rendezvous_nogvl
|
|
|
230
232
|
Context *context;
|
|
231
233
|
Buf *req, *res;
|
|
232
234
|
atomic_int active;
|
|
235
|
+
atomic_int interrupted;
|
|
236
|
+
int started, finished, has_rr_mtx;
|
|
233
237
|
};
|
|
234
238
|
|
|
235
239
|
struct rendezvous_des
|
|
@@ -822,11 +826,28 @@ static void dispatch1(Context *c, const uint8_t *p, size_t n)
|
|
|
822
826
|
fflush(stderr);
|
|
823
827
|
}
|
|
824
828
|
|
|
825
|
-
static void
|
|
829
|
+
static void dispatch_buf(Context *c, Buf *req)
|
|
826
830
|
{
|
|
831
|
+
Buf local_req;
|
|
832
|
+
|
|
833
|
+
// Called with Context.mtx held. Release it while V8 runs so an rb_nogvl
|
|
834
|
+
// UBF can wake the Ruby thread to process interrupts with
|
|
835
|
+
// rb_thread_check_ints(), without forcing termination first. Move the
|
|
836
|
+
// request out first so cancellation cannot free a buffer V8 is reading.
|
|
837
|
+
buf_move(req, &local_req);
|
|
827
838
|
buf_reset(&c->res);
|
|
828
|
-
|
|
829
|
-
|
|
839
|
+
c->res_ready = 0;
|
|
840
|
+
pthread_mutex_unlock(&c->mtx);
|
|
841
|
+
dispatch1(c, local_req.buf, local_req.len);
|
|
842
|
+
pthread_mutex_lock(&c->mtx);
|
|
843
|
+
buf_reset(&local_req);
|
|
844
|
+
c->res_ready = 1;
|
|
845
|
+
pthread_cond_signal(&c->cv);
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
static void dispatch(Context *c)
|
|
849
|
+
{
|
|
850
|
+
dispatch_buf(c, &c->req);
|
|
830
851
|
}
|
|
831
852
|
|
|
832
853
|
// called by v8_isolate_and_context
|
|
@@ -859,19 +880,22 @@ void v8_thread_main(Context *c, struct State *pst)
|
|
|
859
880
|
}
|
|
860
881
|
}
|
|
861
882
|
|
|
862
|
-
// called by v8_thread_main and from mini_racer_v8.cc
|
|
863
|
-
// in all cases with Context.mtx held
|
|
883
|
+
// called by v8_thread_main and from mini_racer_v8.cc
|
|
864
884
|
void v8_dispatch(Context *c)
|
|
865
885
|
{
|
|
866
|
-
|
|
867
|
-
|
|
886
|
+
pthread_mutex_lock(&c->mtx);
|
|
887
|
+
dispatch_buf(c, &c->v8_req);
|
|
888
|
+
pthread_mutex_unlock(&c->mtx);
|
|
868
889
|
}
|
|
869
890
|
|
|
870
|
-
// called from mini_racer_v8.cc with Context.mtx held
|
|
871
891
|
// only called when inside v8_call, v8_eval, or v8_pump_message_loop
|
|
872
892
|
void v8_roundtrip(Context *c, const uint8_t **p, size_t *n)
|
|
873
893
|
{
|
|
894
|
+
pthread_mutex_lock(&c->mtx);
|
|
895
|
+
buf_reset(&c->v8_req);
|
|
874
896
|
buf_reset(&c->req);
|
|
897
|
+
if (c->res.len)
|
|
898
|
+
c->res_ready = 1;
|
|
875
899
|
pthread_cond_signal(&c->cv);
|
|
876
900
|
while (!c->req.len && !atomic_load(&c->quit))
|
|
877
901
|
pthread_cond_wait(&c->cv, &c->mtx);
|
|
@@ -879,17 +903,22 @@ void v8_roundtrip(Context *c, const uint8_t **p, size_t *n)
|
|
|
879
903
|
static const uint8_t disposed[] = "edisposed context";
|
|
880
904
|
*p = disposed;
|
|
881
905
|
*n = sizeof(disposed) - 1;
|
|
906
|
+
pthread_mutex_unlock(&c->mtx);
|
|
882
907
|
return;
|
|
883
908
|
}
|
|
884
909
|
buf_reset(&c->res);
|
|
885
|
-
|
|
886
|
-
|
|
910
|
+
c->res_ready = 0;
|
|
911
|
+
buf_move(&c->req, &c->v8_req);
|
|
912
|
+
*p = c->v8_req.buf;
|
|
913
|
+
*n = c->v8_req.len;
|
|
914
|
+
pthread_mutex_unlock(&c->mtx);
|
|
887
915
|
}
|
|
888
916
|
|
|
889
|
-
// called from mini_racer_v8.cc with Context.mtx held
|
|
890
917
|
void v8_reply(Context *c, const uint8_t *p, size_t n)
|
|
891
918
|
{
|
|
919
|
+
pthread_mutex_lock(&c->mtx);
|
|
892
920
|
buf_put(&c->res, p, n);
|
|
921
|
+
pthread_mutex_unlock(&c->mtx);
|
|
893
922
|
}
|
|
894
923
|
|
|
895
924
|
static void v8_once_init(void)
|
|
@@ -1059,6 +1088,19 @@ static int single_threaded_runner_start(Context *c)
|
|
|
1059
1088
|
return r;
|
|
1060
1089
|
}
|
|
1061
1090
|
|
|
1091
|
+
static void rendezvous_release(struct rendezvous_nogvl *a)
|
|
1092
|
+
{
|
|
1093
|
+
Context *c;
|
|
1094
|
+
|
|
1095
|
+
atomic_store(&a->active, 0);
|
|
1096
|
+
if (!a->has_rr_mtx)
|
|
1097
|
+
return;
|
|
1098
|
+
c = a->context;
|
|
1099
|
+
c->depth--;
|
|
1100
|
+
a->has_rr_mtx = 0;
|
|
1101
|
+
pthread_mutex_unlock(&c->rr_mtx);
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1062
1104
|
static inline void *rendezvous_nogvl(void *arg)
|
|
1063
1105
|
{
|
|
1064
1106
|
struct rendezvous_nogvl *a;
|
|
@@ -1067,62 +1109,77 @@ static inline void *rendezvous_nogvl(void *arg)
|
|
|
1067
1109
|
|
|
1068
1110
|
a = arg;
|
|
1069
1111
|
c = a->context;
|
|
1070
|
-
if (
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1112
|
+
if (!a->started) {
|
|
1113
|
+
if (single_threaded && (r = single_threaded_recover_after_fork(c)))
|
|
1114
|
+
return (void *)(intptr_t)r;
|
|
1115
|
+
pthread_mutex_lock(&c->rr_mtx);
|
|
1116
|
+
a->has_rr_mtx = 1;
|
|
1117
|
+
if (c->depth > 0 && c->depth%50 == 0) { // TODO stop steep recursion
|
|
1118
|
+
fprintf(stderr, "mini_racer: deep js->ruby->js recursion, depth=%d\n", c->depth);
|
|
1119
|
+
fflush(stderr);
|
|
1120
|
+
}
|
|
1121
|
+
c->depth++;
|
|
1122
|
+
a->started = 1;
|
|
1077
1123
|
}
|
|
1078
|
-
|
|
1124
|
+
|
|
1079
1125
|
next:
|
|
1126
|
+
atomic_store(&a->active, 1);
|
|
1080
1127
|
pthread_mutex_lock(&c->mtx);
|
|
1081
1128
|
if (atomic_load(&c->quit)) {
|
|
1082
1129
|
buf_reset(a->req);
|
|
1083
1130
|
pthread_mutex_unlock(&c->mtx);
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
pthread_mutex_unlock(&c->rr_mtx);
|
|
1131
|
+
a->finished = 1;
|
|
1132
|
+
rendezvous_release(a);
|
|
1087
1133
|
return (void *)(intptr_t)ECANCELED;
|
|
1088
1134
|
}
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1135
|
+
if (a->req->len) {
|
|
1136
|
+
assert(c->req.len == 0);
|
|
1137
|
+
assert(!c->res_ready);
|
|
1138
|
+
buf_move(a->req, &c->req); // v8 thread takes ownership of req
|
|
1139
|
+
if (single_threaded) {
|
|
1140
|
+
r = single_threaded_runner_start(c);
|
|
1141
|
+
if (r) {
|
|
1142
|
+
buf_move(&c->req, a->req);
|
|
1143
|
+
pthread_mutex_unlock(&c->mtx);
|
|
1144
|
+
a->finished = 1;
|
|
1145
|
+
rendezvous_release(a);
|
|
1146
|
+
return (void *)(intptr_t)r;
|
|
1147
|
+
}
|
|
1101
1148
|
}
|
|
1102
1149
|
pthread_cond_signal(&c->cv);
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1150
|
+
}
|
|
1151
|
+
while (!c->res_ready && !atomic_load(&a->interrupted) && !atomic_load(&c->quit))
|
|
1152
|
+
pthread_cond_wait(&c->cv, &c->mtx);
|
|
1153
|
+
if (!c->res_ready && atomic_load(&a->interrupted)) {
|
|
1154
|
+
atomic_store(&a->active, 0);
|
|
1155
|
+
pthread_mutex_unlock(&c->mtx);
|
|
1156
|
+
return (void *)(intptr_t)EINTR;
|
|
1157
|
+
}
|
|
1158
|
+
if (!c->res_ready && atomic_load(&c->quit)) {
|
|
1159
|
+
buf_reset(a->req);
|
|
1160
|
+
pthread_mutex_unlock(&c->mtx);
|
|
1161
|
+
a->finished = 1;
|
|
1162
|
+
rendezvous_release(a);
|
|
1163
|
+
return (void *)(intptr_t)ECANCELED;
|
|
1107
1164
|
}
|
|
1108
1165
|
buf_move(&c->res, a->res);
|
|
1166
|
+
c->res_ready = 0;
|
|
1109
1167
|
pthread_cond_broadcast(&c->cv);
|
|
1110
1168
|
pthread_mutex_unlock(&c->mtx);
|
|
1169
|
+
atomic_store(&a->active, 0);
|
|
1111
1170
|
if (*a->res->buf == 'c') { // js -> ruby callback?
|
|
1112
1171
|
rb_thread_call_with_gvl(rendezvous_callback, a);
|
|
1113
1172
|
buf_reset(a->res);
|
|
1114
1173
|
if (atomic_load(&c->quit)) {
|
|
1115
1174
|
buf_reset(a->req);
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
pthread_mutex_unlock(&c->rr_mtx);
|
|
1175
|
+
a->finished = 1;
|
|
1176
|
+
rendezvous_release(a);
|
|
1119
1177
|
return (void *)(intptr_t)ECANCELED;
|
|
1120
1178
|
}
|
|
1121
1179
|
goto next;
|
|
1122
1180
|
}
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
pthread_mutex_unlock(&c->rr_mtx);
|
|
1181
|
+
a->finished = 1;
|
|
1182
|
+
rendezvous_release(a);
|
|
1126
1183
|
return NULL;
|
|
1127
1184
|
}
|
|
1128
1185
|
|
|
@@ -1134,9 +1191,8 @@ static void rendezvous_ubf(void *arg)
|
|
|
1134
1191
|
a = arg;
|
|
1135
1192
|
if (!atomic_load(&a->active))
|
|
1136
1193
|
return;
|
|
1194
|
+
atomic_store(&a->interrupted, 1);
|
|
1137
1195
|
c = a->context;
|
|
1138
|
-
if (c->pst)
|
|
1139
|
-
v8_terminate_execution(c->pst);
|
|
1140
1196
|
pthread_cond_broadcast(&c->cv);
|
|
1141
1197
|
}
|
|
1142
1198
|
|
|
@@ -1150,9 +1206,82 @@ static void terminate_ubf(void *arg)
|
|
|
1150
1206
|
pthread_cond_broadcast(&c->cv);
|
|
1151
1207
|
}
|
|
1152
1208
|
|
|
1209
|
+
static void *rendezvous_cancel_nogvl(void *arg)
|
|
1210
|
+
{
|
|
1211
|
+
// Reply to any pending JS->Ruby callback with an 'e' marker plus message
|
|
1212
|
+
// so V8 throws, unwinds, and reaches its normal termination cleanup.
|
|
1213
|
+
static const uint8_t terminated[] = "eterminated";
|
|
1214
|
+
struct rendezvous_nogvl *a;
|
|
1215
|
+
Context *c;
|
|
1216
|
+
|
|
1217
|
+
a = arg;
|
|
1218
|
+
c = a->context;
|
|
1219
|
+
atomic_store(&a->active, 0);
|
|
1220
|
+
if (c->pst)
|
|
1221
|
+
v8_terminate_execution(c->pst);
|
|
1222
|
+
pthread_mutex_lock(&c->mtx);
|
|
1223
|
+
pthread_cond_broadcast(&c->cv);
|
|
1224
|
+
while (!atomic_load(&c->quit)) {
|
|
1225
|
+
while (!c->res_ready && !atomic_load(&c->quit))
|
|
1226
|
+
pthread_cond_wait(&c->cv, &c->mtx);
|
|
1227
|
+
if (!c->res_ready)
|
|
1228
|
+
break;
|
|
1229
|
+
if (c->res.len && *c->res.buf != 'c')
|
|
1230
|
+
break;
|
|
1231
|
+
buf_reset(&c->res);
|
|
1232
|
+
c->res_ready = 0;
|
|
1233
|
+
buf_reset(&c->req);
|
|
1234
|
+
buf_put(&c->req, terminated, sizeof(terminated) - 1);
|
|
1235
|
+
pthread_cond_signal(&c->cv);
|
|
1236
|
+
}
|
|
1237
|
+
buf_reset(&c->req);
|
|
1238
|
+
buf_reset(&c->res);
|
|
1239
|
+
buf_reset(&c->v8_req);
|
|
1240
|
+
c->res_ready = 0;
|
|
1241
|
+
pthread_cond_broadcast(&c->cv);
|
|
1242
|
+
pthread_mutex_unlock(&c->mtx);
|
|
1243
|
+
if (c->pst)
|
|
1244
|
+
v8_cancel_terminate_execution(c->pst);
|
|
1245
|
+
a->finished = 1;
|
|
1246
|
+
rendezvous_release(a);
|
|
1247
|
+
return NULL;
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
static VALUE rendezvous_no_des_body(VALUE arg)
|
|
1251
|
+
{
|
|
1252
|
+
struct rendezvous_nogvl *a;
|
|
1253
|
+
void *r;
|
|
1254
|
+
|
|
1255
|
+
a = (void *)arg;
|
|
1256
|
+
for (;;) {
|
|
1257
|
+
// Let the UBF wake this wait without deciding why Ruby interrupted it.
|
|
1258
|
+
// Back under the GVL, rb_thread_check_ints() runs signal traps and
|
|
1259
|
+
// raises real asynchronous exceptions (Timeout, Thread#raise, kill).
|
|
1260
|
+
atomic_store(&a->interrupted, 0);
|
|
1261
|
+
atomic_store(&a->active, 1);
|
|
1262
|
+
r = rb_nogvl(rendezvous_nogvl, a, rendezvous_ubf, a, RB_NOGVL_INTR_FAIL);
|
|
1263
|
+
atomic_store(&a->active, 0);
|
|
1264
|
+
if (a->finished || (r && (int)(intptr_t)r != EINTR))
|
|
1265
|
+
return LONG2NUM((long)(intptr_t)r);
|
|
1266
|
+
rb_thread_check_ints();
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
static VALUE rendezvous_no_des_ensure(VALUE arg)
|
|
1271
|
+
{
|
|
1272
|
+
struct rendezvous_nogvl *a;
|
|
1273
|
+
|
|
1274
|
+
a = (void *)arg;
|
|
1275
|
+
if (a->started && !a->finished)
|
|
1276
|
+
rb_nogvl(rendezvous_cancel_nogvl, a, NULL, NULL, 0);
|
|
1277
|
+
buf_reset(a->req);
|
|
1278
|
+
return Qnil;
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1153
1281
|
static void rendezvous_no_des(Context *c, Buf *req, Buf *res)
|
|
1154
1282
|
{
|
|
1155
1283
|
void *r;
|
|
1284
|
+
VALUE rv;
|
|
1156
1285
|
struct rendezvous_nogvl a;
|
|
1157
1286
|
|
|
1158
1287
|
if (atomic_load(&c->quit)) {
|
|
@@ -1163,7 +1292,15 @@ static void rendezvous_no_des(Context *c, Buf *req, Buf *res)
|
|
|
1163
1292
|
a.req = req;
|
|
1164
1293
|
a.res = res;
|
|
1165
1294
|
atomic_init(&a.active, 0);
|
|
1166
|
-
|
|
1295
|
+
atomic_init(&a.interrupted, 0);
|
|
1296
|
+
a.started = 0;
|
|
1297
|
+
a.finished = 0;
|
|
1298
|
+
a.has_rr_mtx = 0;
|
|
1299
|
+
rv = rb_ensure(rendezvous_no_des_body, (VALUE)&a,
|
|
1300
|
+
rendezvous_no_des_ensure, (VALUE)&a);
|
|
1301
|
+
r = (void *)(intptr_t)NUM2LONG(rv);
|
|
1302
|
+
if ((int)(intptr_t)r == ECANCELED)
|
|
1303
|
+
rb_raise(context_disposed_error, "disposed context");
|
|
1167
1304
|
if (r)
|
|
1168
1305
|
rb_raise(runtime_error, "single-threaded runner: %s", strerror((int)(intptr_t)r));
|
|
1169
1306
|
}
|
|
@@ -1283,6 +1420,7 @@ static VALUE context_alloc(VALUE klass)
|
|
|
1283
1420
|
buf_init(&c->snapshot);
|
|
1284
1421
|
buf_init(&c->req);
|
|
1285
1422
|
buf_init(&c->res);
|
|
1423
|
+
buf_init(&c->v8_req);
|
|
1286
1424
|
cause = "pthread_condattr_init";
|
|
1287
1425
|
if ((r = pthread_condattr_init(&cattr)))
|
|
1288
1426
|
goto fail0;
|
|
@@ -1387,6 +1525,7 @@ static void context_abandon(Context *c)
|
|
|
1387
1525
|
buf_reset(&c->snapshot);
|
|
1388
1526
|
buf_reset(&c->req);
|
|
1389
1527
|
buf_reset(&c->res);
|
|
1528
|
+
buf_reset(&c->v8_req);
|
|
1390
1529
|
ruby_xfree(c);
|
|
1391
1530
|
}
|
|
1392
1531
|
|
|
@@ -1402,6 +1541,7 @@ static void context_destroy(Context *c)
|
|
|
1402
1541
|
buf_reset(&c->snapshot);
|
|
1403
1542
|
buf_reset(&c->req);
|
|
1404
1543
|
buf_reset(&c->res);
|
|
1544
|
+
buf_reset(&c->v8_req);
|
|
1405
1545
|
ruby_xfree(c);
|
|
1406
1546
|
}
|
|
1407
1547
|
|
|
@@ -1014,6 +1014,14 @@ extern "C" void v8_terminate_execution(State *pst)
|
|
|
1014
1014
|
pst->isolate->TerminateExecution();
|
|
1015
1015
|
}
|
|
1016
1016
|
|
|
1017
|
+
// called from ruby thread
|
|
1018
|
+
extern "C" void v8_cancel_terminate_execution(State *pst)
|
|
1019
|
+
{
|
|
1020
|
+
// TerminateExecution can race with V8 completing and queue a termination
|
|
1021
|
+
// for the next entry without IsExecutionTerminating() becoming true.
|
|
1022
|
+
pst->isolate->CancelTerminateExecution();
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1017
1025
|
extern "C" void v8_single_threaded_enter(State *pst, Context *c, void (*f)(Context *c))
|
|
1018
1026
|
{
|
|
1019
1027
|
State& st = *pst;
|
|
@@ -48,6 +48,7 @@ void v8_snapshot(struct State *pst, const uint8_t *p, size_t n);
|
|
|
48
48
|
void v8_warmup(struct State *pst, const uint8_t *p, size_t n);
|
|
49
49
|
void v8_low_memory_notification(struct State *pst);
|
|
50
50
|
void v8_terminate_execution(struct State *pst); // called from ruby or watchdog thread
|
|
51
|
+
void v8_cancel_terminate_execution(struct State *pst); // called from ruby thread
|
|
51
52
|
void v8_single_threaded_enter(struct State *pst, struct Context *c, void (*f)(struct Context *c));
|
|
52
53
|
void v8_single_threaded_dispose(struct State *pst);
|
|
53
54
|
|
data/lib/mini_racer/version.rb
CHANGED
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.21.
|
|
4
|
+
version: 0.21.4
|
|
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.21.
|
|
140
|
-
documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.21.
|
|
141
|
-
source_code_uri: https://github.com/discourse/mini_racer/tree/v0.21.
|
|
139
|
+
changelog_uri: https://github.com/discourse/mini_racer/blob/v0.21.4/CHANGELOG
|
|
140
|
+
documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.21.4
|
|
141
|
+
source_code_uri: https://github.com/discourse/mini_racer/tree/v0.21.4
|
|
142
142
|
rdoc_options: []
|
|
143
143
|
require_paths:
|
|
144
144
|
- lib
|