mini_racer 0.2.10 → 0.6.2

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.
@@ -2,6 +2,7 @@
2
2
  #include <ruby.h>
3
3
  #include <ruby/thread.h>
4
4
  #include <ruby/io.h>
5
+ #include <ruby/version.h>
5
6
  #include <v8.h>
6
7
  #include <v8-profiler.h>
7
8
  #include <libplatform/libplatform.h>
@@ -11,6 +12,15 @@
11
12
  #include <mutex>
12
13
  #include <atomic>
13
14
  #include <math.h>
15
+ #include <errno.h>
16
+
17
+ /* workaround C Ruby <= 2.x problems w/ clang in C++ mode */
18
+ #if defined(ENGINE_IS_CRUBY) && \
19
+ RUBY_API_VERSION_MAJOR == 2 && RUBY_API_VERSION_MINOR <= 6
20
+ # define MR_METHOD_FUNC(fn) RUBY_METHOD_FUNC(fn)
21
+ #else
22
+ # define MR_METHOD_FUNC(fn) fn
23
+ #endif
14
24
 
15
25
  using namespace v8;
16
26
 
@@ -25,6 +35,7 @@ public:
25
35
  ArrayBuffer::Allocator* allocator;
26
36
  StartupData* startup_data;
27
37
  bool interrupted;
38
+ bool added_gc_cb;
28
39
  pid_t pid;
29
40
  VALUE mutex;
30
41
 
@@ -42,15 +53,12 @@ public:
42
53
 
43
54
 
44
55
  IsolateInfo() : isolate(nullptr), allocator(nullptr), startup_data(nullptr),
45
- interrupted(false), pid(getpid()), refs_count(0) {
56
+ interrupted(false), added_gc_cb(false), pid(getpid()), refs_count(0) {
46
57
  VALUE cMutex = rb_const_get(rb_cThread, rb_intern("Mutex"));
47
58
  mutex = rb_class_new_instance(0, nullptr, cMutex);
48
59
  }
49
60
 
50
- ~IsolateInfo() {
51
- void free_isolate(IsolateInfo*);
52
- free_isolate(this);
53
- }
61
+ ~IsolateInfo();
54
62
 
55
63
  void init(SnapshotInfo* snapshot_info = nullptr);
56
64
 
@@ -117,6 +125,7 @@ typedef struct {
117
125
  useconds_t timeout;
118
126
  EvalResult* result;
119
127
  size_t max_memory;
128
+ size_t marshal_stackdepth;
120
129
  } EvalParams;
121
130
 
122
131
  typedef struct {
@@ -128,13 +137,152 @@ typedef struct {
128
137
  Local<Value> *argv;
129
138
  EvalResult result;
130
139
  size_t max_memory;
140
+ size_t marshal_stackdepth;
131
141
  } FunctionCall;
132
142
 
133
- enum IsolateFlags {
134
- IN_GVL,
135
- DO_TERMINATE,
136
- MEM_SOFTLIMIT_VALUE,
137
- MEM_SOFTLIMIT_REACHED,
143
+ class IsolateData {
144
+ public:
145
+ enum Flag {
146
+ // first flags are bitfield
147
+ // max count: sizeof(uintptr_t) * 8
148
+ IN_GVL, // whether we are inside of ruby gvl or not
149
+ DO_TERMINATE, // terminate as soon as possible
150
+ MEM_SOFTLIMIT_REACHED, // we've hit the memory soft limit
151
+ MEM_SOFTLIMIT_MAX, // maximum memory value
152
+ MARSHAL_STACKDEPTH_REACHED, // we've hit our max stack depth
153
+ MARSHAL_STACKDEPTH_VALUE, // current stackdepth
154
+ MARSHAL_STACKDEPTH_MAX, // maximum stack depth during marshal
155
+ };
156
+
157
+ static void Init(Isolate *isolate) {
158
+ // zero out all fields in the bitfield
159
+ isolate->SetData(0, 0);
160
+ }
161
+
162
+ static uintptr_t Get(Isolate *isolate, Flag flag) {
163
+ Bitfield u = { reinterpret_cast<uint64_t>(isolate->GetData(0)) };
164
+ switch (flag) {
165
+ case IN_GVL: return u.IN_GVL;
166
+ case DO_TERMINATE: return u.DO_TERMINATE;
167
+ case MEM_SOFTLIMIT_REACHED: return u.MEM_SOFTLIMIT_REACHED;
168
+ case MEM_SOFTLIMIT_MAX: return static_cast<uintptr_t>(u.MEM_SOFTLIMIT_MAX) << 10;
169
+ case MARSHAL_STACKDEPTH_REACHED: return u.MARSHAL_STACKDEPTH_REACHED;
170
+ case MARSHAL_STACKDEPTH_VALUE: return u.MARSHAL_STACKDEPTH_VALUE;
171
+ case MARSHAL_STACKDEPTH_MAX: return u.MARSHAL_STACKDEPTH_MAX;
172
+ }
173
+
174
+ // avoid compiler warning
175
+ return u.IN_GVL;
176
+ }
177
+
178
+ static void Set(Isolate *isolate, Flag flag, uintptr_t value) {
179
+ Bitfield u = { reinterpret_cast<uint64_t>(isolate->GetData(0)) };
180
+ switch (flag) {
181
+ case IN_GVL: u.IN_GVL = value; break;
182
+ case DO_TERMINATE: u.DO_TERMINATE = value; break;
183
+ case MEM_SOFTLIMIT_REACHED: u.MEM_SOFTLIMIT_REACHED = value; break;
184
+ // drop least significant 10 bits 'store memory amount in kb'
185
+ case MEM_SOFTLIMIT_MAX: u.MEM_SOFTLIMIT_MAX = value >> 10; break;
186
+ case MARSHAL_STACKDEPTH_REACHED: u.MARSHAL_STACKDEPTH_REACHED = value; break;
187
+ case MARSHAL_STACKDEPTH_VALUE: u.MARSHAL_STACKDEPTH_VALUE = value; break;
188
+ case MARSHAL_STACKDEPTH_MAX: u.MARSHAL_STACKDEPTH_MAX = value; break;
189
+ }
190
+ isolate->SetData(0, reinterpret_cast<void*>(u.dataPtr));
191
+ }
192
+
193
+ private:
194
+ struct Bitfield {
195
+ // WARNING: this would explode on platforms below 64 bit ptrs
196
+ // compiler will fail here, making it clear for them.
197
+ // Additionally, using the other part of the union to reinterpret the
198
+ // memory is undefined behavior according to spec, but is / has been stable
199
+ // across major compilers for decades.
200
+ static_assert(sizeof(uintptr_t) >= sizeof(uint64_t), "mini_racer not supported on this platform. ptr size must be at least 64 bit.");
201
+ union {
202
+ uint64_t dataPtr: 64;
203
+ // order in this struct matters. For cpu performance keep larger subobjects
204
+ // aligned on their boundaries (8 16 32), try not to straddle
205
+ struct {
206
+ size_t MEM_SOFTLIMIT_MAX:22;
207
+ bool IN_GVL:1;
208
+ bool DO_TERMINATE:1;
209
+ bool MEM_SOFTLIMIT_REACHED:1;
210
+ bool MARSHAL_STACKDEPTH_REACHED:1;
211
+ uint8_t :0; // align to next 8bit bound
212
+ size_t MARSHAL_STACKDEPTH_VALUE:10;
213
+ uint8_t :0; // align to next 8bit bound
214
+ size_t MARSHAL_STACKDEPTH_MAX:10;
215
+ };
216
+ };
217
+ };
218
+ };
219
+
220
+ struct StackCounter {
221
+ static void Reset(Isolate* isolate) {
222
+ if (IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_MAX) > 0) {
223
+ IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE, 0);
224
+ IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED, false);
225
+ }
226
+ }
227
+
228
+ static void SetMax(Isolate* isolate, size_t marshalMaxStackDepth) {
229
+ if (marshalMaxStackDepth > 0) {
230
+ IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_MAX, marshalMaxStackDepth);
231
+ IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE, 0);
232
+ IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED, false);
233
+ }
234
+ }
235
+
236
+ StackCounter(Isolate* isolate) {
237
+ this->isActive = IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_MAX) > 0;
238
+
239
+ if (this->isActive) {
240
+ this->isolate = isolate;
241
+ this->IncDepth(1);
242
+ }
243
+ }
244
+
245
+ bool IsTooDeep() {
246
+ if (!this->IsActive()) {
247
+ return false;
248
+ }
249
+
250
+ size_t depth = IsolateData::Get(this->isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE);
251
+ size_t maxDepth = IsolateData::Get(this->isolate, IsolateData::MARSHAL_STACKDEPTH_MAX);
252
+ if (depth > maxDepth) {
253
+ IsolateData::Set(this->isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED, true);
254
+ return true;
255
+ }
256
+
257
+ return false;
258
+ }
259
+
260
+ bool IsActive() {
261
+ return this->isActive && !IsolateData::Get(this->isolate, IsolateData::DO_TERMINATE);
262
+ }
263
+
264
+ ~StackCounter() {
265
+ if (this->IsActive()) {
266
+ this->IncDepth(-1);
267
+ }
268
+ }
269
+
270
+ private:
271
+ Isolate* isolate;
272
+ bool isActive;
273
+
274
+ void IncDepth(int direction) {
275
+ int inc = direction > 0 ? 1 : -1;
276
+
277
+ size_t depth = IsolateData::Get(this->isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE);
278
+
279
+ // don't decrement past 0
280
+ if (inc > 0 || depth > 0) {
281
+ depth += inc;
282
+ }
283
+
284
+ IsolateData::Set(this->isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE, depth);
285
+ }
138
286
  };
139
287
 
140
288
  static VALUE rb_cContext;
@@ -156,6 +304,34 @@ static VALUE rb_cDateTime = Qnil;
156
304
  static std::unique_ptr<Platform> current_platform = NULL;
157
305
  static std::mutex platform_lock;
158
306
 
307
+ static pthread_attr_t *thread_attr_p;
308
+ static pthread_rwlock_t exit_lock = PTHREAD_RWLOCK_INITIALIZER;
309
+ static bool ruby_exiting = false; // guarded by exit_lock
310
+ static bool single_threaded = false;
311
+
312
+ static void mark_context(void *);
313
+ static void deallocate(void *);
314
+ static size_t context_memsize(const void *);
315
+ static const rb_data_type_t context_type = {
316
+ "mini_racer/context_info",
317
+ { mark_context, deallocate, context_memsize }
318
+ };
319
+
320
+ static void deallocate_snapshot(void *);
321
+ static size_t snapshot_memsize(const void *);
322
+ static const rb_data_type_t snapshot_type = {
323
+ "mini_racer/snapshot_info",
324
+ { NULL, deallocate_snapshot, snapshot_memsize }
325
+ };
326
+
327
+ static void mark_isolate(void *);
328
+ static void deallocate_isolate(void *);
329
+ static size_t isolate_memsize(const void *);
330
+ static const rb_data_type_t isolate_type = {
331
+ "mini_racer/isolate_info",
332
+ { mark_isolate, deallocate_isolate, isolate_memsize }
333
+ };
334
+
159
335
  static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
160
336
  bool platform_already_initialized = false;
161
337
 
@@ -167,7 +343,10 @@ static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
167
343
  platform_lock.lock();
168
344
 
169
345
  if (current_platform == NULL) {
170
- V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), (int)RSTRING_LEN(flag_as_str));
346
+ if (!strcmp(RSTRING_PTR(flag_as_str), "--single_threaded")) {
347
+ single_threaded = true;
348
+ }
349
+ V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), RSTRING_LENINT(flag_as_str));
171
350
  } else {
172
351
  platform_already_initialized = true;
173
352
  }
@@ -190,7 +369,11 @@ static void init_v8() {
190
369
 
191
370
  if (current_platform == NULL) {
192
371
  V8::InitializeICU();
193
- current_platform = platform::NewDefaultPlatform();
372
+ if (single_threaded) {
373
+ current_platform = platform::NewSingleThreadedDefaultPlatform();
374
+ } else {
375
+ current_platform = platform::NewDefaultPlatform();
376
+ }
194
377
  V8::InitializePlatform(current_platform.get());
195
378
  V8::Initialize();
196
379
  }
@@ -199,16 +382,18 @@ static void init_v8() {
199
382
  }
200
383
 
201
384
  static void gc_callback(Isolate *isolate, GCType type, GCCallbackFlags flags) {
202
- if((bool)isolate->GetData(MEM_SOFTLIMIT_REACHED)) return;
385
+ if (IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_REACHED)) {
386
+ return;
387
+ }
203
388
 
204
- size_t softlimit = *(size_t*) isolate->GetData(MEM_SOFTLIMIT_VALUE);
389
+ size_t softlimit = IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_MAX);
205
390
 
206
391
  HeapStatistics stats;
207
392
  isolate->GetHeapStatistics(&stats);
208
393
  size_t used = stats.used_heap_size();
209
394
 
210
395
  if(used > softlimit) {
211
- isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)true);
396
+ IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_REACHED, true);
212
397
  isolate->TerminateExecution();
213
398
  }
214
399
  }
@@ -233,26 +418,32 @@ static void prepare_result(MaybeLocal<Value> v8res,
233
418
  Local<Value> local_value = v8res.ToLocalChecked();
234
419
  if ((local_value->IsObject() || local_value->IsArray()) &&
235
420
  !local_value->IsDate() && !local_value->IsFunction()) {
236
- Local<Object> JSON = context->Global()->Get(String::NewFromUtf8(isolate, "JSON"))
237
- ->ToObject(context).ToLocalChecked();
238
-
239
- Local<Function> stringify = JSON->Get(v8::String::NewFromUtf8(isolate, "stringify"))
240
- .As<Function>();
241
-
242
- Local<Object> object = local_value->ToObject(context).ToLocalChecked();
243
- const unsigned argc = 1;
244
- Local<Value> argv[argc] = { object };
245
- MaybeLocal<Value> json = stringify->Call(context, JSON, argc, argv);
421
+ MaybeLocal<v8::Value> ml = context->Global()->Get(
422
+ context, String::NewFromUtf8Literal(isolate, "JSON"));
246
423
 
247
- if (json.IsEmpty()) {
424
+ if (ml.IsEmpty()) { // exception
248
425
  evalRes.executed = false;
249
426
  } else {
250
- evalRes.json = true;
251
- Persistent<Value>* persistent = new Persistent<Value>();
252
- persistent->Reset(isolate, json.ToLocalChecked());
253
- evalRes.value = persistent;
427
+ Local<Object> JSON = ml.ToLocalChecked().As<Object>();
428
+
429
+ Local<Function> stringify = JSON->Get(
430
+ context, v8::String::NewFromUtf8Literal(isolate, "stringify"))
431
+ .ToLocalChecked().As<Function>();
432
+
433
+ Local<Object> object = local_value->ToObject(context).ToLocalChecked();
434
+ const unsigned argc = 1;
435
+ Local<Value> argv[argc] = { object };
436
+ MaybeLocal<Value> json = stringify->Call(context, JSON, argc, argv);
437
+
438
+ if (json.IsEmpty()) {
439
+ evalRes.executed = false;
440
+ } else {
441
+ evalRes.json = true;
442
+ Persistent<Value>* persistent = new Persistent<Value>();
443
+ persistent->Reset(isolate, json.ToLocalChecked());
444
+ evalRes.value = persistent;
445
+ }
254
446
  }
255
-
256
447
  } else {
257
448
  Persistent<Value>* persistent = new Persistent<Value>();
258
449
  persistent->Reset(isolate, local_value);
@@ -291,7 +482,7 @@ static void prepare_result(MaybeLocal<Value> v8res,
291
482
  } else if(trycatch.HasTerminated()) {
292
483
  evalRes.terminated = true;
293
484
  evalRes.message = new Persistent<Value>();
294
- Local<String> tmp = String::NewFromUtf8(isolate, "JavaScript was terminated (either by timeout or explicitly)");
485
+ Local<String> tmp = String::NewFromUtf8Literal(isolate, "JavaScript was terminated (either by timeout or explicitly)");
295
486
  evalRes.message->Reset(isolate, tmp);
296
487
  }
297
488
  if (!trycatch.StackTrace(context).IsEmpty()) {
@@ -303,12 +494,13 @@ static void prepare_result(MaybeLocal<Value> v8res,
303
494
  }
304
495
  }
305
496
 
306
- void*
497
+ static void*
307
498
  nogvl_context_eval(void* arg) {
308
499
 
309
500
  EvalParams* eval_params = (EvalParams*)arg;
310
501
  EvalResult* result = eval_params->result;
311
- Isolate* isolate = eval_params->context_info->isolate_info->isolate;
502
+ IsolateInfo* isolate_info = eval_params->context_info->isolate_info;
503
+ Isolate* isolate = isolate_info->isolate;
312
504
 
313
505
  Isolate::Scope isolate_scope(isolate);
314
506
  HandleScope handle_scope(isolate);
@@ -317,14 +509,15 @@ nogvl_context_eval(void* arg) {
317
509
  Context::Scope context_scope(context);
318
510
  v8::ScriptOrigin *origin = NULL;
319
511
 
320
- // in gvl flag
321
- isolate->SetData(IN_GVL, (void*)false);
322
- // terminate ASAP
323
- isolate->SetData(DO_TERMINATE, (void*)false);
324
- // Memory softlimit
325
- isolate->SetData(MEM_SOFTLIMIT_VALUE, (void*)false);
326
- // Memory softlimit hit flag
327
- isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)false);
512
+ IsolateData::Init(isolate);
513
+
514
+ if (eval_params->max_memory > 0) {
515
+ IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_MAX, eval_params->max_memory);
516
+ if (!isolate_info->added_gc_cb) {
517
+ isolate->AddGCEpilogueCallback(gc_callback);
518
+ isolate_info->added_gc_cb = true;
519
+ }
520
+ }
328
521
 
329
522
  MaybeLocal<Script> parsed_script;
330
523
 
@@ -350,9 +543,8 @@ nogvl_context_eval(void* arg) {
350
543
  result->message->Reset(isolate, trycatch.Exception());
351
544
  } else {
352
545
  // parsing successful
353
- if (eval_params->max_memory > 0) {
354
- isolate->SetData(MEM_SOFTLIMIT_VALUE, &eval_params->max_memory);
355
- isolate->AddGCEpilogueCallback(gc_callback);
546
+ if (eval_params->marshal_stackdepth > 0) {
547
+ StackCounter::SetMax(isolate, eval_params->marshal_stackdepth);
356
548
  }
357
549
 
358
550
  maybe_value = parsed_script.ToLocalChecked()->Run(context);
@@ -360,11 +552,17 @@ nogvl_context_eval(void* arg) {
360
552
 
361
553
  prepare_result(maybe_value, trycatch, isolate, context, *result);
362
554
 
363
- isolate->SetData(IN_GVL, (void*)true);
555
+ IsolateData::Set(isolate, IsolateData::IN_GVL, true);
364
556
 
365
557
  return NULL;
366
558
  }
367
559
 
560
+ static VALUE new_empty_failed_conv_obj() {
561
+ // TODO isolate code that translates execption to ruby
562
+ // exception so we can properly return it
563
+ return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
564
+ }
565
+
368
566
  // assumes isolate locking is in place
369
567
  static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
370
568
  Local<Value> value) {
@@ -372,6 +570,18 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
372
570
  Isolate::Scope isolate_scope(isolate);
373
571
  HandleScope scope(isolate);
374
572
 
573
+ StackCounter stackCounter(isolate);
574
+
575
+ if (IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED)) {
576
+ return Qnil;
577
+ }
578
+
579
+ if (stackCounter.IsTooDeep()) {
580
+ IsolateData::Set(isolate, IsolateData::DO_TERMINATE, true);
581
+ isolate->TerminateExecution();
582
+ return Qnil;
583
+ }
584
+
375
585
  if (value->IsNull() || value->IsUndefined()){
376
586
  return Qnil;
377
587
  }
@@ -396,8 +606,11 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
396
606
  VALUE rb_array = rb_ary_new();
397
607
  Local<Array> arr = Local<Array>::Cast(value);
398
608
  for(uint32_t i=0; i < arr->Length(); i++) {
399
- Local<Value> element = arr->Get(i);
400
- VALUE rb_elem = convert_v8_to_ruby(isolate, context, element);
609
+ MaybeLocal<Value> element = arr->Get(context, i);
610
+ if (element.IsEmpty()) {
611
+ continue;
612
+ }
613
+ VALUE rb_elem = convert_v8_to_ruby(isolate, context, element.ToLocalChecked());
401
614
  if (rb_funcall(rb_elem, rb_intern("class"), 0) == rb_cFailedV8Conversion) {
402
615
  return rb_elem;
403
616
  }
@@ -427,18 +640,20 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
427
640
  if (!maybe_props.IsEmpty()) {
428
641
  Local<Array> props = maybe_props.ToLocalChecked();
429
642
  for(uint32_t i=0; i < props->Length(); i++) {
430
- Local<Value> key = props->Get(i);
431
- VALUE rb_key = convert_v8_to_ruby(isolate, context, key);
432
- Local<Value> prop_value = object->Get(key);
433
- // this may have failed due to Get raising
643
+ MaybeLocal<Value> key = props->Get(context, i);
644
+ if (key.IsEmpty()) {
645
+ return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
646
+ }
647
+ VALUE rb_key = convert_v8_to_ruby(isolate, context, key.ToLocalChecked());
434
648
 
435
- if (trycatch.HasCaught()) {
436
- // TODO isolate code that translates execption to ruby
437
- // exception so we can properly return it
438
- return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
649
+ MaybeLocal<Value> prop_value = object->Get(context, key.ToLocalChecked());
650
+ // this may have failed due to Get raising
651
+ if (prop_value.IsEmpty() || trycatch.HasCaught()) {
652
+ return new_empty_failed_conv_obj();
439
653
  }
440
654
 
441
- VALUE rb_value = convert_v8_to_ruby(isolate, context, prop_value);
655
+ VALUE rb_value = convert_v8_to_ruby(
656
+ isolate, context, prop_value.ToLocalChecked());
442
657
  rb_hash_aset(rb_hash, rb_key, rb_value);
443
658
  }
444
659
  }
@@ -455,7 +670,7 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
455
670
  rb_enc_find("utf-8")
456
671
  );
457
672
 
458
- return ID2SYM(rb_intern_str(str_symbol));
673
+ return rb_str_intern(str_symbol);
459
674
  }
460
675
 
461
676
  MaybeLocal<String> rstr_maybe = value->ToString(context);
@@ -509,7 +724,7 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
509
724
  case T_FLOAT:
510
725
  return scope.Escape(Number::New(isolate, NUM2DBL(value)));
511
726
  case T_STRING:
512
- return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, (int)RSTRING_LEN(value)).ToLocalChecked());
727
+ return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, RSTRING_LENINT(value)).ToLocalChecked());
513
728
  case T_NIL:
514
729
  return scope.Escape(Null(isolate));
515
730
  case T_TRUE:
@@ -520,7 +735,8 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
520
735
  length = RARRAY_LEN(value);
521
736
  array = Array::New(isolate, (int)length);
522
737
  for(i=0; i<length; i++) {
523
- array->Set(i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
738
+ Maybe<bool> success = array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
739
+ (void)(success);
524
740
  }
525
741
  return scope.Escape(array);
526
742
  case T_HASH:
@@ -529,13 +745,14 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
529
745
  length = RARRAY_LEN(hash_as_array);
530
746
  for(i=0; i<length; i++) {
531
747
  pair = rb_ary_entry(hash_as_array, i);
532
- object->Set(convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
748
+ Maybe<bool> success = object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
533
749
  convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 1)));
750
+ (void)(success);
534
751
  }
535
752
  return scope.Escape(object);
536
753
  case T_SYMBOL:
537
754
  value = rb_funcall(value, rb_intern("to_s"), 0);
538
- return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, (int)RSTRING_LEN(value)).ToLocalChecked());
755
+ return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, RSTRING_LENINT(value)).ToLocalChecked());
539
756
  case T_DATA:
540
757
  klass = rb_funcall(value, rb_intern("class"), 0);
541
758
  if (klass == rb_cTime || klass == rb_cDateTime)
@@ -559,9 +776,9 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
559
776
  case T_UNDEF:
560
777
  case T_NODE:
561
778
  default:
562
- return scope.Escape(String::NewFromUtf8(isolate, "Undefined Conversion"));
779
+ return scope.Escape(String::NewFromUtf8Literal(isolate, "Undefined Conversion"));
780
+ }
563
781
  }
564
- }
565
782
 
566
783
  static void unblock_eval(void *ptr) {
567
784
  EvalParams* eval = (EvalParams*)ptr;
@@ -572,54 +789,45 @@ static void unblock_eval(void *ptr) {
572
789
  * The implementations of the run_extra_code(), create_snapshot_data_blob() and
573
790
  * warm_up_snapshot_data_blob() functions have been derived from V8's test suite.
574
791
  */
575
- bool run_extra_code(Isolate *isolate, Local<v8::Context> context,
792
+ static bool run_extra_code(Isolate *isolate, Local<v8::Context> context,
576
793
  const char *utf8_source, const char *name) {
577
794
  Context::Scope context_scope(context);
578
795
  TryCatch try_catch(isolate);
579
796
  Local<String> source_string;
580
- if (!String::NewFromUtf8(isolate, utf8_source,
581
- NewStringType::kNormal)
582
- .ToLocal(&source_string)) {
797
+ if (!String::NewFromUtf8(isolate, utf8_source).ToLocal(&source_string)) {
583
798
  return false;
584
799
  }
585
- Local<v8::String> resource_name =
586
- String::NewFromUtf8(isolate, name, NewStringType::kNormal)
587
- .ToLocalChecked();
800
+ Local<String> resource_name =
801
+ String::NewFromUtf8(isolate, name).ToLocalChecked();
588
802
  ScriptOrigin origin(resource_name);
589
803
  ScriptCompiler::Source source(source_string, origin);
590
804
  Local<Script> script;
591
805
  if (!ScriptCompiler::Compile(context, &source).ToLocal(&script))
592
806
  return false;
593
- if (script->Run(context).IsEmpty())
594
- return false;
595
- // CHECK(!try_catch.HasCaught());
807
+ if (script->Run(context).IsEmpty()) return false;
596
808
  return true;
597
809
  }
598
810
 
599
- StartupData
811
+ static StartupData
600
812
  create_snapshot_data_blob(const char *embedded_source = nullptr) {
601
- // Create a new isolate and a new context from scratch, optionally run
602
- // a script to embed, and serialize to create a snapshot blob.
603
- StartupData result = {nullptr, 0};
604
- {
605
- SnapshotCreator snapshot_creator;
606
- Isolate *isolate = snapshot_creator.GetIsolate();
813
+ Isolate *isolate = Isolate::Allocate();
814
+
815
+ // Optionally run a script to embed, and serialize to create a snapshot blob.
816
+ SnapshotCreator snapshot_creator(isolate);
607
817
  {
608
818
  HandleScope scope(isolate);
609
- Local<Context> context = Context::New(isolate);
819
+ Local<v8::Context> context = v8::Context::New(isolate);
610
820
  if (embedded_source != nullptr &&
611
- !run_extra_code(isolate, context, embedded_source,
612
- "<embedded>")) {
613
- return result;
821
+ !run_extra_code(isolate, context, embedded_source, "<embedded>")) {
822
+ return {};
614
823
  }
615
824
  snapshot_creator.SetDefaultContext(context);
616
825
  }
617
- result = snapshot_creator.CreateBlob(
826
+ return snapshot_creator.CreateBlob(
618
827
  SnapshotCreator::FunctionCodeHandling::kClear);
619
828
  }
620
- return result;
621
- }
622
829
 
830
+ static
623
831
  StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
624
832
  const char *warmup_source) {
625
833
  // Use following steps to create a warmed up snapshot blob from a cold one:
@@ -655,14 +863,14 @@ StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
655
863
 
656
864
  static VALUE rb_snapshot_size(VALUE self, VALUE str) {
657
865
  SnapshotInfo* snapshot_info;
658
- Data_Get_Struct(self, SnapshotInfo, snapshot_info);
866
+ TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
659
867
 
660
868
  return INT2NUM(snapshot_info->raw_size);
661
869
  }
662
870
 
663
871
  static VALUE rb_snapshot_load(VALUE self, VALUE str) {
664
872
  SnapshotInfo* snapshot_info;
665
- Data_Get_Struct(self, SnapshotInfo, snapshot_info);
873
+ TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
666
874
 
667
875
  if(TYPE(str) != T_STRING) {
668
876
  rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
@@ -685,14 +893,14 @@ static VALUE rb_snapshot_load(VALUE self, VALUE str) {
685
893
 
686
894
  static VALUE rb_snapshot_dump(VALUE self, VALUE str) {
687
895
  SnapshotInfo* snapshot_info;
688
- Data_Get_Struct(self, SnapshotInfo, snapshot_info);
896
+ TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
689
897
 
690
898
  return rb_str_new(snapshot_info->data, snapshot_info->raw_size);
691
899
  }
692
900
 
693
901
  static VALUE rb_snapshot_warmup_unsafe(VALUE self, VALUE str) {
694
902
  SnapshotInfo* snapshot_info;
695
- Data_Get_Struct(self, SnapshotInfo, snapshot_info);
903
+ TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
696
904
 
697
905
  if(TYPE(str) != T_STRING) {
698
906
  rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
@@ -739,13 +947,13 @@ void IsolateInfo::init(SnapshotInfo* snapshot_info) {
739
947
 
740
948
  static VALUE rb_isolate_init_with_snapshot(VALUE self, VALUE snapshot) {
741
949
  IsolateInfo* isolate_info;
742
- Data_Get_Struct(self, IsolateInfo, isolate_info);
950
+ TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
743
951
 
744
952
  init_v8();
745
953
 
746
954
  SnapshotInfo* snapshot_info = nullptr;
747
955
  if (!NIL_P(snapshot)) {
748
- Data_Get_Struct(snapshot, SnapshotInfo, snapshot_info);
956
+ TypedData_Get_Struct(snapshot, SnapshotInfo, &snapshot_type, snapshot_info);
749
957
  }
750
958
 
751
959
  isolate_info->init(snapshot_info);
@@ -756,7 +964,7 @@ static VALUE rb_isolate_init_with_snapshot(VALUE self, VALUE snapshot) {
756
964
 
757
965
  static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
758
966
  IsolateInfo* isolate_info;
759
- Data_Get_Struct(self, IsolateInfo, isolate_info);
967
+ TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
760
968
 
761
969
  if (current_platform == NULL) return Qfalse;
762
970
 
@@ -765,9 +973,32 @@ static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
765
973
  return isolate_info->isolate->IdleNotificationDeadline(now + duration) ? Qtrue : Qfalse;
766
974
  }
767
975
 
976
+ static VALUE rb_isolate_low_memory_notification(VALUE self) {
977
+ IsolateInfo* isolate_info;
978
+ TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
979
+
980
+ if (current_platform == NULL) return Qfalse;
981
+
982
+ isolate_info->isolate->LowMemoryNotification();
983
+ return Qnil;
984
+ }
985
+
986
+ static VALUE rb_isolate_pump_message_loop(VALUE self) {
987
+ IsolateInfo* isolate_info;
988
+ TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
989
+
990
+ if (current_platform == NULL) return Qfalse;
991
+
992
+ if (platform::PumpMessageLoop(current_platform.get(), isolate_info->isolate)){
993
+ return Qtrue;
994
+ } else {
995
+ return Qfalse;
996
+ }
997
+ }
998
+
768
999
  static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
769
1000
  ContextInfo* context_info;
770
- Data_Get_Struct(self, ContextInfo, context_info);
1001
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
771
1002
 
772
1003
  init_v8();
773
1004
 
@@ -778,11 +1009,11 @@ static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
778
1009
 
779
1010
  SnapshotInfo *snapshot_info = nullptr;
780
1011
  if (!NIL_P(snap) && rb_obj_is_kind_of(snap, rb_cSnapshot)) {
781
- Data_Get_Struct(snap, SnapshotInfo, snapshot_info);
1012
+ TypedData_Get_Struct(snap, SnapshotInfo, &snapshot_type, snapshot_info);
782
1013
  }
783
1014
  isolate_info->init(snapshot_info);
784
1015
  } else { // given isolate or snapshot
785
- Data_Get_Struct(isolate, IsolateInfo, isolate_info);
1016
+ TypedData_Get_Struct(isolate, IsolateInfo, &isolate_type, isolate_info);
786
1017
  }
787
1018
 
788
1019
  context_info->isolate_info = isolate_info;
@@ -812,7 +1043,7 @@ static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
812
1043
  static VALUE convert_result_to_ruby(VALUE self /* context */,
813
1044
  EvalResult& result) {
814
1045
  ContextInfo *context_info;
815
- Data_Get_Struct(self, ContextInfo, context_info);
1046
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
816
1047
 
817
1048
  Isolate *isolate = context_info->isolate_info->isolate;
818
1049
  Persistent<Context> *p_ctx = context_info->context;
@@ -839,7 +1070,7 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
839
1070
  // a v8 scope, if we do the scope is never cleaned up properly and we leak
840
1071
  if (!result.parsed) {
841
1072
  if(TYPE(message) == T_STRING) {
842
- rb_raise(rb_eParseError, "%s", RSTRING_PTR(message));
1073
+ rb_raise(rb_eParseError, "%" PRIsVALUE, message);
843
1074
  } else {
844
1075
  rb_raise(rb_eParseError, "Unknown JavaScript Error during parse");
845
1076
  }
@@ -848,9 +1079,14 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
848
1079
  if (!result.executed) {
849
1080
  VALUE ruby_exception = rb_iv_get(self, "@current_exception");
850
1081
  if (ruby_exception == Qnil) {
851
- bool mem_softlimit_reached = (bool)isolate->GetData(MEM_SOFTLIMIT_REACHED);
1082
+ bool mem_softlimit_reached = IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_REACHED);
1083
+ bool marshal_stack_maxdepth_reached = IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED);
852
1084
  // If we were terminated or have the memory softlimit flag set
853
- if (result.terminated || mem_softlimit_reached) {
1085
+ if (marshal_stack_maxdepth_reached) {
1086
+ ruby_exception = rb_eScriptRuntimeError;
1087
+ std::string msg = std::string("Marshal object depth too deep. Script terminated.");
1088
+ message = rb_enc_str_new(msg.c_str(), msg.length(), rb_enc_find("utf-8"));
1089
+ } else if (result.terminated || mem_softlimit_reached) {
854
1090
  ruby_exception = mem_softlimit_reached ? rb_eV8OutOfMemoryError : rb_eScriptTerminatedError;
855
1091
  } else {
856
1092
  ruby_exception = rb_eScriptRuntimeError;
@@ -858,15 +1094,17 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
858
1094
 
859
1095
  // exception report about what happened
860
1096
  if (TYPE(backtrace) == T_STRING) {
861
- rb_raise(ruby_exception, "%s", RSTRING_PTR(backtrace));
1097
+ rb_raise(ruby_exception, "%" PRIsVALUE, backtrace);
862
1098
  } else if(TYPE(message) == T_STRING) {
863
- rb_raise(ruby_exception, "%s", RSTRING_PTR(message));
1099
+ rb_raise(ruby_exception, "%" PRIsVALUE, message);
864
1100
  } else {
865
1101
  rb_raise(ruby_exception, "Unknown JavaScript Error during execution");
866
1102
  }
1103
+ } else if (rb_obj_is_kind_of(ruby_exception, rb_eException)) {
1104
+ rb_exc_raise(ruby_exception);
867
1105
  } else {
868
1106
  VALUE rb_str = rb_funcall(ruby_exception, rb_intern("to_s"), 0);
869
- rb_raise(CLASS_OF(ruby_exception), "%s", RSTRING_PTR(rb_str));
1107
+ rb_raise(CLASS_OF(ruby_exception), "%" PRIsVALUE, rb_str);
870
1108
  }
871
1109
  }
872
1110
 
@@ -885,6 +1123,7 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
885
1123
  VALUE json_string = rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
886
1124
  ret = rb_funcall(rb_mJSON, rb_intern("parse"), 1, json_string);
887
1125
  } else {
1126
+ StackCounter::Reset(isolate);
888
1127
  ret = convert_v8_to_ruby(isolate, *p_ctx, tmp);
889
1128
  }
890
1129
 
@@ -907,7 +1146,7 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
907
1146
  EvalResult eval_result;
908
1147
  ContextInfo* context_info;
909
1148
 
910
- Data_Get_Struct(self, ContextInfo, context_info);
1149
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
911
1150
  Isolate* isolate = context_info->isolate_info->isolate;
912
1151
 
913
1152
  if(TYPE(str) != T_STRING) {
@@ -925,13 +1164,13 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
925
1164
  HandleScope handle_scope(isolate);
926
1165
 
927
1166
  Local<String> eval = String::NewFromUtf8(isolate, RSTRING_PTR(str),
928
- NewStringType::kNormal, (int)RSTRING_LEN(str)).ToLocalChecked();
1167
+ NewStringType::kNormal, RSTRING_LENINT(str)).ToLocalChecked();
929
1168
 
930
1169
  Local<String> local_filename;
931
1170
 
932
1171
  if (filename != Qnil) {
933
1172
  local_filename = String::NewFromUtf8(isolate, RSTRING_PTR(filename),
934
- NewStringType::kNormal, (int)RSTRING_LEN(filename)).ToLocalChecked();
1173
+ NewStringType::kNormal, RSTRING_LENINT(filename)).ToLocalChecked();
935
1174
  eval_params.filename = &local_filename;
936
1175
  } else {
937
1176
  eval_params.filename = NULL;
@@ -942,6 +1181,7 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
942
1181
  eval_params.result = &eval_result;
943
1182
  eval_params.timeout = 0;
944
1183
  eval_params.max_memory = 0;
1184
+ eval_params.marshal_stackdepth = 0;
945
1185
  VALUE timeout = rb_iv_get(self, "@timeout");
946
1186
  if (timeout != Qnil) {
947
1187
  eval_params.timeout = (useconds_t)NUM2LONG(timeout);
@@ -952,6 +1192,11 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
952
1192
  eval_params.max_memory = (size_t)NUM2ULONG(mem_softlimit);
953
1193
  }
954
1194
 
1195
+ VALUE stack_depth = rb_iv_get(self, "@marshal_stack_depth");
1196
+ if (stack_depth != Qnil) {
1197
+ eval_params.marshal_stackdepth = (size_t)NUM2ULONG(stack_depth);
1198
+ }
1199
+
955
1200
  eval_result.message = NULL;
956
1201
  eval_result.backtrace = NULL;
957
1202
 
@@ -989,7 +1234,7 @@ VALUE rescue_callback(VALUE rdata, VALUE exception) {
989
1234
  return exception;
990
1235
  }
991
1236
 
992
- void*
1237
+ static void*
993
1238
  gvl_ruby_callback(void* data) {
994
1239
 
995
1240
  FunctionCallbackInfo<Value>* args = (FunctionCallbackInfo<Value>*)data;
@@ -1005,7 +1250,7 @@ gvl_ruby_callback(void* data) {
1005
1250
  HandleScope scope(args->GetIsolate());
1006
1251
  Local<External> external = Local<External>::Cast(args->Data());
1007
1252
 
1008
- self = *(VALUE*)(external->Value());
1253
+ self = (VALUE)(external->Value());
1009
1254
  callback = rb_iv_get(self, "@callback");
1010
1255
 
1011
1256
  parent = rb_iv_get(self, "@parent");
@@ -1013,7 +1258,7 @@ gvl_ruby_callback(void* data) {
1013
1258
  return NULL;
1014
1259
  }
1015
1260
 
1016
- Data_Get_Struct(parent, ContextInfo, context_info);
1261
+ TypedData_Get_Struct(parent, ContextInfo, &context_type, context_info);
1017
1262
 
1018
1263
  if (length > 0) {
1019
1264
  ruby_args = rb_ary_tmp_new(length);
@@ -1021,6 +1266,7 @@ gvl_ruby_callback(void* data) {
1021
1266
 
1022
1267
  for (int i = 0; i < length; i++) {
1023
1268
  Local<Value> value = ((*args)[i]).As<Value>();
1269
+ StackCounter::Reset(args->GetIsolate());
1024
1270
  VALUE tmp = convert_v8_to_ruby(args->GetIsolate(),
1025
1271
  *context_info->context, value);
1026
1272
  rb_ary_push(ruby_args, tmp);
@@ -1034,22 +1280,26 @@ gvl_ruby_callback(void* data) {
1034
1280
  callback_data.ruby_args = ruby_args;
1035
1281
  callback_data.failed = false;
1036
1282
 
1037
- if ((bool)args->GetIsolate()->GetData(DO_TERMINATE) == true) {
1038
- args->GetIsolate()->ThrowException(String::NewFromUtf8(args->GetIsolate(), "Terminated execution during transition from Ruby to JS"));
1283
+ if (IsolateData::Get(args->GetIsolate(), IsolateData::DO_TERMINATE)) {
1284
+ args->GetIsolate()->ThrowException(
1285
+ String::NewFromUtf8Literal(args->GetIsolate(),
1286
+ "Terminated execution during transition from Ruby to JS"));
1039
1287
  args->GetIsolate()->TerminateExecution();
1040
1288
  if (length > 0) {
1041
1289
  rb_ary_clear(ruby_args);
1042
- rb_gc_force_recycle(ruby_args);
1043
1290
  }
1044
1291
  return NULL;
1045
1292
  }
1046
1293
 
1047
- result = rb_rescue2((VALUE(*)(...))&protected_callback, (VALUE)(&callback_data),
1048
- (VALUE(*)(...))&rescue_callback, (VALUE)(&callback_data), rb_eException, (VALUE)0);
1294
+ VALUE callback_data_value = (VALUE)&callback_data;
1295
+
1296
+ // TODO: use rb_vrescue2 in Ruby 2.7 and above
1297
+ result = rb_rescue2(MR_METHOD_FUNC(protected_callback), callback_data_value,
1298
+ MR_METHOD_FUNC(rescue_callback), callback_data_value, rb_eException, (VALUE)0);
1049
1299
 
1050
1300
  if(callback_data.failed) {
1051
1301
  rb_iv_set(parent, "@current_exception", result);
1052
- args->GetIsolate()->ThrowException(String::NewFromUtf8(args->GetIsolate(), "Ruby exception"));
1302
+ args->GetIsolate()->ThrowException(String::NewFromUtf8Literal(args->GetIsolate(), "Ruby exception"));
1053
1303
  }
1054
1304
  else {
1055
1305
  HandleScope scope(args->GetIsolate());
@@ -1059,26 +1309,24 @@ gvl_ruby_callback(void* data) {
1059
1309
 
1060
1310
  if (length > 0) {
1061
1311
  rb_ary_clear(ruby_args);
1062
- rb_gc_force_recycle(ruby_args);
1063
1312
  }
1064
1313
 
1065
- if ((bool)args->GetIsolate()->GetData(DO_TERMINATE) == true) {
1066
- Isolate* isolate = args->GetIsolate();
1067
- isolate->TerminateExecution();
1314
+ if (IsolateData::Get(args->GetIsolate(), IsolateData::DO_TERMINATE)) {
1315
+ args->GetIsolate()->TerminateExecution();
1068
1316
  }
1069
1317
 
1070
1318
  return NULL;
1071
1319
  }
1072
1320
 
1073
1321
  static void ruby_callback(const FunctionCallbackInfo<Value>& args) {
1074
- bool has_gvl = (bool)args.GetIsolate()->GetData(IN_GVL);
1322
+ bool has_gvl = IsolateData::Get(args.GetIsolate(), IsolateData::IN_GVL);
1075
1323
 
1076
1324
  if(has_gvl) {
1077
1325
  gvl_ruby_callback((void*)&args);
1078
1326
  } else {
1079
- args.GetIsolate()->SetData(IN_GVL, (void*)true);
1327
+ IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, true);
1080
1328
  rb_thread_call_with_gvl(gvl_ruby_callback, (void*)(&args));
1081
- args.GetIsolate()->SetData(IN_GVL, (void*)false);
1329
+ IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, false);
1082
1330
  }
1083
1331
  }
1084
1332
 
@@ -1095,7 +1343,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1095
1343
  bool parse_error = false;
1096
1344
  bool attach_error = false;
1097
1345
 
1098
- Data_Get_Struct(parent, ContextInfo, context_info);
1346
+ TypedData_Get_Struct(parent, ContextInfo, &context_type, context_info);
1099
1347
  Isolate* isolate = context_info->isolate_info->isolate;
1100
1348
 
1101
1349
  {
@@ -1108,32 +1356,32 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1108
1356
 
1109
1357
  Local<String> v8_str =
1110
1358
  String::NewFromUtf8(isolate, RSTRING_PTR(name),
1111
- NewStringType::kNormal, (int)RSTRING_LEN(name))
1359
+ NewStringType::kNormal, RSTRING_LENINT(name))
1112
1360
  .ToLocalChecked();
1113
1361
 
1114
- // copy self so we can access from v8 external
1115
- VALUE* self_copy;
1116
- Data_Get_Struct(self, VALUE, self_copy);
1117
- *self_copy = self;
1118
-
1119
- Local<Value> external = External::New(isolate, self_copy);
1362
+ // Note that self (rb_cExternalFunction) is a pure Ruby T_OBJECT,
1363
+ // not a T_DATA type like most other classes in this file
1364
+ Local<Value> external = External::New(isolate, (void *)self);
1120
1365
 
1121
1366
  if (parent_object == Qnil) {
1122
- context->Global()->Set(
1123
- v8_str, FunctionTemplate::New(isolate, ruby_callback, external)
1367
+ Maybe<bool> success = context->Global()->Set(
1368
+ context,
1369
+ v8_str,
1370
+ FunctionTemplate::New(isolate, ruby_callback, external)
1124
1371
  ->GetFunction(context)
1125
1372
  .ToLocalChecked());
1373
+ (void)success;
1126
1374
 
1127
1375
  } else {
1128
1376
  Local<String> eval =
1129
1377
  String::NewFromUtf8(isolate, RSTRING_PTR(parent_object_eval),
1130
1378
  NewStringType::kNormal,
1131
- (int)RSTRING_LEN(parent_object_eval))
1379
+ RSTRING_LENINT(parent_object_eval))
1132
1380
  .ToLocalChecked();
1133
1381
 
1134
1382
  MaybeLocal<Script> parsed_script = Script::Compile(context, eval);
1135
1383
  if (parsed_script.IsEmpty()) {
1136
- parse_error = true;
1384
+ parse_error = true;
1137
1385
  } else {
1138
1386
  MaybeLocal<Value> maybe_value =
1139
1387
  parsed_script.ToLocalChecked()->Run(context);
@@ -1142,12 +1390,14 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1142
1390
  if (!maybe_value.IsEmpty()) {
1143
1391
  Local<Value> value = maybe_value.ToLocalChecked();
1144
1392
  if (value->IsObject()) {
1145
- value.As<Object>()->Set(
1146
- v8_str, FunctionTemplate::New(
1147
- isolate, ruby_callback, external)
1393
+ Maybe<bool> success = value.As<Object>()->Set(
1394
+ context,
1395
+ v8_str,
1396
+ FunctionTemplate::New(isolate, ruby_callback, external)
1148
1397
  ->GetFunction(context)
1149
1398
  .ToLocalChecked());
1150
- attach_error = false;
1399
+ (void)success;
1400
+ attach_error = false;
1151
1401
  }
1152
1402
  }
1153
1403
  }
@@ -1168,7 +1418,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1168
1418
 
1169
1419
  static VALUE rb_context_isolate_mutex(VALUE self) {
1170
1420
  ContextInfo* context_info;
1171
- Data_Get_Struct(self, ContextInfo, context_info);
1421
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1172
1422
 
1173
1423
  if (!context_info->isolate_info) {
1174
1424
  rb_raise(rb_eScriptRuntimeError, "Context has no Isolate available anymore");
@@ -1177,35 +1427,39 @@ static VALUE rb_context_isolate_mutex(VALUE self) {
1177
1427
  return context_info->isolate_info->mutex;
1178
1428
  }
1179
1429
 
1180
- void free_isolate(IsolateInfo* isolate_info) {
1181
-
1182
- if (isolate_info->isolate) {
1183
- Locker lock(isolate_info->isolate);
1184
- }
1185
-
1186
- if (isolate_info->isolate) {
1187
- if (isolate_info->interrupted) {
1188
- 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");
1430
+ IsolateInfo::~IsolateInfo() {
1431
+ if (isolate) {
1432
+ if (this->interrupted) {
1433
+ fprintf(stderr, "WARNING: V8 isolate was interrupted by Ruby, "
1434
+ "it can not be disposed and memory will not be "
1435
+ "reclaimed till the Ruby process exits.\n");
1189
1436
  } else {
1190
-
1191
- if (isolate_info->pid != getpid()) {
1192
- fprintf(stderr, "WARNING: V8 isolate was forked, it can not be disposed and memory will not be reclaimed till the Ruby process exits.\n");
1437
+ if (this->pid != getpid() && !single_threaded) {
1438
+ fprintf(stderr, "WARNING: V8 isolate was forked, "
1439
+ "it can not be disposed and "
1440
+ "memory will not be reclaimed "
1441
+ "till the Ruby process exits.\n"
1442
+ "It is VERY likely your process will hang.\n"
1443
+ "If you wish to use v8 in forked environment "
1444
+ "please ensure the platform is initialized with:\n"
1445
+ "MiniRacer::Platform.set_flags! :single_threaded\n"
1446
+ );
1193
1447
  } else {
1194
- isolate_info->isolate->Dispose();
1448
+ isolate->Dispose();
1195
1449
  }
1196
1450
  }
1197
- isolate_info->isolate = NULL;
1451
+ isolate = nullptr;
1198
1452
  }
1199
1453
 
1200
- if (isolate_info->startup_data) {
1201
- delete[] isolate_info->startup_data->data;
1202
- delete isolate_info->startup_data;
1454
+ if (startup_data) {
1455
+ delete[] startup_data->data;
1456
+ delete startup_data;
1203
1457
  }
1204
1458
 
1205
- delete isolate_info->allocator;
1459
+ delete allocator;
1206
1460
  }
1207
1461
 
1208
- static void *free_context_raw(void* arg) {
1462
+ static void free_context_raw(void *arg) {
1209
1463
  ContextInfo* context_info = (ContextInfo*)arg;
1210
1464
  IsolateInfo* isolate_info = context_info->isolate_info;
1211
1465
  Persistent<Context>* context = context_info->context;
@@ -1222,6 +1476,20 @@ static void *free_context_raw(void* arg) {
1222
1476
  }
1223
1477
 
1224
1478
  xfree(context_info);
1479
+ }
1480
+
1481
+ static void *free_context_thr(void* arg) {
1482
+ if (pthread_rwlock_tryrdlock(&exit_lock) != 0) {
1483
+ return NULL;
1484
+ }
1485
+ if (ruby_exiting) {
1486
+ return NULL;
1487
+ }
1488
+
1489
+ free_context_raw(arg);
1490
+
1491
+ pthread_rwlock_unlock(&exit_lock);
1492
+
1225
1493
  return NULL;
1226
1494
  }
1227
1495
 
@@ -1235,22 +1503,17 @@ static void free_context(ContextInfo* context_info) {
1235
1503
  context_info_copy->context = context_info->context;
1236
1504
 
1237
1505
  if (isolate_info && isolate_info->refs() > 1) {
1238
- pthread_t free_context_thread;
1239
- if (pthread_create(&free_context_thread, NULL, free_context_raw, (void*)context_info_copy)) {
1240
- fprintf(stderr, "WARNING failed to release memory in MiniRacer, thread to release could not be created, process will leak memory\n");
1241
- }
1242
-
1506
+ pthread_t free_context_thread;
1507
+ if (pthread_create(&free_context_thread, thread_attr_p,
1508
+ free_context_thr, (void*)context_info_copy)) {
1509
+ fprintf(stderr, "WARNING failed to release memory in MiniRacer, thread to release could not be created, process will leak memory\n");
1510
+ }
1243
1511
  } else {
1244
1512
  free_context_raw(context_info_copy);
1245
1513
  }
1246
1514
 
1247
- if (context_info->context && isolate_info && isolate_info->isolate) {
1248
- context_info->context = NULL;
1249
- }
1250
-
1251
- if (isolate_info) {
1252
- context_info->isolate_info = NULL;
1253
- }
1515
+ context_info->context = NULL;
1516
+ context_info->isolate_info = NULL;
1254
1517
  }
1255
1518
 
1256
1519
  static void deallocate_isolate(void* data) {
@@ -1265,7 +1528,12 @@ static void mark_isolate(void* data) {
1265
1528
  isolate_info->mark();
1266
1529
  }
1267
1530
 
1268
- void deallocate(void* data) {
1531
+ static size_t isolate_memsize(const void *ptr) {
1532
+ const IsolateInfo *isolate_info = (const IsolateInfo *)ptr;
1533
+ return sizeof(*isolate_info);
1534
+ }
1535
+
1536
+ static void deallocate(void* data) {
1269
1537
  ContextInfo* context_info = (ContextInfo*)data;
1270
1538
 
1271
1539
  free_context(context_info);
@@ -1273,6 +1541,11 @@ void deallocate(void* data) {
1273
1541
  xfree(data);
1274
1542
  }
1275
1543
 
1544
+ static size_t context_memsize(const void *ptr)
1545
+ {
1546
+ return sizeof(ContextInfo);
1547
+ }
1548
+
1276
1549
  static void mark_context(void* data) {
1277
1550
  ContextInfo* context_info = (ContextInfo*)data;
1278
1551
  if (context_info->isolate_info) {
@@ -1280,48 +1553,39 @@ static void mark_context(void* data) {
1280
1553
  }
1281
1554
  }
1282
1555
 
1283
- void deallocate_external_function(void * data) {
1284
- xfree(data);
1285
- }
1286
-
1287
- void deallocate_snapshot(void * data) {
1556
+ static void deallocate_snapshot(void * data) {
1288
1557
  SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
1289
1558
  delete[] snapshot_info->data;
1290
1559
  xfree(snapshot_info);
1291
1560
  }
1292
1561
 
1293
- VALUE allocate_external_function(VALUE klass) {
1294
- VALUE* self = ALLOC(VALUE);
1295
- return Data_Wrap_Struct(klass, NULL, deallocate_external_function, (void*)self);
1562
+ static size_t snapshot_memsize(const void *data) {
1563
+ SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
1564
+ return sizeof(*snapshot_info) + snapshot_info->raw_size;
1296
1565
  }
1297
1566
 
1298
- VALUE allocate(VALUE klass) {
1299
- ContextInfo* context_info = ALLOC(ContextInfo);
1300
- context_info->isolate_info = NULL;
1301
- context_info->context = NULL;
1302
-
1303
- return Data_Wrap_Struct(klass, mark_context, deallocate, (void*)context_info);
1567
+ static VALUE allocate(VALUE klass) {
1568
+ ContextInfo* context_info;
1569
+ return TypedData_Make_Struct(klass, ContextInfo, &context_type, context_info);
1304
1570
  }
1305
1571
 
1306
- VALUE allocate_snapshot(VALUE klass) {
1307
- SnapshotInfo* snapshot_info = ALLOC(SnapshotInfo);
1308
- snapshot_info->data = NULL;
1309
- snapshot_info->raw_size = 0;
1572
+ static VALUE allocate_snapshot(VALUE klass) {
1573
+ SnapshotInfo* snapshot_info;
1310
1574
 
1311
- return Data_Wrap_Struct(klass, NULL, deallocate_snapshot, (void*)snapshot_info);
1575
+ return TypedData_Make_Struct(klass, SnapshotInfo, &snapshot_type, snapshot_info);
1312
1576
  }
1313
1577
 
1314
- VALUE allocate_isolate(VALUE klass) {
1578
+ static VALUE allocate_isolate(VALUE klass) {
1315
1579
  IsolateInfo* isolate_info = new IsolateInfo();
1316
1580
 
1317
- return Data_Wrap_Struct(klass, mark_isolate, deallocate_isolate, (void*)isolate_info);
1581
+ return TypedData_Wrap_Struct(klass, &isolate_type, isolate_info);
1318
1582
  }
1319
1583
 
1320
1584
  static VALUE
1321
1585
  rb_heap_stats(VALUE self) {
1322
1586
 
1323
1587
  ContextInfo* context_info;
1324
- Data_Get_Struct(self, ContextInfo, context_info);
1588
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1325
1589
  Isolate* isolate;
1326
1590
  v8::HeapStatistics stats;
1327
1591
 
@@ -1353,7 +1617,9 @@ rb_heap_stats(VALUE self) {
1353
1617
  // https://github.com/bnoordhuis/node-heapdump/blob/master/src/heapdump.cc
1354
1618
  class FileOutputStream : public OutputStream {
1355
1619
  public:
1356
- FileOutputStream(FILE* stream) : stream_(stream) {}
1620
+ int err;
1621
+
1622
+ FileOutputStream(int fd) : fd(fd) { err = 0; }
1357
1623
 
1358
1624
  virtual int GetChunkSize() {
1359
1625
  return 65536;
@@ -1362,17 +1628,27 @@ class FileOutputStream : public OutputStream {
1362
1628
  virtual void EndOfStream() {}
1363
1629
 
1364
1630
  virtual WriteResult WriteAsciiChunk(char* data, int size) {
1365
- const size_t len = static_cast<size_t>(size);
1366
- size_t off = 0;
1367
-
1368
- while (off < len && !feof(stream_) && !ferror(stream_))
1369
- off += fwrite(data + off, 1, len - off, stream_);
1370
-
1371
- return off == len ? kContinue : kAbort;
1631
+ size_t len = static_cast<size_t>(size);
1632
+
1633
+ while (len) {
1634
+ ssize_t w = write(fd, data, len);
1635
+
1636
+ if (w > 0) {
1637
+ data += w;
1638
+ len -= w;
1639
+ } else if (w < 0) {
1640
+ err = errno;
1641
+ return kAbort;
1642
+ } else { /* w == 0, could be out-of-space */
1643
+ err = -1;
1644
+ return kAbort;
1645
+ }
1646
+ }
1647
+ return kContinue;
1372
1648
  }
1373
1649
 
1374
1650
  private:
1375
- FILE* stream_;
1651
+ int fd;
1376
1652
  };
1377
1653
 
1378
1654
 
@@ -1385,13 +1661,11 @@ rb_heap_snapshot(VALUE self, VALUE file) {
1385
1661
 
1386
1662
  if (!fptr) return Qfalse;
1387
1663
 
1388
- FILE* fp;
1389
- fp = fdopen(fptr->fd, "w");
1390
- if (fp == NULL) return Qfalse;
1391
-
1664
+ // prepare for unbuffered write(2) below:
1665
+ rb_funcall(file, rb_intern("flush"), 0);
1392
1666
 
1393
1667
  ContextInfo* context_info;
1394
- Data_Get_Struct(self, ContextInfo, context_info);
1668
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1395
1669
  Isolate* isolate;
1396
1670
  isolate = context_info->isolate_info ? context_info->isolate_info->isolate : NULL;
1397
1671
 
@@ -1405,11 +1679,14 @@ rb_heap_snapshot(VALUE self, VALUE file) {
1405
1679
 
1406
1680
  const HeapSnapshot* const snap = heap_profiler->TakeHeapSnapshot();
1407
1681
 
1408
- FileOutputStream stream(fp);
1682
+ FileOutputStream stream(fptr->fd);
1409
1683
  snap->Serialize(&stream, HeapSnapshot::kJSON);
1410
1684
 
1411
1685
  const_cast<HeapSnapshot*>(snap)->Delete();
1412
1686
 
1687
+ /* TODO: perhaps rb_sys_fail here */
1688
+ if (stream.err) return Qfalse;
1689
+
1413
1690
  return Qtrue;
1414
1691
  }
1415
1692
 
@@ -1417,12 +1694,12 @@ static VALUE
1417
1694
  rb_context_stop(VALUE self) {
1418
1695
 
1419
1696
  ContextInfo* context_info;
1420
- Data_Get_Struct(self, ContextInfo, context_info);
1697
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1421
1698
 
1422
1699
  Isolate* isolate = context_info->isolate_info->isolate;
1423
1700
 
1424
1701
  // flag for termination
1425
- isolate->SetData(DO_TERMINATE, (void*)true);
1702
+ IsolateData::Set(isolate, IsolateData::DO_TERMINATE, true);
1426
1703
 
1427
1704
  isolate->TerminateExecution();
1428
1705
  rb_funcall(self, rb_intern("stop_attached"), 0);
@@ -1434,7 +1711,7 @@ static VALUE
1434
1711
  rb_context_dispose(VALUE self) {
1435
1712
 
1436
1713
  ContextInfo* context_info;
1437
- Data_Get_Struct(self, ContextInfo, context_info);
1714
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1438
1715
 
1439
1716
  free_context(context_info);
1440
1717
 
@@ -1448,17 +1725,23 @@ nogvl_context_call(void *args) {
1448
1725
  if (!call) {
1449
1726
  return NULL;
1450
1727
  }
1451
- Isolate* isolate = call->context_info->isolate_info->isolate;
1728
+ IsolateInfo *isolate_info = call->context_info->isolate_info;
1729
+ Isolate* isolate = isolate_info->isolate;
1452
1730
 
1453
- // in gvl flag
1454
- isolate->SetData(IN_GVL, (void*)false);
1455
- // terminate ASAP
1456
- isolate->SetData(DO_TERMINATE, (void*)false);
1731
+ IsolateData::Set(isolate, IsolateData::IN_GVL, false);
1732
+ IsolateData::Set(isolate, IsolateData::DO_TERMINATE, false);
1457
1733
 
1458
1734
  if (call->max_memory > 0) {
1459
- isolate->SetData(MEM_SOFTLIMIT_VALUE, &call->max_memory);
1460
- isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)false);
1461
- isolate->AddGCEpilogueCallback(gc_callback);
1735
+ IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_MAX, call->max_memory);
1736
+ IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_REACHED, false);
1737
+ if (!isolate_info->added_gc_cb) {
1738
+ isolate->AddGCEpilogueCallback(gc_callback);
1739
+ isolate_info->added_gc_cb = true;
1740
+ }
1741
+ }
1742
+
1743
+ if (call->marshal_stackdepth > 0) {
1744
+ StackCounter::SetMax(isolate, call->marshal_stackdepth);
1462
1745
  }
1463
1746
 
1464
1747
  Isolate::Scope isolate_scope(isolate);
@@ -1476,7 +1759,7 @@ nogvl_context_call(void *args) {
1476
1759
  MaybeLocal<v8::Value> res = fun->Call(context, context->Global(), call->argc, call->argv);
1477
1760
  prepare_result(res, trycatch, isolate, context, eval_res);
1478
1761
 
1479
- isolate->SetData(IN_GVL, (void*)true);
1762
+ IsolateData::Set(isolate, IsolateData::IN_GVL, true);
1480
1763
 
1481
1764
  return NULL;
1482
1765
  }
@@ -1491,7 +1774,7 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1491
1774
  FunctionCall call;
1492
1775
  VALUE *call_argv = NULL;
1493
1776
 
1494
- Data_Get_Struct(self, ContextInfo, context_info);
1777
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1495
1778
  Isolate* isolate = context_info->isolate_info->isolate;
1496
1779
 
1497
1780
  if (argc < 1) {
@@ -1524,6 +1807,13 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1524
1807
  unsigned long sl_int = NUM2ULONG(mem_softlimit);
1525
1808
  call.max_memory = (size_t)sl_int;
1526
1809
  }
1810
+
1811
+ call.marshal_stackdepth = 0;
1812
+ VALUE marshal_stackdepth = rb_iv_get(self, "@marshal_stack_depth");
1813
+ if (marshal_stackdepth != Qnil) {
1814
+ unsigned long sl_int = NUM2ULONG(marshal_stackdepth);
1815
+ call.marshal_stackdepth = (size_t)sl_int;
1816
+ }
1527
1817
 
1528
1818
  bool missingFunction = false;
1529
1819
  {
@@ -1536,29 +1826,24 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1536
1826
 
1537
1827
  // examples of such usage can be found in
1538
1828
  // https://github.com/v8/v8/blob/36b32aa28db5e993312f4588d60aad5c8330c8a5/test/cctest/test-api.cc#L15711
1539
- Local<String> fname = String::NewFromUtf8(isolate, call.function_name);
1540
- MaybeLocal<v8::Value> val = context->Global()->Get(fname);
1829
+ MaybeLocal<String> fname = String::NewFromUtf8(isolate, call.function_name);
1830
+ MaybeLocal<v8::Value> val;
1831
+ if (!fname.IsEmpty()) {
1832
+ val = context->Global()->Get(context, fname.ToLocalChecked());
1833
+ }
1541
1834
 
1542
1835
  if (val.IsEmpty() || !val.ToLocalChecked()->IsFunction()) {
1543
1836
  missingFunction = true;
1544
1837
  } else {
1545
-
1546
1838
  Local<v8::Function> fun = Local<v8::Function>::Cast(val.ToLocalChecked());
1839
+ VALUE tmp;
1547
1840
  call.fun = fun;
1548
- int fun_argc = call.argc;
1549
-
1550
- if (fun_argc > 0) {
1551
- call.argv = (v8::Local<Value> *) malloc(sizeof(void *) * fun_argc);
1552
- if (!call.argv) {
1553
- return Qnil;
1554
- }
1555
- for(int i=0; i < fun_argc; i++) {
1556
- call.argv[i] = convert_ruby_to_v8(isolate, context, call_argv[i]);
1557
- }
1841
+ call.argv = (v8::Local<Value> *)RB_ALLOCV_N(void *, tmp, call.argc);
1842
+ for(int i=0; i < call.argc; i++) {
1843
+ call.argv[i] = convert_ruby_to_v8(isolate, context, call_argv[i]);
1558
1844
  }
1559
1845
  rb_thread_call_without_gvl(nogvl_context_call, &call, unblock_function, &call);
1560
- free(call.argv);
1561
-
1846
+ RB_ALLOCV_END(tmp);
1562
1847
  }
1563
1848
  }
1564
1849
 
@@ -1571,7 +1856,7 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1571
1856
 
1572
1857
  static VALUE rb_context_create_isolate_value(VALUE self) {
1573
1858
  ContextInfo* context_info;
1574
- Data_Get_Struct(self, ContextInfo, context_info);
1859
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1575
1860
  IsolateInfo *isolate_info = context_info->isolate_info;
1576
1861
 
1577
1862
  if (!isolate_info) {
@@ -1579,12 +1864,23 @@ static VALUE rb_context_create_isolate_value(VALUE self) {
1579
1864
  }
1580
1865
 
1581
1866
  isolate_info->hold();
1582
- return Data_Wrap_Struct(rb_cIsolate, NULL, &deallocate_isolate, isolate_info);
1867
+ return TypedData_Wrap_Struct(rb_cIsolate, &isolate_type, isolate_info);
1868
+ }
1869
+
1870
+ static void set_ruby_exiting(VALUE value) {
1871
+ (void)value;
1872
+
1873
+ int res = pthread_rwlock_wrlock(&exit_lock);
1874
+
1875
+ ruby_exiting = true;
1876
+ if (res == 0) {
1877
+ pthread_rwlock_unlock(&exit_lock);
1878
+ }
1583
1879
  }
1584
1880
 
1585
1881
  extern "C" {
1586
1882
 
1587
- void Init_mini_racer_extension ( void )
1883
+ __attribute__((visibility("default"))) void Init_mini_racer_extension ( void )
1588
1884
  {
1589
1885
  VALUE rb_mMiniRacer = rb_define_module("MiniRacer");
1590
1886
  rb_cContext = rb_define_class_under(rb_mMiniRacer, "Context", rb_cObject);
@@ -1624,7 +1920,6 @@ extern "C" {
1624
1920
  rb_define_alloc_func(rb_cIsolate, allocate_isolate);
1625
1921
 
1626
1922
  rb_define_private_method(rb_cExternalFunction, "notify_v8", (VALUE(*)(...))&rb_external_function_notify_v8, 0);
1627
- rb_define_alloc_func(rb_cExternalFunction, allocate_external_function);
1628
1923
 
1629
1924
  rb_define_method(rb_cSnapshot, "size", (VALUE(*)(...))&rb_snapshot_size, 0);
1630
1925
  rb_define_method(rb_cSnapshot, "dump", (VALUE(*)(...))&rb_snapshot_dump, 0);
@@ -1632,8 +1927,23 @@ extern "C" {
1632
1927
  rb_define_private_method(rb_cSnapshot, "load", (VALUE(*)(...))&rb_snapshot_load, 1);
1633
1928
 
1634
1929
  rb_define_method(rb_cIsolate, "idle_notification", (VALUE(*)(...))&rb_isolate_idle_notification, 1);
1930
+ rb_define_method(rb_cIsolate, "low_memory_notification", (VALUE(*)(...))&rb_isolate_low_memory_notification, 0);
1931
+ rb_define_method(rb_cIsolate, "pump_message_loop", (VALUE(*)(...))&rb_isolate_pump_message_loop, 0);
1635
1932
  rb_define_private_method(rb_cIsolate, "init_with_snapshot",(VALUE(*)(...))&rb_isolate_init_with_snapshot, 1);
1636
1933
 
1637
1934
  rb_define_singleton_method(rb_cPlatform, "set_flag_as_str!", (VALUE(*)(...))&rb_platform_set_flag_as_str, 1);
1935
+
1936
+ rb_set_end_proc(set_ruby_exiting, Qnil);
1937
+
1938
+ static pthread_attr_t attr;
1939
+ if (pthread_attr_init(&attr) == 0) {
1940
+ if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == 0) {
1941
+ thread_attr_p = &attr;
1942
+ }
1943
+ }
1944
+ auto on_fork_for_child = []() {
1945
+ exit_lock = PTHREAD_RWLOCK_INITIALIZER;
1946
+ };
1947
+ pthread_atfork(nullptr, nullptr, on_fork_for_child);
1638
1948
  }
1639
1949
  }