mini_racer 0.2.9 → 0.5.0
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/.dockerignore +12 -0
- data/.github/workflows/ci.yml +78 -0
- data/.gitignore +2 -0
- data/.travis.yml +14 -15
- data/CHANGELOG +65 -0
- data/Dockerfile +21 -0
- data/README.md +35 -7
- data/Rakefile +1 -0
- data/ext/mini_racer_extension/extconf.rb +11 -3
- data/ext/mini_racer_extension/mini_racer_extension.cc +421 -139
- data/ext/mini_racer_loader/extconf.rb +8 -0
- data/ext/mini_racer_loader/mini_racer_loader.c +123 -0
- data/lib/mini_racer/version.rb +4 -1
- data/lib/mini_racer.rb +91 -15
- data/mini_racer.gemspec +6 -7
- metadata +40 -20
@@ -25,6 +25,7 @@ public:
|
|
25
25
|
ArrayBuffer::Allocator* allocator;
|
26
26
|
StartupData* startup_data;
|
27
27
|
bool interrupted;
|
28
|
+
bool added_gc_cb;
|
28
29
|
pid_t pid;
|
29
30
|
VALUE mutex;
|
30
31
|
|
@@ -42,15 +43,12 @@ public:
|
|
42
43
|
|
43
44
|
|
44
45
|
IsolateInfo() : isolate(nullptr), allocator(nullptr), startup_data(nullptr),
|
45
|
-
interrupted(false), pid(getpid()), refs_count(0) {
|
46
|
+
interrupted(false), added_gc_cb(false), pid(getpid()), refs_count(0) {
|
46
47
|
VALUE cMutex = rb_const_get(rb_cThread, rb_intern("Mutex"));
|
47
48
|
mutex = rb_class_new_instance(0, nullptr, cMutex);
|
48
49
|
}
|
49
50
|
|
50
|
-
~IsolateInfo()
|
51
|
-
void free_isolate(IsolateInfo*);
|
52
|
-
free_isolate(this);
|
53
|
-
}
|
51
|
+
~IsolateInfo();
|
54
52
|
|
55
53
|
void init(SnapshotInfo* snapshot_info = nullptr);
|
56
54
|
|
@@ -117,6 +115,7 @@ typedef struct {
|
|
117
115
|
useconds_t timeout;
|
118
116
|
EvalResult* result;
|
119
117
|
size_t max_memory;
|
118
|
+
size_t marshal_stackdepth;
|
120
119
|
} EvalParams;
|
121
120
|
|
122
121
|
typedef struct {
|
@@ -127,13 +126,153 @@ typedef struct {
|
|
127
126
|
Local<Function> fun;
|
128
127
|
Local<Value> *argv;
|
129
128
|
EvalResult result;
|
129
|
+
size_t max_memory;
|
130
|
+
size_t marshal_stackdepth;
|
130
131
|
} FunctionCall;
|
131
132
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
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
|
+
}
|
137
276
|
};
|
138
277
|
|
139
278
|
static VALUE rb_cContext;
|
@@ -155,6 +294,11 @@ static VALUE rb_cDateTime = Qnil;
|
|
155
294
|
static std::unique_ptr<Platform> current_platform = NULL;
|
156
295
|
static std::mutex platform_lock;
|
157
296
|
|
297
|
+
static pthread_attr_t *thread_attr_p;
|
298
|
+
static pthread_rwlock_t exit_lock = PTHREAD_RWLOCK_INITIALIZER;
|
299
|
+
static bool ruby_exiting = false; // guarded by exit_lock
|
300
|
+
static bool single_threaded = false;
|
301
|
+
|
158
302
|
static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
159
303
|
bool platform_already_initialized = false;
|
160
304
|
|
@@ -166,6 +310,9 @@ static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
|
166
310
|
platform_lock.lock();
|
167
311
|
|
168
312
|
if (current_platform == NULL) {
|
313
|
+
if (!strcmp(RSTRING_PTR(flag_as_str), "--single_threaded")) {
|
314
|
+
single_threaded = true;
|
315
|
+
}
|
169
316
|
V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), (int)RSTRING_LEN(flag_as_str));
|
170
317
|
} else {
|
171
318
|
platform_already_initialized = true;
|
@@ -198,16 +345,18 @@ static void init_v8() {
|
|
198
345
|
}
|
199
346
|
|
200
347
|
static void gc_callback(Isolate *isolate, GCType type, GCCallbackFlags flags) {
|
201
|
-
if((
|
348
|
+
if (IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_REACHED)) {
|
349
|
+
return;
|
350
|
+
}
|
202
351
|
|
203
|
-
size_t softlimit =
|
352
|
+
size_t softlimit = IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_MAX);
|
204
353
|
|
205
354
|
HeapStatistics stats;
|
206
355
|
isolate->GetHeapStatistics(&stats);
|
207
356
|
size_t used = stats.used_heap_size();
|
208
357
|
|
209
358
|
if(used > softlimit) {
|
210
|
-
isolate
|
359
|
+
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_REACHED, true);
|
211
360
|
isolate->TerminateExecution();
|
212
361
|
}
|
213
362
|
}
|
@@ -232,11 +381,13 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
232
381
|
Local<Value> local_value = v8res.ToLocalChecked();
|
233
382
|
if ((local_value->IsObject() || local_value->IsArray()) &&
|
234
383
|
!local_value->IsDate() && !local_value->IsFunction()) {
|
235
|
-
Local<Object> JSON = context->Global()->Get(
|
236
|
-
|
384
|
+
Local<Object> JSON = context->Global()->Get(
|
385
|
+
context, String::NewFromUtf8Literal(isolate, "JSON"))
|
386
|
+
.ToLocalChecked().As<Object>();
|
237
387
|
|
238
|
-
Local<Function> stringify = JSON->Get(
|
239
|
-
|
388
|
+
Local<Function> stringify = JSON->Get(
|
389
|
+
context, v8::String::NewFromUtf8Literal(isolate, "stringify"))
|
390
|
+
.ToLocalChecked().As<Function>();
|
240
391
|
|
241
392
|
Local<Object> object = local_value->ToObject(context).ToLocalChecked();
|
242
393
|
const unsigned argc = 1;
|
@@ -290,7 +441,7 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
290
441
|
} else if(trycatch.HasTerminated()) {
|
291
442
|
evalRes.terminated = true;
|
292
443
|
evalRes.message = new Persistent<Value>();
|
293
|
-
Local<String> tmp = String::
|
444
|
+
Local<String> tmp = String::NewFromUtf8Literal(isolate, "JavaScript was terminated (either by timeout or explicitly)");
|
294
445
|
evalRes.message->Reset(isolate, tmp);
|
295
446
|
}
|
296
447
|
if (!trycatch.StackTrace(context).IsEmpty()) {
|
@@ -307,7 +458,8 @@ nogvl_context_eval(void* arg) {
|
|
307
458
|
|
308
459
|
EvalParams* eval_params = (EvalParams*)arg;
|
309
460
|
EvalResult* result = eval_params->result;
|
310
|
-
|
461
|
+
IsolateInfo* isolate_info = eval_params->context_info->isolate_info;
|
462
|
+
Isolate* isolate = isolate_info->isolate;
|
311
463
|
|
312
464
|
Isolate::Scope isolate_scope(isolate);
|
313
465
|
HandleScope handle_scope(isolate);
|
@@ -316,14 +468,15 @@ nogvl_context_eval(void* arg) {
|
|
316
468
|
Context::Scope context_scope(context);
|
317
469
|
v8::ScriptOrigin *origin = NULL;
|
318
470
|
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
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
|
+
}
|
327
480
|
|
328
481
|
MaybeLocal<Script> parsed_script;
|
329
482
|
|
@@ -349,9 +502,8 @@ nogvl_context_eval(void* arg) {
|
|
349
502
|
result->message->Reset(isolate, trycatch.Exception());
|
350
503
|
} else {
|
351
504
|
// parsing successful
|
352
|
-
if (eval_params->
|
353
|
-
isolate
|
354
|
-
isolate->AddGCEpilogueCallback(gc_callback);
|
505
|
+
if (eval_params->marshal_stackdepth > 0) {
|
506
|
+
StackCounter::SetMax(isolate, eval_params->marshal_stackdepth);
|
355
507
|
}
|
356
508
|
|
357
509
|
maybe_value = parsed_script.ToLocalChecked()->Run(context);
|
@@ -359,11 +511,17 @@ nogvl_context_eval(void* arg) {
|
|
359
511
|
|
360
512
|
prepare_result(maybe_value, trycatch, isolate, context, *result);
|
361
513
|
|
362
|
-
isolate
|
514
|
+
IsolateData::Set(isolate, IsolateData::IN_GVL, true);
|
363
515
|
|
364
516
|
return NULL;
|
365
517
|
}
|
366
518
|
|
519
|
+
static VALUE new_empty_failed_conv_obj() {
|
520
|
+
// TODO isolate code that translates execption to ruby
|
521
|
+
// exception so we can properly return it
|
522
|
+
return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
|
523
|
+
}
|
524
|
+
|
367
525
|
// assumes isolate locking is in place
|
368
526
|
static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
369
527
|
Local<Value> value) {
|
@@ -371,6 +529,18 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
371
529
|
Isolate::Scope isolate_scope(isolate);
|
372
530
|
HandleScope scope(isolate);
|
373
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
|
+
|
374
544
|
if (value->IsNull() || value->IsUndefined()){
|
375
545
|
return Qnil;
|
376
546
|
}
|
@@ -395,8 +565,11 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
395
565
|
VALUE rb_array = rb_ary_new();
|
396
566
|
Local<Array> arr = Local<Array>::Cast(value);
|
397
567
|
for(uint32_t i=0; i < arr->Length(); i++) {
|
398
|
-
|
399
|
-
|
568
|
+
MaybeLocal<Value> element = arr->Get(context, i);
|
569
|
+
if (element.IsEmpty()) {
|
570
|
+
continue;
|
571
|
+
}
|
572
|
+
VALUE rb_elem = convert_v8_to_ruby(isolate, context, element.ToLocalChecked());
|
400
573
|
if (rb_funcall(rb_elem, rb_intern("class"), 0) == rb_cFailedV8Conversion) {
|
401
574
|
return rb_elem;
|
402
575
|
}
|
@@ -426,18 +599,20 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
426
599
|
if (!maybe_props.IsEmpty()) {
|
427
600
|
Local<Array> props = maybe_props.ToLocalChecked();
|
428
601
|
for(uint32_t i=0; i < props->Length(); i++) {
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
602
|
+
MaybeLocal<Value> key = props->Get(context, i);
|
603
|
+
if (key.IsEmpty()) {
|
604
|
+
return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
|
605
|
+
}
|
606
|
+
VALUE rb_key = convert_v8_to_ruby(isolate, context, key.ToLocalChecked());
|
433
607
|
|
434
|
-
|
435
|
-
//
|
436
|
-
|
437
|
-
|
608
|
+
MaybeLocal<Value> prop_value = object->Get(context, key.ToLocalChecked());
|
609
|
+
// this may have failed due to Get raising
|
610
|
+
if (prop_value.IsEmpty() || trycatch.HasCaught()) {
|
611
|
+
return new_empty_failed_conv_obj();
|
438
612
|
}
|
439
613
|
|
440
|
-
VALUE rb_value = convert_v8_to_ruby(
|
614
|
+
VALUE rb_value = convert_v8_to_ruby(
|
615
|
+
isolate, context, prop_value.ToLocalChecked());
|
441
616
|
rb_hash_aset(rb_hash, rb_key, rb_value);
|
442
617
|
}
|
443
618
|
}
|
@@ -454,7 +629,7 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
454
629
|
rb_enc_find("utf-8")
|
455
630
|
);
|
456
631
|
|
457
|
-
return
|
632
|
+
return rb_str_intern(str_symbol);
|
458
633
|
}
|
459
634
|
|
460
635
|
MaybeLocal<String> rstr_maybe = value->ToString(context);
|
@@ -519,7 +694,8 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
519
694
|
length = RARRAY_LEN(value);
|
520
695
|
array = Array::New(isolate, (int)length);
|
521
696
|
for(i=0; i<length; i++) {
|
522
|
-
|
697
|
+
Maybe<bool> success = array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
|
698
|
+
(void)(success);
|
523
699
|
}
|
524
700
|
return scope.Escape(array);
|
525
701
|
case T_HASH:
|
@@ -528,8 +704,9 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
528
704
|
length = RARRAY_LEN(hash_as_array);
|
529
705
|
for(i=0; i<length; i++) {
|
530
706
|
pair = rb_ary_entry(hash_as_array, i);
|
531
|
-
|
707
|
+
Maybe<bool> success = object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
|
532
708
|
convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 1)));
|
709
|
+
(void)(success);
|
533
710
|
}
|
534
711
|
return scope.Escape(object);
|
535
712
|
case T_SYMBOL:
|
@@ -558,9 +735,9 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
558
735
|
case T_UNDEF:
|
559
736
|
case T_NODE:
|
560
737
|
default:
|
561
|
-
return scope.Escape(String::
|
738
|
+
return scope.Escape(String::NewFromUtf8Literal(isolate, "Undefined Conversion"));
|
739
|
+
}
|
562
740
|
}
|
563
|
-
}
|
564
741
|
|
565
742
|
static void unblock_eval(void *ptr) {
|
566
743
|
EvalParams* eval = (EvalParams*)ptr;
|
@@ -571,53 +748,43 @@ static void unblock_eval(void *ptr) {
|
|
571
748
|
* The implementations of the run_extra_code(), create_snapshot_data_blob() and
|
572
749
|
* warm_up_snapshot_data_blob() functions have been derived from V8's test suite.
|
573
750
|
*/
|
574
|
-
bool run_extra_code(Isolate *isolate, Local<v8::Context> context,
|
751
|
+
static bool run_extra_code(Isolate *isolate, Local<v8::Context> context,
|
575
752
|
const char *utf8_source, const char *name) {
|
576
753
|
Context::Scope context_scope(context);
|
577
754
|
TryCatch try_catch(isolate);
|
578
755
|
Local<String> source_string;
|
579
|
-
if (!String::NewFromUtf8(isolate, utf8_source
|
580
|
-
NewStringType::kNormal)
|
581
|
-
.ToLocal(&source_string)) {
|
756
|
+
if (!String::NewFromUtf8(isolate, utf8_source).ToLocal(&source_string)) {
|
582
757
|
return false;
|
583
758
|
}
|
584
|
-
Local<
|
585
|
-
|
586
|
-
.ToLocalChecked();
|
759
|
+
Local<String> resource_name =
|
760
|
+
String::NewFromUtf8(isolate, name).ToLocalChecked();
|
587
761
|
ScriptOrigin origin(resource_name);
|
588
762
|
ScriptCompiler::Source source(source_string, origin);
|
589
763
|
Local<Script> script;
|
590
764
|
if (!ScriptCompiler::Compile(context, &source).ToLocal(&script))
|
591
765
|
return false;
|
592
|
-
if (script->Run(context).IsEmpty())
|
593
|
-
return false;
|
594
|
-
// CHECK(!try_catch.HasCaught());
|
766
|
+
if (script->Run(context).IsEmpty()) return false;
|
595
767
|
return true;
|
596
768
|
}
|
597
769
|
|
598
|
-
StartupData
|
770
|
+
static StartupData
|
599
771
|
create_snapshot_data_blob(const char *embedded_source = nullptr) {
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
SnapshotCreator snapshot_creator;
|
605
|
-
Isolate *isolate = snapshot_creator.GetIsolate();
|
772
|
+
Isolate *isolate = Isolate::Allocate();
|
773
|
+
|
774
|
+
// Optionally run a script to embed, and serialize to create a snapshot blob.
|
775
|
+
SnapshotCreator snapshot_creator(isolate);
|
606
776
|
{
|
607
777
|
HandleScope scope(isolate);
|
608
|
-
|
778
|
+
Local<v8::Context> context = v8::Context::New(isolate);
|
609
779
|
if (embedded_source != nullptr &&
|
610
|
-
!run_extra_code(isolate, context, embedded_source,
|
611
|
-
|
612
|
-
return result;
|
780
|
+
!run_extra_code(isolate, context, embedded_source, "<embedded>")) {
|
781
|
+
return {};
|
613
782
|
}
|
614
783
|
snapshot_creator.SetDefaultContext(context);
|
615
784
|
}
|
616
|
-
|
785
|
+
return snapshot_creator.CreateBlob(
|
617
786
|
SnapshotCreator::FunctionCodeHandling::kClear);
|
618
787
|
}
|
619
|
-
return result;
|
620
|
-
}
|
621
788
|
|
622
789
|
StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
|
623
790
|
const char *warmup_source) {
|
@@ -764,6 +931,29 @@ static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
|
|
764
931
|
return isolate_info->isolate->IdleNotificationDeadline(now + duration) ? Qtrue : Qfalse;
|
765
932
|
}
|
766
933
|
|
934
|
+
static VALUE rb_isolate_low_memory_notification(VALUE self) {
|
935
|
+
IsolateInfo* isolate_info;
|
936
|
+
Data_Get_Struct(self, IsolateInfo, isolate_info);
|
937
|
+
|
938
|
+
if (current_platform == NULL) return Qfalse;
|
939
|
+
|
940
|
+
isolate_info->isolate->LowMemoryNotification();
|
941
|
+
return Qnil;
|
942
|
+
}
|
943
|
+
|
944
|
+
static VALUE rb_isolate_pump_message_loop(VALUE self) {
|
945
|
+
IsolateInfo* isolate_info;
|
946
|
+
Data_Get_Struct(self, IsolateInfo, isolate_info);
|
947
|
+
|
948
|
+
if (current_platform == NULL) return Qfalse;
|
949
|
+
|
950
|
+
if (platform::PumpMessageLoop(current_platform.get(), isolate_info->isolate)){
|
951
|
+
return Qtrue;
|
952
|
+
} else {
|
953
|
+
return Qfalse;
|
954
|
+
}
|
955
|
+
}
|
956
|
+
|
767
957
|
static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
|
768
958
|
ContextInfo* context_info;
|
769
959
|
Data_Get_Struct(self, ContextInfo, context_info);
|
@@ -847,9 +1037,14 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
847
1037
|
if (!result.executed) {
|
848
1038
|
VALUE ruby_exception = rb_iv_get(self, "@current_exception");
|
849
1039
|
if (ruby_exception == Qnil) {
|
850
|
-
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);
|
851
1042
|
// If we were terminated or have the memory softlimit flag set
|
852
|
-
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) {
|
853
1048
|
ruby_exception = mem_softlimit_reached ? rb_eV8OutOfMemoryError : rb_eScriptTerminatedError;
|
854
1049
|
} else {
|
855
1050
|
ruby_exception = rb_eScriptRuntimeError;
|
@@ -884,6 +1079,7 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
884
1079
|
VALUE json_string = rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
|
885
1080
|
ret = rb_funcall(rb_mJSON, rb_intern("parse"), 1, json_string);
|
886
1081
|
} else {
|
1082
|
+
StackCounter::Reset(isolate);
|
887
1083
|
ret = convert_v8_to_ruby(isolate, *p_ctx, tmp);
|
888
1084
|
}
|
889
1085
|
|
@@ -941,6 +1137,7 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
|
|
941
1137
|
eval_params.result = &eval_result;
|
942
1138
|
eval_params.timeout = 0;
|
943
1139
|
eval_params.max_memory = 0;
|
1140
|
+
eval_params.marshal_stackdepth = 0;
|
944
1141
|
VALUE timeout = rb_iv_get(self, "@timeout");
|
945
1142
|
if (timeout != Qnil) {
|
946
1143
|
eval_params.timeout = (useconds_t)NUM2LONG(timeout);
|
@@ -951,6 +1148,11 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
|
|
951
1148
|
eval_params.max_memory = (size_t)NUM2ULONG(mem_softlimit);
|
952
1149
|
}
|
953
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
|
+
|
954
1156
|
eval_result.message = NULL;
|
955
1157
|
eval_result.backtrace = NULL;
|
956
1158
|
|
@@ -1020,6 +1222,7 @@ gvl_ruby_callback(void* data) {
|
|
1020
1222
|
|
1021
1223
|
for (int i = 0; i < length; i++) {
|
1022
1224
|
Local<Value> value = ((*args)[i]).As<Value>();
|
1225
|
+
StackCounter::Reset(args->GetIsolate());
|
1023
1226
|
VALUE tmp = convert_v8_to_ruby(args->GetIsolate(),
|
1024
1227
|
*context_info->context, value);
|
1025
1228
|
rb_ary_push(ruby_args, tmp);
|
@@ -1033,8 +1236,10 @@ gvl_ruby_callback(void* data) {
|
|
1033
1236
|
callback_data.ruby_args = ruby_args;
|
1034
1237
|
callback_data.failed = false;
|
1035
1238
|
|
1036
|
-
if ((
|
1037
|
-
args->GetIsolate()->ThrowException(
|
1239
|
+
if (IsolateData::Get(args->GetIsolate(), IsolateData::DO_TERMINATE)) {
|
1240
|
+
args->GetIsolate()->ThrowException(
|
1241
|
+
String::NewFromUtf8Literal(args->GetIsolate(),
|
1242
|
+
"Terminated execution during transition from Ruby to JS"));
|
1038
1243
|
args->GetIsolate()->TerminateExecution();
|
1039
1244
|
if (length > 0) {
|
1040
1245
|
rb_ary_clear(ruby_args);
|
@@ -1043,12 +1248,15 @@ gvl_ruby_callback(void* data) {
|
|
1043
1248
|
return NULL;
|
1044
1249
|
}
|
1045
1250
|
|
1046
|
-
|
1047
|
-
|
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);
|
1048
1256
|
|
1049
1257
|
if(callback_data.failed) {
|
1050
1258
|
rb_iv_set(parent, "@current_exception", result);
|
1051
|
-
args->GetIsolate()->ThrowException(String::
|
1259
|
+
args->GetIsolate()->ThrowException(String::NewFromUtf8Literal(args->GetIsolate(), "Ruby exception"));
|
1052
1260
|
}
|
1053
1261
|
else {
|
1054
1262
|
HandleScope scope(args->GetIsolate());
|
@@ -1061,23 +1269,22 @@ gvl_ruby_callback(void* data) {
|
|
1061
1269
|
rb_gc_force_recycle(ruby_args);
|
1062
1270
|
}
|
1063
1271
|
|
1064
|
-
if ((
|
1065
|
-
|
1066
|
-
isolate->TerminateExecution();
|
1272
|
+
if (IsolateData::Get(args->GetIsolate(), IsolateData::DO_TERMINATE)) {
|
1273
|
+
args->GetIsolate()->TerminateExecution();
|
1067
1274
|
}
|
1068
1275
|
|
1069
1276
|
return NULL;
|
1070
1277
|
}
|
1071
1278
|
|
1072
1279
|
static void ruby_callback(const FunctionCallbackInfo<Value>& args) {
|
1073
|
-
bool has_gvl = (
|
1280
|
+
bool has_gvl = IsolateData::Get(args.GetIsolate(), IsolateData::IN_GVL);
|
1074
1281
|
|
1075
1282
|
if(has_gvl) {
|
1076
1283
|
gvl_ruby_callback((void*)&args);
|
1077
1284
|
} else {
|
1078
|
-
args.GetIsolate()
|
1285
|
+
IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, true);
|
1079
1286
|
rb_thread_call_with_gvl(gvl_ruby_callback, (void*)(&args));
|
1080
|
-
args.GetIsolate()
|
1287
|
+
IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, false);
|
1081
1288
|
}
|
1082
1289
|
}
|
1083
1290
|
|
@@ -1118,10 +1325,13 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1118
1325
|
Local<Value> external = External::New(isolate, self_copy);
|
1119
1326
|
|
1120
1327
|
if (parent_object == Qnil) {
|
1121
|
-
context->Global()->Set(
|
1122
|
-
|
1328
|
+
Maybe<bool> success = context->Global()->Set(
|
1329
|
+
context,
|
1330
|
+
v8_str,
|
1331
|
+
FunctionTemplate::New(isolate, ruby_callback, external)
|
1123
1332
|
->GetFunction(context)
|
1124
1333
|
.ToLocalChecked());
|
1334
|
+
(void)success;
|
1125
1335
|
|
1126
1336
|
} else {
|
1127
1337
|
Local<String> eval =
|
@@ -1132,7 +1342,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1132
1342
|
|
1133
1343
|
MaybeLocal<Script> parsed_script = Script::Compile(context, eval);
|
1134
1344
|
if (parsed_script.IsEmpty()) {
|
1135
|
-
|
1345
|
+
parse_error = true;
|
1136
1346
|
} else {
|
1137
1347
|
MaybeLocal<Value> maybe_value =
|
1138
1348
|
parsed_script.ToLocalChecked()->Run(context);
|
@@ -1141,12 +1351,14 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1141
1351
|
if (!maybe_value.IsEmpty()) {
|
1142
1352
|
Local<Value> value = maybe_value.ToLocalChecked();
|
1143
1353
|
if (value->IsObject()) {
|
1144
|
-
value.As<Object>()->Set(
|
1145
|
-
|
1146
|
-
|
1354
|
+
Maybe<bool> success = value.As<Object>()->Set(
|
1355
|
+
context,
|
1356
|
+
v8_str,
|
1357
|
+
FunctionTemplate::New(isolate, ruby_callback, external)
|
1147
1358
|
->GetFunction(context)
|
1148
1359
|
.ToLocalChecked());
|
1149
|
-
|
1360
|
+
(void)success;
|
1361
|
+
attach_error = false;
|
1150
1362
|
}
|
1151
1363
|
}
|
1152
1364
|
}
|
@@ -1176,35 +1388,39 @@ static VALUE rb_context_isolate_mutex(VALUE self) {
|
|
1176
1388
|
return context_info->isolate_info->mutex;
|
1177
1389
|
}
|
1178
1390
|
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1185
|
-
if (isolate_info->isolate) {
|
1186
|
-
if (isolate_info->interrupted) {
|
1187
|
-
fprintf(stderr, "WARNING: V8 isolate was interrupted by Ruby, it can not be disposed and memory will not be reclaimed till the Ruby process exits.\n");
|
1391
|
+
IsolateInfo::~IsolateInfo() {
|
1392
|
+
if (isolate) {
|
1393
|
+
if (this->interrupted) {
|
1394
|
+
fprintf(stderr, "WARNING: V8 isolate was interrupted by Ruby, "
|
1395
|
+
"it can not be disposed and memory will not be "
|
1396
|
+
"reclaimed till the Ruby process exits.\n");
|
1188
1397
|
} else {
|
1189
|
-
|
1190
|
-
|
1191
|
-
|
1398
|
+
if (this->pid != getpid() && !single_threaded) {
|
1399
|
+
fprintf(stderr, "WARNING: V8 isolate was forked, "
|
1400
|
+
"it can not be disposed and "
|
1401
|
+
"memory will not be reclaimed "
|
1402
|
+
"till the Ruby process exits.\n"
|
1403
|
+
"It is VERY likely your process will hang.\n"
|
1404
|
+
"If you wish to use v8 in forked environment "
|
1405
|
+
"please ensure the platform is initialized with:\n"
|
1406
|
+
"MiniRacer::Platform.set_flags! :single_threaded\n"
|
1407
|
+
);
|
1192
1408
|
} else {
|
1193
|
-
|
1409
|
+
isolate->Dispose();
|
1194
1410
|
}
|
1195
1411
|
}
|
1196
|
-
|
1412
|
+
isolate = nullptr;
|
1197
1413
|
}
|
1198
1414
|
|
1199
|
-
if (
|
1200
|
-
delete[]
|
1201
|
-
delete
|
1415
|
+
if (startup_data) {
|
1416
|
+
delete[] startup_data->data;
|
1417
|
+
delete startup_data;
|
1202
1418
|
}
|
1203
1419
|
|
1204
|
-
delete
|
1420
|
+
delete allocator;
|
1205
1421
|
}
|
1206
1422
|
|
1207
|
-
static void
|
1423
|
+
static void free_context_raw(void *arg) {
|
1208
1424
|
ContextInfo* context_info = (ContextInfo*)arg;
|
1209
1425
|
IsolateInfo* isolate_info = context_info->isolate_info;
|
1210
1426
|
Persistent<Context>* context = context_info->context;
|
@@ -1221,6 +1437,20 @@ static void *free_context_raw(void* arg) {
|
|
1221
1437
|
}
|
1222
1438
|
|
1223
1439
|
xfree(context_info);
|
1440
|
+
}
|
1441
|
+
|
1442
|
+
static void *free_context_thr(void* arg) {
|
1443
|
+
if (pthread_rwlock_tryrdlock(&exit_lock) != 0) {
|
1444
|
+
return NULL;
|
1445
|
+
}
|
1446
|
+
if (ruby_exiting) {
|
1447
|
+
return NULL;
|
1448
|
+
}
|
1449
|
+
|
1450
|
+
free_context_raw(arg);
|
1451
|
+
|
1452
|
+
pthread_rwlock_unlock(&exit_lock);
|
1453
|
+
|
1224
1454
|
return NULL;
|
1225
1455
|
}
|
1226
1456
|
|
@@ -1234,22 +1464,17 @@ static void free_context(ContextInfo* context_info) {
|
|
1234
1464
|
context_info_copy->context = context_info->context;
|
1235
1465
|
|
1236
1466
|
if (isolate_info && isolate_info->refs() > 1) {
|
1237
|
-
|
1238
|
-
|
1239
|
-
|
1240
|
-
|
1241
|
-
|
1467
|
+
pthread_t free_context_thread;
|
1468
|
+
if (pthread_create(&free_context_thread, thread_attr_p,
|
1469
|
+
free_context_thr, (void*)context_info_copy)) {
|
1470
|
+
fprintf(stderr, "WARNING failed to release memory in MiniRacer, thread to release could not be created, process will leak memory\n");
|
1471
|
+
}
|
1242
1472
|
} else {
|
1243
1473
|
free_context_raw(context_info_copy);
|
1244
1474
|
}
|
1245
1475
|
|
1246
|
-
|
1247
|
-
|
1248
|
-
}
|
1249
|
-
|
1250
|
-
if (isolate_info) {
|
1251
|
-
context_info->isolate_info = NULL;
|
1252
|
-
}
|
1476
|
+
context_info->context = NULL;
|
1477
|
+
context_info->isolate_info = NULL;
|
1253
1478
|
}
|
1254
1479
|
|
1255
1480
|
static void deallocate_isolate(void* data) {
|
@@ -1264,7 +1489,7 @@ static void mark_isolate(void* data) {
|
|
1264
1489
|
isolate_info->mark();
|
1265
1490
|
}
|
1266
1491
|
|
1267
|
-
void deallocate(void* data) {
|
1492
|
+
static void deallocate(void* data) {
|
1268
1493
|
ContextInfo* context_info = (ContextInfo*)data;
|
1269
1494
|
|
1270
1495
|
free_context(context_info);
|
@@ -1279,22 +1504,22 @@ static void mark_context(void* data) {
|
|
1279
1504
|
}
|
1280
1505
|
}
|
1281
1506
|
|
1282
|
-
void deallocate_external_function(void * data) {
|
1507
|
+
static void deallocate_external_function(void * data) {
|
1283
1508
|
xfree(data);
|
1284
1509
|
}
|
1285
1510
|
|
1286
|
-
void deallocate_snapshot(void * data) {
|
1511
|
+
static void deallocate_snapshot(void * data) {
|
1287
1512
|
SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
|
1288
1513
|
delete[] snapshot_info->data;
|
1289
1514
|
xfree(snapshot_info);
|
1290
1515
|
}
|
1291
1516
|
|
1292
|
-
VALUE allocate_external_function(VALUE klass) {
|
1517
|
+
static VALUE allocate_external_function(VALUE klass) {
|
1293
1518
|
VALUE* self = ALLOC(VALUE);
|
1294
1519
|
return Data_Wrap_Struct(klass, NULL, deallocate_external_function, (void*)self);
|
1295
1520
|
}
|
1296
1521
|
|
1297
|
-
VALUE allocate(VALUE klass) {
|
1522
|
+
static VALUE allocate(VALUE klass) {
|
1298
1523
|
ContextInfo* context_info = ALLOC(ContextInfo);
|
1299
1524
|
context_info->isolate_info = NULL;
|
1300
1525
|
context_info->context = NULL;
|
@@ -1302,7 +1527,7 @@ VALUE allocate(VALUE klass) {
|
|
1302
1527
|
return Data_Wrap_Struct(klass, mark_context, deallocate, (void*)context_info);
|
1303
1528
|
}
|
1304
1529
|
|
1305
|
-
VALUE allocate_snapshot(VALUE klass) {
|
1530
|
+
static VALUE allocate_snapshot(VALUE klass) {
|
1306
1531
|
SnapshotInfo* snapshot_info = ALLOC(SnapshotInfo);
|
1307
1532
|
snapshot_info->data = NULL;
|
1308
1533
|
snapshot_info->raw_size = 0;
|
@@ -1310,7 +1535,7 @@ VALUE allocate_snapshot(VALUE klass) {
|
|
1310
1535
|
return Data_Wrap_Struct(klass, NULL, deallocate_snapshot, (void*)snapshot_info);
|
1311
1536
|
}
|
1312
1537
|
|
1313
|
-
VALUE allocate_isolate(VALUE klass) {
|
1538
|
+
static VALUE allocate_isolate(VALUE klass) {
|
1314
1539
|
IsolateInfo* isolate_info = new IsolateInfo();
|
1315
1540
|
|
1316
1541
|
return Data_Wrap_Struct(klass, mark_isolate, deallocate_isolate, (void*)isolate_info);
|
@@ -1407,6 +1632,8 @@ rb_heap_snapshot(VALUE self, VALUE file) {
|
|
1407
1632
|
FileOutputStream stream(fp);
|
1408
1633
|
snap->Serialize(&stream, HeapSnapshot::kJSON);
|
1409
1634
|
|
1635
|
+
fflush(fp);
|
1636
|
+
|
1410
1637
|
const_cast<HeapSnapshot*>(snap)->Delete();
|
1411
1638
|
|
1412
1639
|
return Qtrue;
|
@@ -1421,7 +1648,7 @@ rb_context_stop(VALUE self) {
|
|
1421
1648
|
Isolate* isolate = context_info->isolate_info->isolate;
|
1422
1649
|
|
1423
1650
|
// flag for termination
|
1424
|
-
isolate
|
1651
|
+
IsolateData::Set(isolate, IsolateData::DO_TERMINATE, true);
|
1425
1652
|
|
1426
1653
|
isolate->TerminateExecution();
|
1427
1654
|
rb_funcall(self, rb_intern("stop_attached"), 0);
|
@@ -1447,12 +1674,24 @@ nogvl_context_call(void *args) {
|
|
1447
1674
|
if (!call) {
|
1448
1675
|
return NULL;
|
1449
1676
|
}
|
1450
|
-
|
1677
|
+
IsolateInfo *isolate_info = call->context_info->isolate_info;
|
1678
|
+
Isolate* isolate = isolate_info->isolate;
|
1679
|
+
|
1680
|
+
IsolateData::Set(isolate, IsolateData::IN_GVL, false);
|
1681
|
+
IsolateData::Set(isolate, IsolateData::DO_TERMINATE, false);
|
1451
1682
|
|
1452
|
-
|
1453
|
-
|
1454
|
-
|
1455
|
-
|
1683
|
+
if (call->max_memory > 0) {
|
1684
|
+
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_MAX, call->max_memory);
|
1685
|
+
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_REACHED, false);
|
1686
|
+
if (!isolate_info->added_gc_cb) {
|
1687
|
+
isolate->AddGCEpilogueCallback(gc_callback);
|
1688
|
+
isolate_info->added_gc_cb = true;
|
1689
|
+
}
|
1690
|
+
}
|
1691
|
+
|
1692
|
+
if (call->marshal_stackdepth > 0) {
|
1693
|
+
StackCounter::SetMax(isolate, call->marshal_stackdepth);
|
1694
|
+
}
|
1456
1695
|
|
1457
1696
|
Isolate::Scope isolate_scope(isolate);
|
1458
1697
|
EscapableHandleScope handle_scope(isolate);
|
@@ -1469,7 +1708,7 @@ nogvl_context_call(void *args) {
|
|
1469
1708
|
MaybeLocal<v8::Value> res = fun->Call(context, context->Global(), call->argc, call->argv);
|
1470
1709
|
prepare_result(res, trycatch, isolate, context, eval_res);
|
1471
1710
|
|
1472
|
-
isolate
|
1711
|
+
IsolateData::Set(isolate, IsolateData::IN_GVL, true);
|
1473
1712
|
|
1474
1713
|
return NULL;
|
1475
1714
|
}
|
@@ -1511,6 +1750,20 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1511
1750
|
call_argv = argv + 1;
|
1512
1751
|
}
|
1513
1752
|
|
1753
|
+
call.max_memory = 0;
|
1754
|
+
VALUE mem_softlimit = rb_iv_get(self, "@max_memory");
|
1755
|
+
if (mem_softlimit != Qnil) {
|
1756
|
+
unsigned long sl_int = NUM2ULONG(mem_softlimit);
|
1757
|
+
call.max_memory = (size_t)sl_int;
|
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
|
+
}
|
1766
|
+
|
1514
1767
|
bool missingFunction = false;
|
1515
1768
|
{
|
1516
1769
|
Locker lock(isolate);
|
@@ -1522,8 +1775,11 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1522
1775
|
|
1523
1776
|
// examples of such usage can be found in
|
1524
1777
|
// https://github.com/v8/v8/blob/36b32aa28db5e993312f4588d60aad5c8330c8a5/test/cctest/test-api.cc#L15711
|
1525
|
-
|
1526
|
-
MaybeLocal<v8::Value> val
|
1778
|
+
MaybeLocal<String> fname = String::NewFromUtf8(isolate, call.function_name);
|
1779
|
+
MaybeLocal<v8::Value> val;
|
1780
|
+
if (!fname.IsEmpty()) {
|
1781
|
+
val = context->Global()->Get(context, fname.ToLocalChecked());
|
1782
|
+
}
|
1527
1783
|
|
1528
1784
|
if (val.IsEmpty() || !val.ToLocalChecked()->IsFunction()) {
|
1529
1785
|
missingFunction = true;
|
@@ -1568,9 +1824,20 @@ static VALUE rb_context_create_isolate_value(VALUE self) {
|
|
1568
1824
|
return Data_Wrap_Struct(rb_cIsolate, NULL, &deallocate_isolate, isolate_info);
|
1569
1825
|
}
|
1570
1826
|
|
1827
|
+
static void set_ruby_exiting(VALUE value) {
|
1828
|
+
(void)value;
|
1829
|
+
|
1830
|
+
int res = pthread_rwlock_wrlock(&exit_lock);
|
1831
|
+
|
1832
|
+
ruby_exiting = true;
|
1833
|
+
if (res == 0) {
|
1834
|
+
pthread_rwlock_unlock(&exit_lock);
|
1835
|
+
}
|
1836
|
+
}
|
1837
|
+
|
1571
1838
|
extern "C" {
|
1572
1839
|
|
1573
|
-
void Init_mini_racer_extension ( void )
|
1840
|
+
__attribute__((visibility("default"))) void Init_mini_racer_extension ( void )
|
1574
1841
|
{
|
1575
1842
|
VALUE rb_mMiniRacer = rb_define_module("MiniRacer");
|
1576
1843
|
rb_cContext = rb_define_class_under(rb_mMiniRacer, "Context", rb_cObject);
|
@@ -1618,8 +1885,23 @@ extern "C" {
|
|
1618
1885
|
rb_define_private_method(rb_cSnapshot, "load", (VALUE(*)(...))&rb_snapshot_load, 1);
|
1619
1886
|
|
1620
1887
|
rb_define_method(rb_cIsolate, "idle_notification", (VALUE(*)(...))&rb_isolate_idle_notification, 1);
|
1888
|
+
rb_define_method(rb_cIsolate, "low_memory_notification", (VALUE(*)(...))&rb_isolate_low_memory_notification, 0);
|
1889
|
+
rb_define_method(rb_cIsolate, "pump_message_loop", (VALUE(*)(...))&rb_isolate_pump_message_loop, 0);
|
1621
1890
|
rb_define_private_method(rb_cIsolate, "init_with_snapshot",(VALUE(*)(...))&rb_isolate_init_with_snapshot, 1);
|
1622
1891
|
|
1623
1892
|
rb_define_singleton_method(rb_cPlatform, "set_flag_as_str!", (VALUE(*)(...))&rb_platform_set_flag_as_str, 1);
|
1893
|
+
|
1894
|
+
rb_set_end_proc(set_ruby_exiting, Qnil);
|
1895
|
+
|
1896
|
+
static pthread_attr_t attr;
|
1897
|
+
if (pthread_attr_init(&attr) == 0) {
|
1898
|
+
if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == 0) {
|
1899
|
+
thread_attr_p = &attr;
|
1900
|
+
}
|
1901
|
+
}
|
1902
|
+
auto on_fork_for_child = []() {
|
1903
|
+
exit_lock = PTHREAD_RWLOCK_INITIALIZER;
|
1904
|
+
};
|
1905
|
+
pthread_atfork(nullptr, nullptr, on_fork_for_child);
|
1624
1906
|
}
|
1625
1907
|
}
|