mini_racer 0.4.0 → 0.5.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +0 -2
- data/.gitignore +1 -0
- data/.travis.yml +0 -8
- data/README.md +16 -3
- data/ext/mini_racer_extension/extconf.rb +2 -2
- data/ext/mini_racer_extension/mini_racer_extension.cc +227 -48
- data/lib/mini_racer/version.rb +2 -2
- data/lib/mini_racer.rb +15 -6
- data/mini_racer.gemspec +1 -3
- metadata +15 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b0a067ca868ffe75b89fc350d6d62b6e4d2670a4319eda9f6cd05fe424c762ec
|
4
|
+
data.tar.gz: 26c865123cceec38530a9be40e32c1042063f3cbb400172438308a7eb0e06568
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0e1de5f130d4cf5ddca606f57cf7864f9d05d87c9775cd6873d6bb6151983216f74bc7a7eb3e20a9382e5cf95a2ccd8958c7a548ddb491ac8cb9cb89d1c17fd8
|
7
|
+
data.tar.gz: 7df5b7f82afbc9040a6d317bdd8d8600d6656a965bcb44c3ce4f7b6f02007ae1e9fc088022e6c5c10c4755aa4c0dea9badbd4ca8087e3e9185f6f93f42835775
|
data/.github/workflows/ci.yml
CHANGED
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
language: ruby
|
2
2
|
os: linux
|
3
3
|
rvm:
|
4
|
-
- 2.4
|
5
|
-
- 2.5
|
6
4
|
- 2.6
|
7
5
|
- 2.7
|
8
6
|
- 3.0
|
@@ -12,9 +10,6 @@ arch:
|
|
12
10
|
- arm64
|
13
11
|
jobs:
|
14
12
|
include:
|
15
|
-
- rvm: 2.5
|
16
|
-
os: osx
|
17
|
-
osx_image: xcode9.4
|
18
13
|
- rvm: 2.6
|
19
14
|
os: osx
|
20
15
|
osx_image: xcode11.3
|
@@ -25,7 +20,4 @@ jobs:
|
|
25
20
|
os: osx
|
26
21
|
osx_image: xcode12.2
|
27
22
|
dist: xenial
|
28
|
-
before_install:
|
29
|
-
- gem update --system
|
30
|
-
- gem install bundler -v 1.16.2
|
31
23
|
cache: bundler
|
data/README.md
CHANGED
@@ -85,6 +85,19 @@ context.eval 'var a = new Array(10000); while(true) {a = a.concat(new Array(1000
|
|
85
85
|
# => V8OutOfMemoryError is raised
|
86
86
|
```
|
87
87
|
|
88
|
+
### Object marshal max stackdepth support
|
89
|
+
|
90
|
+
Contexts can specify a stack depth limit for object marshalling. Max depth is unbounded by default.
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
# terminates script if stack depth exceeds max during marshal
|
94
|
+
context = MiniRacer::Context.new(marshal_stack_depth: 500)
|
95
|
+
context.attach("a", proc{|a| a})
|
96
|
+
|
97
|
+
context.eval("let arr = []; arr.push(arr); a(arr)")
|
98
|
+
# => RuntimeError is raised
|
99
|
+
```
|
100
|
+
|
88
101
|
### Rich debugging with "filename" support
|
89
102
|
|
90
103
|
```ruby
|
@@ -403,14 +416,14 @@ Or install it yourself as:
|
|
403
416
|
$ gem install mini_racer
|
404
417
|
|
405
418
|
|
406
|
-
**Note** using v8.h and compiling MiniRacer requires a C++11 standard compiler, more specifically clang 3.5 (or later) or
|
419
|
+
**Note** using v8.h and compiling MiniRacer requires a C++11 standard compiler, more specifically clang 3.5 (or later) or GCC 6.3 (or later).
|
407
420
|
|
408
421
|
|
409
422
|
## Travis-ci
|
410
423
|
|
411
|
-
To install `mini-racer` you will need a version of
|
424
|
+
To install `mini-racer` you will need a version of GCC that supports C++11 (GCC 6.3) this is included by default in ubuntu trusty based images.
|
412
425
|
|
413
|
-
Travis today ships by default with a precise based image. Precise Pangolin (12.04 LTS) was first released in August 2012. Even though you can install GCC
|
426
|
+
Travis today ships by default with a precise based image. Precise Pangolin (12.04 LTS) was first released in August 2012. Even though you can install GCC 6.3 on precise the simpler approach is to opt for the trusty based image.
|
414
427
|
|
415
428
|
Add this to your .travis.yml file:
|
416
429
|
|
@@ -11,9 +11,9 @@ $CPPFLAGS += " -Wall" unless $CPPFLAGS.split.include? "-Wall"
|
|
11
11
|
$CPPFLAGS += " -g" unless $CPPFLAGS.split.include? "-g"
|
12
12
|
$CPPFLAGS += " -rdynamic" unless $CPPFLAGS.split.include? "-rdynamic"
|
13
13
|
$CPPFLAGS += " -fPIC" unless $CPPFLAGS.split.include? "-rdynamic" or IS_DARWIN
|
14
|
-
$CPPFLAGS += " -std=c++
|
14
|
+
$CPPFLAGS += " -std=c++14"
|
15
15
|
$CPPFLAGS += " -fpermissive"
|
16
|
-
|
16
|
+
#$CPPFLAGS += " -DV8_COMPRESS_POINTERS"
|
17
17
|
$CPPFLAGS += " -fvisibility=hidden "
|
18
18
|
|
19
19
|
$CPPFLAGS += " -Wno-reserved-user-defined-literal" if IS_DARWIN
|
@@ -115,6 +115,7 @@ typedef struct {
|
|
115
115
|
useconds_t timeout;
|
116
116
|
EvalResult* result;
|
117
117
|
size_t max_memory;
|
118
|
+
size_t marshal_stackdepth;
|
118
119
|
} EvalParams;
|
119
120
|
|
120
121
|
typedef struct {
|
@@ -126,13 +127,152 @@ typedef struct {
|
|
126
127
|
Local<Value> *argv;
|
127
128
|
EvalResult result;
|
128
129
|
size_t max_memory;
|
130
|
+
size_t marshal_stackdepth;
|
129
131
|
} FunctionCall;
|
130
132
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
133
|
+
class IsolateData {
|
134
|
+
public:
|
135
|
+
enum Flag {
|
136
|
+
// first flags are bitfield
|
137
|
+
// max count: sizeof(uintptr_t) * 8
|
138
|
+
IN_GVL, // whether we are inside of ruby gvl or not
|
139
|
+
DO_TERMINATE, // terminate as soon as possible
|
140
|
+
MEM_SOFTLIMIT_REACHED, // we've hit the memory soft limit
|
141
|
+
MEM_SOFTLIMIT_MAX, // maximum memory value
|
142
|
+
MARSHAL_STACKDEPTH_REACHED, // we've hit our max stack depth
|
143
|
+
MARSHAL_STACKDEPTH_VALUE, // current stackdepth
|
144
|
+
MARSHAL_STACKDEPTH_MAX, // maximum stack depth during marshal
|
145
|
+
};
|
146
|
+
|
147
|
+
static void Init(Isolate *isolate) {
|
148
|
+
// zero out all fields in the bitfield
|
149
|
+
isolate->SetData(0, 0);
|
150
|
+
}
|
151
|
+
|
152
|
+
static uintptr_t Get(Isolate *isolate, Flag flag) {
|
153
|
+
Bitfield u = { reinterpret_cast<uint64_t>(isolate->GetData(0)) };
|
154
|
+
switch (flag) {
|
155
|
+
case IN_GVL: return u.IN_GVL;
|
156
|
+
case DO_TERMINATE: return u.DO_TERMINATE;
|
157
|
+
case MEM_SOFTLIMIT_REACHED: return u.MEM_SOFTLIMIT_REACHED;
|
158
|
+
case MEM_SOFTLIMIT_MAX: return static_cast<uintptr_t>(u.MEM_SOFTLIMIT_MAX) << 10;
|
159
|
+
case MARSHAL_STACKDEPTH_REACHED: return u.MARSHAL_STACKDEPTH_REACHED;
|
160
|
+
case MARSHAL_STACKDEPTH_VALUE: return u.MARSHAL_STACKDEPTH_VALUE;
|
161
|
+
case MARSHAL_STACKDEPTH_MAX: return u.MARSHAL_STACKDEPTH_MAX;
|
162
|
+
}
|
163
|
+
|
164
|
+
// avoid compiler warning
|
165
|
+
return u.IN_GVL;
|
166
|
+
}
|
167
|
+
|
168
|
+
static void Set(Isolate *isolate, Flag flag, uintptr_t value) {
|
169
|
+
Bitfield u = { reinterpret_cast<uint64_t>(isolate->GetData(0)) };
|
170
|
+
switch (flag) {
|
171
|
+
case IN_GVL: u.IN_GVL = value; break;
|
172
|
+
case DO_TERMINATE: u.DO_TERMINATE = value; break;
|
173
|
+
case MEM_SOFTLIMIT_REACHED: u.MEM_SOFTLIMIT_REACHED = value; break;
|
174
|
+
// drop least significant 10 bits 'store memory amount in kb'
|
175
|
+
case MEM_SOFTLIMIT_MAX: u.MEM_SOFTLIMIT_MAX = value >> 10; break;
|
176
|
+
case MARSHAL_STACKDEPTH_REACHED: u.MARSHAL_STACKDEPTH_REACHED = value; break;
|
177
|
+
case MARSHAL_STACKDEPTH_VALUE: u.MARSHAL_STACKDEPTH_VALUE = value; break;
|
178
|
+
case MARSHAL_STACKDEPTH_MAX: u.MARSHAL_STACKDEPTH_MAX = value; break;
|
179
|
+
}
|
180
|
+
isolate->SetData(0, reinterpret_cast<void*>(u.dataPtr));
|
181
|
+
}
|
182
|
+
|
183
|
+
private:
|
184
|
+
struct Bitfield {
|
185
|
+
// WARNING: this would explode on platforms below 64 bit ptrs
|
186
|
+
// compiler will fail here, making it clear for them.
|
187
|
+
// Additionally, using the other part of the union to reinterpret the
|
188
|
+
// memory is undefined behavior according to spec, but is / has been stable
|
189
|
+
// across major compilers for decades.
|
190
|
+
static_assert(sizeof(uintptr_t) >= sizeof(uint64_t), "mini_racer not supported on this platform. ptr size must be at least 64 bit.");
|
191
|
+
union {
|
192
|
+
uint64_t dataPtr: 64;
|
193
|
+
// order in this struct matters. For cpu performance keep larger subobjects
|
194
|
+
// aligned on their boundaries (8 16 32), try not to straddle
|
195
|
+
struct {
|
196
|
+
size_t MEM_SOFTLIMIT_MAX:22;
|
197
|
+
bool IN_GVL:1;
|
198
|
+
bool DO_TERMINATE:1;
|
199
|
+
bool MEM_SOFTLIMIT_REACHED:1;
|
200
|
+
bool MARSHAL_STACKDEPTH_REACHED:1;
|
201
|
+
uint8_t :0; // align to next 8bit bound
|
202
|
+
size_t MARSHAL_STACKDEPTH_VALUE:10;
|
203
|
+
uint8_t :0; // align to next 8bit bound
|
204
|
+
size_t MARSHAL_STACKDEPTH_MAX:10;
|
205
|
+
};
|
206
|
+
};
|
207
|
+
};
|
208
|
+
};
|
209
|
+
|
210
|
+
struct StackCounter {
|
211
|
+
static void Reset(Isolate* isolate) {
|
212
|
+
if (IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_MAX) > 0) {
|
213
|
+
IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE, 0);
|
214
|
+
IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED, false);
|
215
|
+
}
|
216
|
+
}
|
217
|
+
|
218
|
+
static void SetMax(Isolate* isolate, size_t marshalMaxStackDepth) {
|
219
|
+
if (marshalMaxStackDepth > 0) {
|
220
|
+
IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_MAX, marshalMaxStackDepth);
|
221
|
+
IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE, 0);
|
222
|
+
IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED, false);
|
223
|
+
}
|
224
|
+
}
|
225
|
+
|
226
|
+
StackCounter(Isolate* isolate) {
|
227
|
+
this->isActive = IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_MAX) > 0;
|
228
|
+
|
229
|
+
if (this->isActive) {
|
230
|
+
this->isolate = isolate;
|
231
|
+
this->IncDepth(1);
|
232
|
+
}
|
233
|
+
}
|
234
|
+
|
235
|
+
bool IsTooDeep() {
|
236
|
+
if (!this->IsActive()) {
|
237
|
+
return false;
|
238
|
+
}
|
239
|
+
|
240
|
+
size_t depth = IsolateData::Get(this->isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE);
|
241
|
+
size_t maxDepth = IsolateData::Get(this->isolate, IsolateData::MARSHAL_STACKDEPTH_MAX);
|
242
|
+
if (depth > maxDepth) {
|
243
|
+
IsolateData::Set(this->isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED, true);
|
244
|
+
return true;
|
245
|
+
}
|
246
|
+
|
247
|
+
return false;
|
248
|
+
}
|
249
|
+
|
250
|
+
bool IsActive() {
|
251
|
+
return this->isActive && !IsolateData::Get(this->isolate, IsolateData::DO_TERMINATE);
|
252
|
+
}
|
253
|
+
|
254
|
+
~StackCounter() {
|
255
|
+
if (this->IsActive()) {
|
256
|
+
this->IncDepth(-1);
|
257
|
+
}
|
258
|
+
}
|
259
|
+
|
260
|
+
private:
|
261
|
+
Isolate* isolate;
|
262
|
+
bool isActive;
|
263
|
+
|
264
|
+
void IncDepth(int direction) {
|
265
|
+
int inc = direction > 0 ? 1 : -1;
|
266
|
+
|
267
|
+
size_t depth = IsolateData::Get(this->isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE);
|
268
|
+
|
269
|
+
// don't decrement past 0
|
270
|
+
if (inc > 0 || depth > 0) {
|
271
|
+
depth += inc;
|
272
|
+
}
|
273
|
+
|
274
|
+
IsolateData::Set(this->isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE, depth);
|
275
|
+
}
|
136
276
|
};
|
137
277
|
|
138
278
|
static VALUE rb_cContext;
|
@@ -205,16 +345,18 @@ static void init_v8() {
|
|
205
345
|
}
|
206
346
|
|
207
347
|
static void gc_callback(Isolate *isolate, GCType type, GCCallbackFlags flags) {
|
208
|
-
if((
|
348
|
+
if (IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_REACHED)) {
|
349
|
+
return;
|
350
|
+
}
|
209
351
|
|
210
|
-
size_t softlimit =
|
352
|
+
size_t softlimit = IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_MAX);
|
211
353
|
|
212
354
|
HeapStatistics stats;
|
213
355
|
isolate->GetHeapStatistics(&stats);
|
214
356
|
size_t used = stats.used_heap_size();
|
215
357
|
|
216
358
|
if(used > softlimit) {
|
217
|
-
isolate
|
359
|
+
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_REACHED, true);
|
218
360
|
isolate->TerminateExecution();
|
219
361
|
}
|
220
362
|
}
|
@@ -326,14 +468,15 @@ nogvl_context_eval(void* arg) {
|
|
326
468
|
Context::Scope context_scope(context);
|
327
469
|
v8::ScriptOrigin *origin = NULL;
|
328
470
|
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
471
|
+
IsolateData::Init(isolate);
|
472
|
+
|
473
|
+
if (eval_params->max_memory > 0) {
|
474
|
+
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_MAX, eval_params->max_memory);
|
475
|
+
if (!isolate_info->added_gc_cb) {
|
476
|
+
isolate->AddGCEpilogueCallback(gc_callback);
|
477
|
+
isolate_info->added_gc_cb = true;
|
478
|
+
}
|
479
|
+
}
|
337
480
|
|
338
481
|
MaybeLocal<Script> parsed_script;
|
339
482
|
|
@@ -359,12 +502,8 @@ nogvl_context_eval(void* arg) {
|
|
359
502
|
result->message->Reset(isolate, trycatch.Exception());
|
360
503
|
} else {
|
361
504
|
// parsing successful
|
362
|
-
if (eval_params->
|
363
|
-
isolate
|
364
|
-
if (!isolate_info->added_gc_cb) {
|
365
|
-
isolate->AddGCEpilogueCallback(gc_callback);
|
366
|
-
isolate_info->added_gc_cb = true;
|
367
|
-
}
|
505
|
+
if (eval_params->marshal_stackdepth > 0) {
|
506
|
+
StackCounter::SetMax(isolate, eval_params->marshal_stackdepth);
|
368
507
|
}
|
369
508
|
|
370
509
|
maybe_value = parsed_script.ToLocalChecked()->Run(context);
|
@@ -372,7 +511,7 @@ nogvl_context_eval(void* arg) {
|
|
372
511
|
|
373
512
|
prepare_result(maybe_value, trycatch, isolate, context, *result);
|
374
513
|
|
375
|
-
isolate
|
514
|
+
IsolateData::Set(isolate, IsolateData::IN_GVL, true);
|
376
515
|
|
377
516
|
return NULL;
|
378
517
|
}
|
@@ -390,6 +529,18 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
390
529
|
Isolate::Scope isolate_scope(isolate);
|
391
530
|
HandleScope scope(isolate);
|
392
531
|
|
532
|
+
StackCounter stackCounter(isolate);
|
533
|
+
|
534
|
+
if (IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED)) {
|
535
|
+
return Qnil;
|
536
|
+
}
|
537
|
+
|
538
|
+
if (stackCounter.IsTooDeep()) {
|
539
|
+
IsolateData::Set(isolate, IsolateData::DO_TERMINATE, true);
|
540
|
+
isolate->TerminateExecution();
|
541
|
+
return Qnil;
|
542
|
+
}
|
543
|
+
|
393
544
|
if (value->IsNull() || value->IsUndefined()){
|
394
545
|
return Qnil;
|
395
546
|
}
|
@@ -478,7 +629,7 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
478
629
|
rb_enc_find("utf-8")
|
479
630
|
);
|
480
631
|
|
481
|
-
return
|
632
|
+
return rb_str_intern(str_symbol);
|
482
633
|
}
|
483
634
|
|
484
635
|
MaybeLocal<String> rstr_maybe = value->ToString(context);
|
@@ -543,7 +694,8 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
543
694
|
length = RARRAY_LEN(value);
|
544
695
|
array = Array::New(isolate, (int)length);
|
545
696
|
for(i=0; i<length; i++) {
|
546
|
-
array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
|
697
|
+
Maybe<bool> success = array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
|
698
|
+
(void)(success);
|
547
699
|
}
|
548
700
|
return scope.Escape(array);
|
549
701
|
case T_HASH:
|
@@ -552,8 +704,9 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
552
704
|
length = RARRAY_LEN(hash_as_array);
|
553
705
|
for(i=0; i<length; i++) {
|
554
706
|
pair = rb_ary_entry(hash_as_array, i);
|
555
|
-
object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
|
707
|
+
Maybe<bool> success = object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
|
556
708
|
convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 1)));
|
709
|
+
(void)(success);
|
557
710
|
}
|
558
711
|
return scope.Escape(object);
|
559
712
|
case T_SYMBOL:
|
@@ -884,9 +1037,14 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
884
1037
|
if (!result.executed) {
|
885
1038
|
VALUE ruby_exception = rb_iv_get(self, "@current_exception");
|
886
1039
|
if (ruby_exception == Qnil) {
|
887
|
-
bool mem_softlimit_reached = (
|
1040
|
+
bool mem_softlimit_reached = IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_REACHED);
|
1041
|
+
bool marshal_stack_maxdepth_reached = IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED);
|
888
1042
|
// If we were terminated or have the memory softlimit flag set
|
889
|
-
if (
|
1043
|
+
if (marshal_stack_maxdepth_reached) {
|
1044
|
+
ruby_exception = rb_eScriptRuntimeError;
|
1045
|
+
std::string msg = std::string("Marshal object depth too deep. Script terminated.");
|
1046
|
+
message = rb_enc_str_new(msg.c_str(), msg.length(), rb_enc_find("utf-8"));
|
1047
|
+
} else if (result.terminated || mem_softlimit_reached) {
|
890
1048
|
ruby_exception = mem_softlimit_reached ? rb_eV8OutOfMemoryError : rb_eScriptTerminatedError;
|
891
1049
|
} else {
|
892
1050
|
ruby_exception = rb_eScriptRuntimeError;
|
@@ -921,6 +1079,7 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
921
1079
|
VALUE json_string = rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
|
922
1080
|
ret = rb_funcall(rb_mJSON, rb_intern("parse"), 1, json_string);
|
923
1081
|
} else {
|
1082
|
+
StackCounter::Reset(isolate);
|
924
1083
|
ret = convert_v8_to_ruby(isolate, *p_ctx, tmp);
|
925
1084
|
}
|
926
1085
|
|
@@ -978,6 +1137,7 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
|
|
978
1137
|
eval_params.result = &eval_result;
|
979
1138
|
eval_params.timeout = 0;
|
980
1139
|
eval_params.max_memory = 0;
|
1140
|
+
eval_params.marshal_stackdepth = 0;
|
981
1141
|
VALUE timeout = rb_iv_get(self, "@timeout");
|
982
1142
|
if (timeout != Qnil) {
|
983
1143
|
eval_params.timeout = (useconds_t)NUM2LONG(timeout);
|
@@ -988,6 +1148,11 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
|
|
988
1148
|
eval_params.max_memory = (size_t)NUM2ULONG(mem_softlimit);
|
989
1149
|
}
|
990
1150
|
|
1151
|
+
VALUE stack_depth = rb_iv_get(self, "@marshal_stack_depth");
|
1152
|
+
if (stack_depth != Qnil) {
|
1153
|
+
eval_params.marshal_stackdepth = (size_t)NUM2ULONG(stack_depth);
|
1154
|
+
}
|
1155
|
+
|
991
1156
|
eval_result.message = NULL;
|
992
1157
|
eval_result.backtrace = NULL;
|
993
1158
|
|
@@ -1057,6 +1222,7 @@ gvl_ruby_callback(void* data) {
|
|
1057
1222
|
|
1058
1223
|
for (int i = 0; i < length; i++) {
|
1059
1224
|
Local<Value> value = ((*args)[i]).As<Value>();
|
1225
|
+
StackCounter::Reset(args->GetIsolate());
|
1060
1226
|
VALUE tmp = convert_v8_to_ruby(args->GetIsolate(),
|
1061
1227
|
*context_info->context, value);
|
1062
1228
|
rb_ary_push(ruby_args, tmp);
|
@@ -1070,7 +1236,7 @@ gvl_ruby_callback(void* data) {
|
|
1070
1236
|
callback_data.ruby_args = ruby_args;
|
1071
1237
|
callback_data.failed = false;
|
1072
1238
|
|
1073
|
-
if ((
|
1239
|
+
if (IsolateData::Get(args->GetIsolate(), IsolateData::DO_TERMINATE)) {
|
1074
1240
|
args->GetIsolate()->ThrowException(
|
1075
1241
|
String::NewFromUtf8Literal(args->GetIsolate(),
|
1076
1242
|
"Terminated execution during transition from Ruby to JS"));
|
@@ -1082,8 +1248,11 @@ gvl_ruby_callback(void* data) {
|
|
1082
1248
|
return NULL;
|
1083
1249
|
}
|
1084
1250
|
|
1085
|
-
|
1086
|
-
|
1251
|
+
VALUE callback_data_value = (VALUE)&callback_data;
|
1252
|
+
|
1253
|
+
// TODO: use rb_vrescue2 in Ruby 2.7 and above
|
1254
|
+
result = rb_rescue2(RUBY_METHOD_FUNC(protected_callback), callback_data_value,
|
1255
|
+
RUBY_METHOD_FUNC(rescue_callback), callback_data_value, rb_eException, (VALUE)0);
|
1087
1256
|
|
1088
1257
|
if(callback_data.failed) {
|
1089
1258
|
rb_iv_set(parent, "@current_exception", result);
|
@@ -1100,23 +1269,22 @@ gvl_ruby_callback(void* data) {
|
|
1100
1269
|
rb_gc_force_recycle(ruby_args);
|
1101
1270
|
}
|
1102
1271
|
|
1103
|
-
if ((
|
1104
|
-
|
1105
|
-
isolate->TerminateExecution();
|
1272
|
+
if (IsolateData::Get(args->GetIsolate(), IsolateData::DO_TERMINATE)) {
|
1273
|
+
args->GetIsolate()->TerminateExecution();
|
1106
1274
|
}
|
1107
1275
|
|
1108
1276
|
return NULL;
|
1109
1277
|
}
|
1110
1278
|
|
1111
1279
|
static void ruby_callback(const FunctionCallbackInfo<Value>& args) {
|
1112
|
-
bool has_gvl = (
|
1280
|
+
bool has_gvl = IsolateData::Get(args.GetIsolate(), IsolateData::IN_GVL);
|
1113
1281
|
|
1114
1282
|
if(has_gvl) {
|
1115
1283
|
gvl_ruby_callback((void*)&args);
|
1116
1284
|
} else {
|
1117
|
-
args.GetIsolate()
|
1285
|
+
IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, true);
|
1118
1286
|
rb_thread_call_with_gvl(gvl_ruby_callback, (void*)(&args));
|
1119
|
-
args.GetIsolate()
|
1287
|
+
IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, false);
|
1120
1288
|
}
|
1121
1289
|
}
|
1122
1290
|
|
@@ -1157,12 +1325,13 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1157
1325
|
Local<Value> external = External::New(isolate, self_copy);
|
1158
1326
|
|
1159
1327
|
if (parent_object == Qnil) {
|
1160
|
-
context->Global()->Set(
|
1328
|
+
Maybe<bool> success = context->Global()->Set(
|
1161
1329
|
context,
|
1162
1330
|
v8_str,
|
1163
1331
|
FunctionTemplate::New(isolate, ruby_callback, external)
|
1164
1332
|
->GetFunction(context)
|
1165
1333
|
.ToLocalChecked());
|
1334
|
+
(void)success;
|
1166
1335
|
|
1167
1336
|
} else {
|
1168
1337
|
Local<String> eval =
|
@@ -1182,12 +1351,13 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1182
1351
|
if (!maybe_value.IsEmpty()) {
|
1183
1352
|
Local<Value> value = maybe_value.ToLocalChecked();
|
1184
1353
|
if (value->IsObject()) {
|
1185
|
-
value.As<Object>()->Set(
|
1354
|
+
Maybe<bool> success = value.As<Object>()->Set(
|
1186
1355
|
context,
|
1187
1356
|
v8_str,
|
1188
1357
|
FunctionTemplate::New(isolate, ruby_callback, external)
|
1189
1358
|
->GetFunction(context)
|
1190
1359
|
.ToLocalChecked());
|
1360
|
+
(void)success;
|
1191
1361
|
attach_error = false;
|
1192
1362
|
}
|
1193
1363
|
}
|
@@ -1478,7 +1648,7 @@ rb_context_stop(VALUE self) {
|
|
1478
1648
|
Isolate* isolate = context_info->isolate_info->isolate;
|
1479
1649
|
|
1480
1650
|
// flag for termination
|
1481
|
-
isolate
|
1651
|
+
IsolateData::Set(isolate, IsolateData::DO_TERMINATE, true);
|
1482
1652
|
|
1483
1653
|
isolate->TerminateExecution();
|
1484
1654
|
rb_funcall(self, rb_intern("stop_attached"), 0);
|
@@ -1507,18 +1677,20 @@ nogvl_context_call(void *args) {
|
|
1507
1677
|
IsolateInfo *isolate_info = call->context_info->isolate_info;
|
1508
1678
|
Isolate* isolate = isolate_info->isolate;
|
1509
1679
|
|
1510
|
-
|
1511
|
-
isolate
|
1512
|
-
// terminate ASAP
|
1513
|
-
isolate->SetData(DO_TERMINATE, (void*)false);
|
1680
|
+
IsolateData::Set(isolate, IsolateData::IN_GVL, false);
|
1681
|
+
IsolateData::Set(isolate, IsolateData::DO_TERMINATE, false);
|
1514
1682
|
|
1515
1683
|
if (call->max_memory > 0) {
|
1516
|
-
isolate
|
1517
|
-
isolate
|
1684
|
+
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_MAX, call->max_memory);
|
1685
|
+
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_REACHED, false);
|
1518
1686
|
if (!isolate_info->added_gc_cb) {
|
1519
|
-
|
1687
|
+
isolate->AddGCEpilogueCallback(gc_callback);
|
1520
1688
|
isolate_info->added_gc_cb = true;
|
1689
|
+
}
|
1521
1690
|
}
|
1691
|
+
|
1692
|
+
if (call->marshal_stackdepth > 0) {
|
1693
|
+
StackCounter::SetMax(isolate, call->marshal_stackdepth);
|
1522
1694
|
}
|
1523
1695
|
|
1524
1696
|
Isolate::Scope isolate_scope(isolate);
|
@@ -1536,7 +1708,7 @@ nogvl_context_call(void *args) {
|
|
1536
1708
|
MaybeLocal<v8::Value> res = fun->Call(context, context->Global(), call->argc, call->argv);
|
1537
1709
|
prepare_result(res, trycatch, isolate, context, eval_res);
|
1538
1710
|
|
1539
|
-
isolate
|
1711
|
+
IsolateData::Set(isolate, IsolateData::IN_GVL, true);
|
1540
1712
|
|
1541
1713
|
return NULL;
|
1542
1714
|
}
|
@@ -1584,6 +1756,13 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1584
1756
|
unsigned long sl_int = NUM2ULONG(mem_softlimit);
|
1585
1757
|
call.max_memory = (size_t)sl_int;
|
1586
1758
|
}
|
1759
|
+
|
1760
|
+
call.marshal_stackdepth = 0;
|
1761
|
+
VALUE marshal_stackdepth = rb_iv_get(self, "@marshal_stack_depth");
|
1762
|
+
if (marshal_stackdepth != Qnil) {
|
1763
|
+
unsigned long sl_int = NUM2ULONG(marshal_stackdepth);
|
1764
|
+
call.marshal_stackdepth = (size_t)sl_int;
|
1765
|
+
}
|
1587
1766
|
|
1588
1767
|
bool missingFunction = false;
|
1589
1768
|
{
|
data/lib/mini_racer/version.rb
CHANGED
data/lib/mini_racer.rb
CHANGED
@@ -15,6 +15,9 @@ require "json"
|
|
15
15
|
|
16
16
|
module MiniRacer
|
17
17
|
|
18
|
+
MARSHAL_STACKDEPTH_DEFAULT = 2**9-2
|
19
|
+
MARSHAL_STACKDEPTH_MAX_VALUE = 2**10-2
|
20
|
+
|
18
21
|
class Error < ::StandardError; end
|
19
22
|
|
20
23
|
class ContextDisposedError < Error; end
|
@@ -140,10 +143,10 @@ module MiniRacer
|
|
140
143
|
end
|
141
144
|
end
|
142
145
|
|
143
|
-
def initialize(max_memory: nil, timeout: nil, isolate: nil, ensure_gc_after_idle: nil, snapshot: nil)
|
146
|
+
def initialize(max_memory: nil, timeout: nil, isolate: nil, ensure_gc_after_idle: nil, snapshot: nil, marshal_stack_depth: nil)
|
144
147
|
options ||= {}
|
145
148
|
|
146
|
-
check_init_options!(isolate: isolate, snapshot: snapshot, max_memory: max_memory, ensure_gc_after_idle: ensure_gc_after_idle, timeout: timeout)
|
149
|
+
check_init_options!(isolate: isolate, snapshot: snapshot, max_memory: max_memory, marshal_stack_depth: marshal_stack_depth, ensure_gc_after_idle: ensure_gc_after_idle, timeout: timeout)
|
147
150
|
|
148
151
|
@functions = {}
|
149
152
|
@timeout = nil
|
@@ -151,6 +154,7 @@ module MiniRacer
|
|
151
154
|
@current_exception = nil
|
152
155
|
@timeout = timeout
|
153
156
|
@max_memory = max_memory
|
157
|
+
@marshal_stack_depth = marshal_stack_depth
|
154
158
|
|
155
159
|
# false signals it should be fetched if requested
|
156
160
|
@isolate = isolate || false
|
@@ -379,11 +383,12 @@ module MiniRacer
|
|
379
383
|
rp.close if rp
|
380
384
|
end
|
381
385
|
|
382
|
-
def check_init_options!(isolate:, snapshot:, max_memory:, ensure_gc_after_idle:, timeout:)
|
386
|
+
def check_init_options!(isolate:, snapshot:, max_memory:, marshal_stack_depth:, ensure_gc_after_idle:, timeout:)
|
383
387
|
assert_option_is_nil_or_a('isolate', isolate, Isolate)
|
384
388
|
assert_option_is_nil_or_a('snapshot', snapshot, Snapshot)
|
385
389
|
|
386
|
-
assert_numeric_or_nil('max_memory', max_memory, min_value: 10_000)
|
390
|
+
assert_numeric_or_nil('max_memory', max_memory, min_value: 10_000, max_value: 2**32-1)
|
391
|
+
assert_numeric_or_nil('marshal_stack_depth', marshal_stack_depth, min_value: 1, max_value: MARSHAL_STACKDEPTH_MAX_VALUE)
|
387
392
|
assert_numeric_or_nil('ensure_gc_after_idle', ensure_gc_after_idle, min_value: 1)
|
388
393
|
assert_numeric_or_nil('timeout', timeout, min_value: 1)
|
389
394
|
|
@@ -392,9 +397,13 @@ module MiniRacer
|
|
392
397
|
end
|
393
398
|
end
|
394
399
|
|
395
|
-
def assert_numeric_or_nil(option_name, object, min_value:)
|
400
|
+
def assert_numeric_or_nil(option_name, object, min_value:, max_value: nil)
|
401
|
+
if max_value && object.is_a?(Numeric) && object > max_value
|
402
|
+
raise ArgumentError, "#{option_name} must be less than or equal to #{max_value}"
|
403
|
+
end
|
404
|
+
|
396
405
|
if object.is_a?(Numeric) && object < min_value
|
397
|
-
raise ArgumentError, "#{option_name} must be larger than #{min_value}"
|
406
|
+
raise ArgumentError, "#{option_name} must be larger than or equal to #{min_value}"
|
398
407
|
end
|
399
408
|
|
400
409
|
if !object.nil? && !object.is_a?(Numeric)
|
data/mini_racer.gemspec
CHANGED
@@ -22,8 +22,6 @@ Gem::Specification.new do |spec|
|
|
22
22
|
}
|
23
23
|
|
24
24
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(benchmark|test|spec|features|examples)/}) }
|
25
|
-
spec.bindir = "exe"
|
26
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
27
25
|
spec.require_paths = ["lib"]
|
28
26
|
|
29
27
|
spec.add_development_dependency "bundler"
|
@@ -37,5 +35,5 @@ Gem::Specification.new do |spec|
|
|
37
35
|
|
38
36
|
spec.extensions = ["ext/mini_racer_loader/extconf.rb", "ext/mini_racer_extension/extconf.rb"]
|
39
37
|
|
40
|
-
spec.required_ruby_version = '>= 2.
|
38
|
+
spec.required_ruby_version = '>= 2.6'
|
41
39
|
end
|
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.
|
4
|
+
version: 0.5.0.pre
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Saffron
|
8
|
-
autorequire:
|
9
|
-
bindir:
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-11-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -86,14 +86,14 @@ dependencies:
|
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
89
|
+
version: 16.10.0.0
|
90
90
|
type: :runtime
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
96
|
+
version: 16.10.0.0
|
97
97
|
description: Minimal embedded v8 engine for Ruby
|
98
98
|
email:
|
99
99
|
- sam.saffron@gmail.com
|
@@ -128,10 +128,10 @@ licenses:
|
|
128
128
|
- MIT
|
129
129
|
metadata:
|
130
130
|
bug_tracker_uri: https://github.com/discourse/mini_racer/issues
|
131
|
-
changelog_uri: https://github.com/discourse/mini_racer/blob/v0.
|
132
|
-
documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.
|
133
|
-
source_code_uri: https://github.com/discourse/mini_racer/tree/v0.
|
134
|
-
post_install_message:
|
131
|
+
changelog_uri: https://github.com/discourse/mini_racer/blob/v0.5.0.pre/CHANGELOG
|
132
|
+
documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.5.0.pre
|
133
|
+
source_code_uri: https://github.com/discourse/mini_racer/tree/v0.5.0.pre
|
134
|
+
post_install_message:
|
135
135
|
rdoc_options: []
|
136
136
|
require_paths:
|
137
137
|
- lib
|
@@ -140,15 +140,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
140
140
|
requirements:
|
141
141
|
- - ">="
|
142
142
|
- !ruby/object:Gem::Version
|
143
|
-
version: '2.
|
143
|
+
version: '2.6'
|
144
144
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
145
145
|
requirements:
|
146
|
-
- - "
|
146
|
+
- - ">"
|
147
147
|
- !ruby/object:Gem::Version
|
148
|
-
version:
|
148
|
+
version: 1.3.1
|
149
149
|
requirements: []
|
150
|
-
rubygems_version: 3.
|
151
|
-
signing_key:
|
150
|
+
rubygems_version: 3.1.6
|
151
|
+
signing_key:
|
152
152
|
specification_version: 4
|
153
153
|
summary: Minimal embedded v8 for Ruby
|
154
154
|
test_files: []
|