mini_racer 0.3.1 → 0.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
 
@@ -115,6 +125,7 @@ typedef struct {
115
125
  useconds_t timeout;
116
126
  EvalResult* result;
117
127
  size_t max_memory;
128
+ size_t marshal_stackdepth;
118
129
  } EvalParams;
119
130
 
120
131
  typedef struct {
@@ -126,13 +137,152 @@ typedef struct {
126
137
  Local<Value> *argv;
127
138
  EvalResult result;
128
139
  size_t max_memory;
140
+ size_t marshal_stackdepth;
129
141
  } FunctionCall;
130
142
 
131
- enum IsolateFlags {
132
- IN_GVL,
133
- DO_TERMINATE,
134
- MEM_SOFTLIMIT_VALUE,
135
- 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
+ }
136
286
  };
137
287
 
138
288
  static VALUE rb_cContext;
@@ -155,21 +305,44 @@ static std::unique_ptr<Platform> current_platform = NULL;
155
305
  static std::mutex platform_lock;
156
306
 
157
307
  static pthread_attr_t *thread_attr_p;
158
- static pthread_rwlock_t exit_lock = PTHREAD_RWLOCK_INITIALIZER;
159
- static bool ruby_exiting; // guarded by exit_lock
308
+ static std::atomic_int ruby_exiting(0);
309
+ static bool single_threaded = false;
310
+
311
+ static void mark_context(void *);
312
+ static void deallocate(void *);
313
+ static size_t context_memsize(const void *);
314
+ static const rb_data_type_t context_type = {
315
+ "mini_racer/context_info",
316
+ { mark_context, deallocate, context_memsize }
317
+ };
318
+
319
+ static void deallocate_snapshot(void *);
320
+ static size_t snapshot_memsize(const void *);
321
+ static const rb_data_type_t snapshot_type = {
322
+ "mini_racer/snapshot_info",
323
+ { NULL, deallocate_snapshot, snapshot_memsize }
324
+ };
325
+
326
+ static void mark_isolate(void *);
327
+ static void deallocate_isolate(void *);
328
+ static size_t isolate_memsize(const void *);
329
+ static const rb_data_type_t isolate_type = {
330
+ "mini_racer/isolate_info",
331
+ { mark_isolate, deallocate_isolate, isolate_memsize }
332
+ };
160
333
 
161
334
  static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
162
335
  bool platform_already_initialized = false;
163
336
 
164
- if(TYPE(flag_as_str) != T_STRING) {
165
- rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE" (should be a string)",
166
- rb_obj_class(flag_as_str));
167
- }
337
+ Check_Type(flag_as_str, T_STRING);
168
338
 
169
339
  platform_lock.lock();
170
340
 
171
341
  if (current_platform == NULL) {
172
- V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), (int)RSTRING_LEN(flag_as_str));
342
+ if (!strcmp(RSTRING_PTR(flag_as_str), "--single_threaded")) {
343
+ single_threaded = true;
344
+ }
345
+ V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), RSTRING_LENINT(flag_as_str));
173
346
  } else {
174
347
  platform_already_initialized = true;
175
348
  }
@@ -192,7 +365,11 @@ static void init_v8() {
192
365
 
193
366
  if (current_platform == NULL) {
194
367
  V8::InitializeICU();
195
- current_platform = platform::NewDefaultPlatform();
368
+ if (single_threaded) {
369
+ current_platform = platform::NewSingleThreadedDefaultPlatform();
370
+ } else {
371
+ current_platform = platform::NewDefaultPlatform();
372
+ }
196
373
  V8::InitializePlatform(current_platform.get());
197
374
  V8::Initialize();
198
375
  }
@@ -201,16 +378,18 @@ static void init_v8() {
201
378
  }
202
379
 
203
380
  static void gc_callback(Isolate *isolate, GCType type, GCCallbackFlags flags) {
204
- if((bool)isolate->GetData(MEM_SOFTLIMIT_REACHED)) return;
381
+ if (IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_REACHED)) {
382
+ return;
383
+ }
205
384
 
206
- size_t softlimit = *(size_t*) isolate->GetData(MEM_SOFTLIMIT_VALUE);
385
+ size_t softlimit = IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_MAX);
207
386
 
208
387
  HeapStatistics stats;
209
388
  isolate->GetHeapStatistics(&stats);
210
389
  size_t used = stats.used_heap_size();
211
390
 
212
391
  if(used > softlimit) {
213
- isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)true);
392
+ IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_REACHED, true);
214
393
  isolate->TerminateExecution();
215
394
  }
216
395
  }
@@ -235,28 +414,32 @@ static void prepare_result(MaybeLocal<Value> v8res,
235
414
  Local<Value> local_value = v8res.ToLocalChecked();
236
415
  if ((local_value->IsObject() || local_value->IsArray()) &&
237
416
  !local_value->IsDate() && !local_value->IsFunction()) {
238
- Local<Object> JSON = context->Global()->Get(
239
- context, String::NewFromUtf8Literal(isolate, "JSON"))
240
- .ToLocalChecked().As<Object>();
417
+ MaybeLocal<v8::Value> ml = context->Global()->Get(
418
+ context, String::NewFromUtf8Literal(isolate, "JSON"));
241
419
 
242
- Local<Function> stringify = JSON->Get(
243
- context, v8::String::NewFromUtf8Literal(isolate, "stringify"))
244
- .ToLocalChecked().As<Function>();
245
-
246
- Local<Object> object = local_value->ToObject(context).ToLocalChecked();
247
- const unsigned argc = 1;
248
- Local<Value> argv[argc] = { object };
249
- MaybeLocal<Value> json = stringify->Call(context, JSON, argc, argv);
250
-
251
- if (json.IsEmpty()) {
420
+ if (ml.IsEmpty()) { // exception
252
421
  evalRes.executed = false;
253
422
  } else {
254
- evalRes.json = true;
255
- Persistent<Value>* persistent = new Persistent<Value>();
256
- persistent->Reset(isolate, json.ToLocalChecked());
257
- evalRes.value = persistent;
423
+ Local<Object> JSON = ml.ToLocalChecked().As<Object>();
424
+
425
+ Local<Function> stringify = JSON->Get(
426
+ context, v8::String::NewFromUtf8Literal(isolate, "stringify"))
427
+ .ToLocalChecked().As<Function>();
428
+
429
+ Local<Object> object = local_value->ToObject(context).ToLocalChecked();
430
+ const unsigned argc = 1;
431
+ Local<Value> argv[argc] = { object };
432
+ MaybeLocal<Value> json = stringify->Call(context, JSON, argc, argv);
433
+
434
+ if (json.IsEmpty()) {
435
+ evalRes.executed = false;
436
+ } else {
437
+ evalRes.json = true;
438
+ Persistent<Value>* persistent = new Persistent<Value>();
439
+ persistent->Reset(isolate, json.ToLocalChecked());
440
+ evalRes.value = persistent;
441
+ }
258
442
  }
259
-
260
443
  } else {
261
444
  Persistent<Value>* persistent = new Persistent<Value>();
262
445
  persistent->Reset(isolate, local_value);
@@ -307,7 +490,7 @@ static void prepare_result(MaybeLocal<Value> v8res,
307
490
  }
308
491
  }
309
492
 
310
- void*
493
+ static void*
311
494
  nogvl_context_eval(void* arg) {
312
495
 
313
496
  EvalParams* eval_params = (EvalParams*)arg;
@@ -322,14 +505,15 @@ nogvl_context_eval(void* arg) {
322
505
  Context::Scope context_scope(context);
323
506
  v8::ScriptOrigin *origin = NULL;
324
507
 
325
- // in gvl flag
326
- isolate->SetData(IN_GVL, (void*)false);
327
- // terminate ASAP
328
- isolate->SetData(DO_TERMINATE, (void*)false);
329
- // Memory softlimit
330
- isolate->SetData(MEM_SOFTLIMIT_VALUE, (void*)false);
331
- // Memory softlimit hit flag
332
- isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)false);
508
+ IsolateData::Init(isolate);
509
+
510
+ if (eval_params->max_memory > 0) {
511
+ IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_MAX, eval_params->max_memory);
512
+ if (!isolate_info->added_gc_cb) {
513
+ isolate->AddGCEpilogueCallback(gc_callback);
514
+ isolate_info->added_gc_cb = true;
515
+ }
516
+ }
333
517
 
334
518
  MaybeLocal<Script> parsed_script;
335
519
 
@@ -355,12 +539,8 @@ nogvl_context_eval(void* arg) {
355
539
  result->message->Reset(isolate, trycatch.Exception());
356
540
  } else {
357
541
  // parsing successful
358
- if (eval_params->max_memory > 0) {
359
- isolate->SetData(MEM_SOFTLIMIT_VALUE, &eval_params->max_memory);
360
- if (!isolate_info->added_gc_cb) {
361
- isolate->AddGCEpilogueCallback(gc_callback);
362
- isolate_info->added_gc_cb = true;
363
- }
542
+ if (eval_params->marshal_stackdepth > 0) {
543
+ StackCounter::SetMax(isolate, eval_params->marshal_stackdepth);
364
544
  }
365
545
 
366
546
  maybe_value = parsed_script.ToLocalChecked()->Run(context);
@@ -368,7 +548,7 @@ nogvl_context_eval(void* arg) {
368
548
 
369
549
  prepare_result(maybe_value, trycatch, isolate, context, *result);
370
550
 
371
- isolate->SetData(IN_GVL, (void*)true);
551
+ IsolateData::Set(isolate, IsolateData::IN_GVL, true);
372
552
 
373
553
  return NULL;
374
554
  }
@@ -386,6 +566,18 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
386
566
  Isolate::Scope isolate_scope(isolate);
387
567
  HandleScope scope(isolate);
388
568
 
569
+ StackCounter stackCounter(isolate);
570
+
571
+ if (IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED)) {
572
+ return Qnil;
573
+ }
574
+
575
+ if (stackCounter.IsTooDeep()) {
576
+ IsolateData::Set(isolate, IsolateData::DO_TERMINATE, true);
577
+ isolate->TerminateExecution();
578
+ return Qnil;
579
+ }
580
+
389
581
  if (value->IsNull() || value->IsUndefined()){
390
582
  return Qnil;
391
583
  }
@@ -468,13 +660,9 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
468
660
  v8::String::Utf8Value symbol_name(isolate,
469
661
  Local<Symbol>::Cast(value)->Name());
470
662
 
471
- VALUE str_symbol = rb_enc_str_new(
472
- *symbol_name,
473
- symbol_name.length(),
474
- rb_enc_find("utf-8")
475
- );
663
+ VALUE str_symbol = rb_utf8_str_new(*symbol_name, symbol_name.length());
476
664
 
477
- return ID2SYM(rb_intern_str(str_symbol));
665
+ return rb_str_intern(str_symbol);
478
666
  }
479
667
 
480
668
  MaybeLocal<String> rstr_maybe = value->ToString(context);
@@ -483,7 +671,7 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
483
671
  return Qnil;
484
672
  } else {
485
673
  Local<String> rstr = rstr_maybe.ToLocalChecked();
486
- return rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
674
+ return rb_utf8_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate));
487
675
  }
488
676
  }
489
677
 
@@ -528,7 +716,7 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
528
716
  case T_FLOAT:
529
717
  return scope.Escape(Number::New(isolate, NUM2DBL(value)));
530
718
  case T_STRING:
531
- return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, (int)RSTRING_LEN(value)).ToLocalChecked());
719
+ return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, RSTRING_LENINT(value)).ToLocalChecked());
532
720
  case T_NIL:
533
721
  return scope.Escape(Null(isolate));
534
722
  case T_TRUE:
@@ -539,7 +727,8 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
539
727
  length = RARRAY_LEN(value);
540
728
  array = Array::New(isolate, (int)length);
541
729
  for(i=0; i<length; i++) {
542
- array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
730
+ Maybe<bool> success = array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
731
+ (void)(success);
543
732
  }
544
733
  return scope.Escape(array);
545
734
  case T_HASH:
@@ -548,13 +737,14 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
548
737
  length = RARRAY_LEN(hash_as_array);
549
738
  for(i=0; i<length; i++) {
550
739
  pair = rb_ary_entry(hash_as_array, i);
551
- object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
740
+ Maybe<bool> success = object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
552
741
  convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 1)));
742
+ (void)(success);
553
743
  }
554
744
  return scope.Escape(object);
555
745
  case T_SYMBOL:
556
746
  value = rb_funcall(value, rb_intern("to_s"), 0);
557
- return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, (int)RSTRING_LEN(value)).ToLocalChecked());
747
+ return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, RSTRING_LENINT(value)).ToLocalChecked());
558
748
  case T_DATA:
559
749
  klass = rb_funcall(value, rb_intern("class"), 0);
560
750
  if (klass == rb_cTime || klass == rb_cDateTime)
@@ -629,6 +819,7 @@ create_snapshot_data_blob(const char *embedded_source = nullptr) {
629
819
  SnapshotCreator::FunctionCodeHandling::kClear);
630
820
  }
631
821
 
822
+ static
632
823
  StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
633
824
  const char *warmup_source) {
634
825
  // Use following steps to create a warmed up snapshot blob from a cold one:
@@ -662,21 +853,18 @@ StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
662
853
  return result;
663
854
  }
664
855
 
665
- static VALUE rb_snapshot_size(VALUE self, VALUE str) {
856
+ static VALUE rb_snapshot_size(VALUE self) {
666
857
  SnapshotInfo* snapshot_info;
667
- Data_Get_Struct(self, SnapshotInfo, snapshot_info);
858
+ TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
668
859
 
669
860
  return INT2NUM(snapshot_info->raw_size);
670
861
  }
671
862
 
672
863
  static VALUE rb_snapshot_load(VALUE self, VALUE str) {
673
864
  SnapshotInfo* snapshot_info;
674
- Data_Get_Struct(self, SnapshotInfo, snapshot_info);
865
+ TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
675
866
 
676
- if(TYPE(str) != T_STRING) {
677
- rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
678
- rb_obj_class(str));
679
- }
867
+ Check_Type(str, T_STRING);
680
868
 
681
869
  init_v8();
682
870
 
@@ -692,21 +880,18 @@ static VALUE rb_snapshot_load(VALUE self, VALUE str) {
692
880
  return Qnil;
693
881
  }
694
882
 
695
- static VALUE rb_snapshot_dump(VALUE self, VALUE str) {
883
+ static VALUE rb_snapshot_dump(VALUE self) {
696
884
  SnapshotInfo* snapshot_info;
697
- Data_Get_Struct(self, SnapshotInfo, snapshot_info);
885
+ TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
698
886
 
699
887
  return rb_str_new(snapshot_info->data, snapshot_info->raw_size);
700
888
  }
701
889
 
702
890
  static VALUE rb_snapshot_warmup_unsafe(VALUE self, VALUE str) {
703
891
  SnapshotInfo* snapshot_info;
704
- Data_Get_Struct(self, SnapshotInfo, snapshot_info);
892
+ TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
705
893
 
706
- if(TYPE(str) != T_STRING) {
707
- rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
708
- rb_obj_class(str));
709
- }
894
+ Check_Type(str, T_STRING);
710
895
 
711
896
  init_v8();
712
897
 
@@ -748,13 +933,13 @@ void IsolateInfo::init(SnapshotInfo* snapshot_info) {
748
933
 
749
934
  static VALUE rb_isolate_init_with_snapshot(VALUE self, VALUE snapshot) {
750
935
  IsolateInfo* isolate_info;
751
- Data_Get_Struct(self, IsolateInfo, isolate_info);
936
+ TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
752
937
 
753
938
  init_v8();
754
939
 
755
940
  SnapshotInfo* snapshot_info = nullptr;
756
941
  if (!NIL_P(snapshot)) {
757
- Data_Get_Struct(snapshot, SnapshotInfo, snapshot_info);
942
+ TypedData_Get_Struct(snapshot, SnapshotInfo, &snapshot_type, snapshot_info);
758
943
  }
759
944
 
760
945
  isolate_info->init(snapshot_info);
@@ -765,7 +950,7 @@ static VALUE rb_isolate_init_with_snapshot(VALUE self, VALUE snapshot) {
765
950
 
766
951
  static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
767
952
  IsolateInfo* isolate_info;
768
- Data_Get_Struct(self, IsolateInfo, isolate_info);
953
+ TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
769
954
 
770
955
  if (current_platform == NULL) return Qfalse;
771
956
 
@@ -776,7 +961,7 @@ static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
776
961
 
777
962
  static VALUE rb_isolate_low_memory_notification(VALUE self) {
778
963
  IsolateInfo* isolate_info;
779
- Data_Get_Struct(self, IsolateInfo, isolate_info);
964
+ TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
780
965
 
781
966
  if (current_platform == NULL) return Qfalse;
782
967
 
@@ -786,7 +971,7 @@ static VALUE rb_isolate_low_memory_notification(VALUE self) {
786
971
 
787
972
  static VALUE rb_isolate_pump_message_loop(VALUE self) {
788
973
  IsolateInfo* isolate_info;
789
- Data_Get_Struct(self, IsolateInfo, isolate_info);
974
+ TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
790
975
 
791
976
  if (current_platform == NULL) return Qfalse;
792
977
 
@@ -799,7 +984,7 @@ static VALUE rb_isolate_pump_message_loop(VALUE self) {
799
984
 
800
985
  static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
801
986
  ContextInfo* context_info;
802
- Data_Get_Struct(self, ContextInfo, context_info);
987
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
803
988
 
804
989
  init_v8();
805
990
 
@@ -810,11 +995,11 @@ static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
810
995
 
811
996
  SnapshotInfo *snapshot_info = nullptr;
812
997
  if (!NIL_P(snap) && rb_obj_is_kind_of(snap, rb_cSnapshot)) {
813
- Data_Get_Struct(snap, SnapshotInfo, snapshot_info);
998
+ TypedData_Get_Struct(snap, SnapshotInfo, &snapshot_type, snapshot_info);
814
999
  }
815
1000
  isolate_info->init(snapshot_info);
816
1001
  } else { // given isolate or snapshot
817
- Data_Get_Struct(isolate, IsolateInfo, isolate_info);
1002
+ TypedData_Get_Struct(isolate, IsolateInfo, &isolate_type, isolate_info);
818
1003
  }
819
1004
 
820
1005
  context_info->isolate_info = isolate_info;
@@ -844,7 +1029,7 @@ static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
844
1029
  static VALUE convert_result_to_ruby(VALUE self /* context */,
845
1030
  EvalResult& result) {
846
1031
  ContextInfo *context_info;
847
- Data_Get_Struct(self, ContextInfo, context_info);
1032
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
848
1033
 
849
1034
  Isolate *isolate = context_info->isolate_info->isolate;
850
1035
  Persistent<Context> *p_ctx = context_info->context;
@@ -871,7 +1056,7 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
871
1056
  // a v8 scope, if we do the scope is never cleaned up properly and we leak
872
1057
  if (!result.parsed) {
873
1058
  if(TYPE(message) == T_STRING) {
874
- rb_raise(rb_eParseError, "%s", RSTRING_PTR(message));
1059
+ rb_raise(rb_eParseError, "%" PRIsVALUE, message);
875
1060
  } else {
876
1061
  rb_raise(rb_eParseError, "Unknown JavaScript Error during parse");
877
1062
  }
@@ -880,9 +1065,13 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
880
1065
  if (!result.executed) {
881
1066
  VALUE ruby_exception = rb_iv_get(self, "@current_exception");
882
1067
  if (ruby_exception == Qnil) {
883
- bool mem_softlimit_reached = (bool)isolate->GetData(MEM_SOFTLIMIT_REACHED);
1068
+ bool mem_softlimit_reached = IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_REACHED);
1069
+ bool marshal_stack_maxdepth_reached = IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED);
884
1070
  // If we were terminated or have the memory softlimit flag set
885
- if (result.terminated || mem_softlimit_reached) {
1071
+ if (marshal_stack_maxdepth_reached) {
1072
+ ruby_exception = rb_eScriptRuntimeError;
1073
+ message = rb_utf8_str_new_literal("Marshal object depth too deep. Script terminated.");
1074
+ } else if (result.terminated || mem_softlimit_reached) {
886
1075
  ruby_exception = mem_softlimit_reached ? rb_eV8OutOfMemoryError : rb_eScriptTerminatedError;
887
1076
  } else {
888
1077
  ruby_exception = rb_eScriptRuntimeError;
@@ -890,15 +1079,17 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
890
1079
 
891
1080
  // exception report about what happened
892
1081
  if (TYPE(backtrace) == T_STRING) {
893
- rb_raise(ruby_exception, "%s", RSTRING_PTR(backtrace));
1082
+ rb_raise(ruby_exception, "%" PRIsVALUE, backtrace);
894
1083
  } else if(TYPE(message) == T_STRING) {
895
- rb_raise(ruby_exception, "%s", RSTRING_PTR(message));
1084
+ rb_raise(ruby_exception, "%" PRIsVALUE, message);
896
1085
  } else {
897
1086
  rb_raise(ruby_exception, "Unknown JavaScript Error during execution");
898
1087
  }
1088
+ } else if (rb_obj_is_kind_of(ruby_exception, rb_eException)) {
1089
+ rb_exc_raise(ruby_exception);
899
1090
  } else {
900
1091
  VALUE rb_str = rb_funcall(ruby_exception, rb_intern("to_s"), 0);
901
- rb_raise(CLASS_OF(ruby_exception), "%s", RSTRING_PTR(rb_str));
1092
+ rb_raise(CLASS_OF(ruby_exception), "%" PRIsVALUE, rb_str);
902
1093
  }
903
1094
  }
904
1095
 
@@ -914,9 +1105,10 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
914
1105
 
915
1106
  if (result.json) {
916
1107
  Local<String> rstr = tmp->ToString(p_ctx->Get(isolate)).ToLocalChecked();
917
- VALUE json_string = rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
1108
+ VALUE json_string = rb_utf8_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate));
918
1109
  ret = rb_funcall(rb_mJSON, rb_intern("parse"), 1, json_string);
919
1110
  } else {
1111
+ StackCounter::Reset(isolate);
920
1112
  ret = convert_v8_to_ruby(isolate, *p_ctx, tmp);
921
1113
  }
922
1114
 
@@ -939,16 +1131,13 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
939
1131
  EvalResult eval_result;
940
1132
  ContextInfo* context_info;
941
1133
 
942
- Data_Get_Struct(self, ContextInfo, context_info);
1134
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
943
1135
  Isolate* isolate = context_info->isolate_info->isolate;
944
1136
 
945
- if(TYPE(str) != T_STRING) {
946
- rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
947
- rb_obj_class(str));
948
- }
949
- if(filename != Qnil && TYPE(filename) != T_STRING) {
950
- rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be nil or a string)",
951
- rb_obj_class(filename));
1137
+ Check_Type(str, T_STRING);
1138
+
1139
+ if (!NIL_P(filename)) {
1140
+ Check_Type(filename, T_STRING);
952
1141
  }
953
1142
 
954
1143
  {
@@ -957,13 +1146,13 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
957
1146
  HandleScope handle_scope(isolate);
958
1147
 
959
1148
  Local<String> eval = String::NewFromUtf8(isolate, RSTRING_PTR(str),
960
- NewStringType::kNormal, (int)RSTRING_LEN(str)).ToLocalChecked();
1149
+ NewStringType::kNormal, RSTRING_LENINT(str)).ToLocalChecked();
961
1150
 
962
1151
  Local<String> local_filename;
963
1152
 
964
1153
  if (filename != Qnil) {
965
1154
  local_filename = String::NewFromUtf8(isolate, RSTRING_PTR(filename),
966
- NewStringType::kNormal, (int)RSTRING_LEN(filename)).ToLocalChecked();
1155
+ NewStringType::kNormal, RSTRING_LENINT(filename)).ToLocalChecked();
967
1156
  eval_params.filename = &local_filename;
968
1157
  } else {
969
1158
  eval_params.filename = NULL;
@@ -974,6 +1163,7 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
974
1163
  eval_params.result = &eval_result;
975
1164
  eval_params.timeout = 0;
976
1165
  eval_params.max_memory = 0;
1166
+ eval_params.marshal_stackdepth = 0;
977
1167
  VALUE timeout = rb_iv_get(self, "@timeout");
978
1168
  if (timeout != Qnil) {
979
1169
  eval_params.timeout = (useconds_t)NUM2LONG(timeout);
@@ -984,6 +1174,11 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
984
1174
  eval_params.max_memory = (size_t)NUM2ULONG(mem_softlimit);
985
1175
  }
986
1176
 
1177
+ VALUE stack_depth = rb_iv_get(self, "@marshal_stack_depth");
1178
+ if (stack_depth != Qnil) {
1179
+ eval_params.marshal_stackdepth = (size_t)NUM2ULONG(stack_depth);
1180
+ }
1181
+
987
1182
  eval_result.message = NULL;
988
1183
  eval_result.backtrace = NULL;
989
1184
 
@@ -1021,7 +1216,7 @@ VALUE rescue_callback(VALUE rdata, VALUE exception) {
1021
1216
  return exception;
1022
1217
  }
1023
1218
 
1024
- void*
1219
+ static void*
1025
1220
  gvl_ruby_callback(void* data) {
1026
1221
 
1027
1222
  FunctionCallbackInfo<Value>* args = (FunctionCallbackInfo<Value>*)data;
@@ -1037,7 +1232,7 @@ gvl_ruby_callback(void* data) {
1037
1232
  HandleScope scope(args->GetIsolate());
1038
1233
  Local<External> external = Local<External>::Cast(args->Data());
1039
1234
 
1040
- self = *(VALUE*)(external->Value());
1235
+ self = (VALUE)(external->Value());
1041
1236
  callback = rb_iv_get(self, "@callback");
1042
1237
 
1043
1238
  parent = rb_iv_get(self, "@parent");
@@ -1045,7 +1240,7 @@ gvl_ruby_callback(void* data) {
1045
1240
  return NULL;
1046
1241
  }
1047
1242
 
1048
- Data_Get_Struct(parent, ContextInfo, context_info);
1243
+ TypedData_Get_Struct(parent, ContextInfo, &context_type, context_info);
1049
1244
 
1050
1245
  if (length > 0) {
1051
1246
  ruby_args = rb_ary_tmp_new(length);
@@ -1053,6 +1248,7 @@ gvl_ruby_callback(void* data) {
1053
1248
 
1054
1249
  for (int i = 0; i < length; i++) {
1055
1250
  Local<Value> value = ((*args)[i]).As<Value>();
1251
+ StackCounter::Reset(args->GetIsolate());
1056
1252
  VALUE tmp = convert_v8_to_ruby(args->GetIsolate(),
1057
1253
  *context_info->context, value);
1058
1254
  rb_ary_push(ruby_args, tmp);
@@ -1066,20 +1262,22 @@ gvl_ruby_callback(void* data) {
1066
1262
  callback_data.ruby_args = ruby_args;
1067
1263
  callback_data.failed = false;
1068
1264
 
1069
- if ((bool)args->GetIsolate()->GetData(DO_TERMINATE) == true) {
1265
+ if (IsolateData::Get(args->GetIsolate(), IsolateData::DO_TERMINATE)) {
1070
1266
  args->GetIsolate()->ThrowException(
1071
1267
  String::NewFromUtf8Literal(args->GetIsolate(),
1072
1268
  "Terminated execution during transition from Ruby to JS"));
1073
1269
  args->GetIsolate()->TerminateExecution();
1074
1270
  if (length > 0) {
1075
1271
  rb_ary_clear(ruby_args);
1076
- rb_gc_force_recycle(ruby_args);
1077
1272
  }
1078
1273
  return NULL;
1079
1274
  }
1080
1275
 
1081
- result = rb_rescue2((VALUE(*)(...))&protected_callback, (VALUE)(&callback_data),
1082
- (VALUE(*)(...))&rescue_callback, (VALUE)(&callback_data), rb_eException, (VALUE)0);
1276
+ VALUE callback_data_value = (VALUE)&callback_data;
1277
+
1278
+ // TODO: use rb_vrescue2 in Ruby 2.7 and above
1279
+ result = rb_rescue2(MR_METHOD_FUNC(protected_callback), callback_data_value,
1280
+ MR_METHOD_FUNC(rescue_callback), callback_data_value, rb_eException, (VALUE)0);
1083
1281
 
1084
1282
  if(callback_data.failed) {
1085
1283
  rb_iv_set(parent, "@current_exception", result);
@@ -1093,26 +1291,24 @@ gvl_ruby_callback(void* data) {
1093
1291
 
1094
1292
  if (length > 0) {
1095
1293
  rb_ary_clear(ruby_args);
1096
- rb_gc_force_recycle(ruby_args);
1097
1294
  }
1098
1295
 
1099
- if ((bool)args->GetIsolate()->GetData(DO_TERMINATE) == true) {
1100
- Isolate* isolate = args->GetIsolate();
1101
- isolate->TerminateExecution();
1296
+ if (IsolateData::Get(args->GetIsolate(), IsolateData::DO_TERMINATE)) {
1297
+ args->GetIsolate()->TerminateExecution();
1102
1298
  }
1103
1299
 
1104
1300
  return NULL;
1105
1301
  }
1106
1302
 
1107
1303
  static void ruby_callback(const FunctionCallbackInfo<Value>& args) {
1108
- bool has_gvl = (bool)args.GetIsolate()->GetData(IN_GVL);
1304
+ bool has_gvl = IsolateData::Get(args.GetIsolate(), IsolateData::IN_GVL);
1109
1305
 
1110
1306
  if(has_gvl) {
1111
1307
  gvl_ruby_callback((void*)&args);
1112
1308
  } else {
1113
- args.GetIsolate()->SetData(IN_GVL, (void*)true);
1309
+ IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, true);
1114
1310
  rb_thread_call_with_gvl(gvl_ruby_callback, (void*)(&args));
1115
- args.GetIsolate()->SetData(IN_GVL, (void*)false);
1311
+ IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, false);
1116
1312
  }
1117
1313
  }
1118
1314
 
@@ -1129,7 +1325,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1129
1325
  bool parse_error = false;
1130
1326
  bool attach_error = false;
1131
1327
 
1132
- Data_Get_Struct(parent, ContextInfo, context_info);
1328
+ TypedData_Get_Struct(parent, ContextInfo, &context_type, context_info);
1133
1329
  Isolate* isolate = context_info->isolate_info->isolate;
1134
1330
 
1135
1331
  {
@@ -1142,29 +1338,27 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1142
1338
 
1143
1339
  Local<String> v8_str =
1144
1340
  String::NewFromUtf8(isolate, RSTRING_PTR(name),
1145
- NewStringType::kNormal, (int)RSTRING_LEN(name))
1341
+ NewStringType::kNormal, RSTRING_LENINT(name))
1146
1342
  .ToLocalChecked();
1147
1343
 
1148
- // copy self so we can access from v8 external
1149
- VALUE* self_copy;
1150
- Data_Get_Struct(self, VALUE, self_copy);
1151
- *self_copy = self;
1152
-
1153
- Local<Value> external = External::New(isolate, self_copy);
1344
+ // Note that self (rb_cExternalFunction) is a pure Ruby T_OBJECT,
1345
+ // not a T_DATA type like most other classes in this file
1346
+ Local<Value> external = External::New(isolate, (void *)self);
1154
1347
 
1155
1348
  if (parent_object == Qnil) {
1156
- context->Global()->Set(
1349
+ Maybe<bool> success = context->Global()->Set(
1157
1350
  context,
1158
1351
  v8_str,
1159
1352
  FunctionTemplate::New(isolate, ruby_callback, external)
1160
1353
  ->GetFunction(context)
1161
1354
  .ToLocalChecked());
1355
+ (void)success;
1162
1356
 
1163
1357
  } else {
1164
1358
  Local<String> eval =
1165
1359
  String::NewFromUtf8(isolate, RSTRING_PTR(parent_object_eval),
1166
1360
  NewStringType::kNormal,
1167
- (int)RSTRING_LEN(parent_object_eval))
1361
+ RSTRING_LENINT(parent_object_eval))
1168
1362
  .ToLocalChecked();
1169
1363
 
1170
1364
  MaybeLocal<Script> parsed_script = Script::Compile(context, eval);
@@ -1178,12 +1372,13 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1178
1372
  if (!maybe_value.IsEmpty()) {
1179
1373
  Local<Value> value = maybe_value.ToLocalChecked();
1180
1374
  if (value->IsObject()) {
1181
- value.As<Object>()->Set(
1375
+ Maybe<bool> success = value.As<Object>()->Set(
1182
1376
  context,
1183
1377
  v8_str,
1184
1378
  FunctionTemplate::New(isolate, ruby_callback, external)
1185
1379
  ->GetFunction(context)
1186
1380
  .ToLocalChecked());
1381
+ (void)success;
1187
1382
  attach_error = false;
1188
1383
  }
1189
1384
  }
@@ -1205,7 +1400,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1205
1400
 
1206
1401
  static VALUE rb_context_isolate_mutex(VALUE self) {
1207
1402
  ContextInfo* context_info;
1208
- Data_Get_Struct(self, ContextInfo, context_info);
1403
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1209
1404
 
1210
1405
  if (!context_info->isolate_info) {
1211
1406
  rb_raise(rb_eScriptRuntimeError, "Context has no Isolate available anymore");
@@ -1221,11 +1416,16 @@ IsolateInfo::~IsolateInfo() {
1221
1416
  "it can not be disposed and memory will not be "
1222
1417
  "reclaimed till the Ruby process exits.\n");
1223
1418
  } else {
1224
- if (this->pid != getpid()) {
1419
+ if (this->pid != getpid() && !single_threaded) {
1225
1420
  fprintf(stderr, "WARNING: V8 isolate was forked, "
1226
1421
  "it can not be disposed and "
1227
1422
  "memory will not be reclaimed "
1228
- "till the Ruby process exits.\n");
1423
+ "till the Ruby process exits.\n"
1424
+ "It is VERY likely your process will hang.\n"
1425
+ "If you wish to use v8 in forked environment "
1426
+ "please ensure the platform is initialized with:\n"
1427
+ "MiniRacer::Platform.set_flags! :single_threaded\n"
1428
+ );
1229
1429
  } else {
1230
1430
  isolate->Dispose();
1231
1431
  }
@@ -1256,42 +1456,33 @@ static void free_context_raw(void *arg) {
1256
1456
  if (isolate_info) {
1257
1457
  isolate_info->release();
1258
1458
  }
1259
-
1260
- xfree(context_info);
1261
1459
  }
1262
1460
 
1263
1461
  static void *free_context_thr(void* arg) {
1264
- if (pthread_rwlock_tryrdlock(&exit_lock) != 0) {
1265
- return NULL;
1462
+ if (ruby_exiting.load() == 0) {
1463
+ free_context_raw(arg);
1464
+ xfree(arg);
1266
1465
  }
1267
- if (ruby_exiting) {
1268
- return NULL;
1269
- }
1270
-
1271
- free_context_raw(arg);
1272
-
1273
- pthread_rwlock_unlock(&exit_lock);
1274
-
1275
1466
  return NULL;
1276
1467
  }
1277
1468
 
1278
1469
  // destroys everything except freeing the ContextInfo struct (see deallocate())
1279
1470
  static void free_context(ContextInfo* context_info) {
1280
-
1281
1471
  IsolateInfo* isolate_info = context_info->isolate_info;
1282
1472
 
1283
- ContextInfo* context_info_copy = ALLOC(ContextInfo);
1284
- context_info_copy->isolate_info = context_info->isolate_info;
1285
- context_info_copy->context = context_info->context;
1286
-
1287
1473
  if (isolate_info && isolate_info->refs() > 1) {
1288
1474
  pthread_t free_context_thread;
1475
+ ContextInfo* context_info_copy = ALLOC(ContextInfo);
1476
+
1477
+ context_info_copy->isolate_info = context_info->isolate_info;
1478
+ context_info_copy->context = context_info->context;
1289
1479
  if (pthread_create(&free_context_thread, thread_attr_p,
1290
1480
  free_context_thr, (void*)context_info_copy)) {
1291
1481
  fprintf(stderr, "WARNING failed to release memory in MiniRacer, thread to release could not be created, process will leak memory\n");
1482
+ xfree(context_info_copy);
1292
1483
  }
1293
1484
  } else {
1294
- free_context_raw(context_info_copy);
1485
+ free_context_raw(context_info);
1295
1486
  }
1296
1487
 
1297
1488
  context_info->context = NULL;
@@ -1310,6 +1501,11 @@ static void mark_isolate(void* data) {
1310
1501
  isolate_info->mark();
1311
1502
  }
1312
1503
 
1504
+ static size_t isolate_memsize(const void *ptr) {
1505
+ const IsolateInfo *isolate_info = (const IsolateInfo *)ptr;
1506
+ return sizeof(*isolate_info);
1507
+ }
1508
+
1313
1509
  static void deallocate(void* data) {
1314
1510
  ContextInfo* context_info = (ContextInfo*)data;
1315
1511
 
@@ -1318,6 +1514,11 @@ static void deallocate(void* data) {
1318
1514
  xfree(data);
1319
1515
  }
1320
1516
 
1517
+ static size_t context_memsize(const void *ptr)
1518
+ {
1519
+ return sizeof(ContextInfo);
1520
+ }
1521
+
1321
1522
  static void mark_context(void* data) {
1322
1523
  ContextInfo* context_info = (ContextInfo*)data;
1323
1524
  if (context_info->isolate_info) {
@@ -1325,48 +1526,39 @@ static void mark_context(void* data) {
1325
1526
  }
1326
1527
  }
1327
1528
 
1328
- static void deallocate_external_function(void * data) {
1329
- xfree(data);
1330
- }
1331
-
1332
1529
  static void deallocate_snapshot(void * data) {
1333
1530
  SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
1334
1531
  delete[] snapshot_info->data;
1335
1532
  xfree(snapshot_info);
1336
1533
  }
1337
1534
 
1338
- static VALUE allocate_external_function(VALUE klass) {
1339
- VALUE* self = ALLOC(VALUE);
1340
- return Data_Wrap_Struct(klass, NULL, deallocate_external_function, (void*)self);
1535
+ static size_t snapshot_memsize(const void *data) {
1536
+ SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
1537
+ return sizeof(*snapshot_info) + snapshot_info->raw_size;
1341
1538
  }
1342
1539
 
1343
1540
  static VALUE allocate(VALUE klass) {
1344
- ContextInfo* context_info = ALLOC(ContextInfo);
1345
- context_info->isolate_info = NULL;
1346
- context_info->context = NULL;
1347
-
1348
- return Data_Wrap_Struct(klass, mark_context, deallocate, (void*)context_info);
1541
+ ContextInfo* context_info;
1542
+ return TypedData_Make_Struct(klass, ContextInfo, &context_type, context_info);
1349
1543
  }
1350
1544
 
1351
1545
  static VALUE allocate_snapshot(VALUE klass) {
1352
- SnapshotInfo* snapshot_info = ALLOC(SnapshotInfo);
1353
- snapshot_info->data = NULL;
1354
- snapshot_info->raw_size = 0;
1546
+ SnapshotInfo* snapshot_info;
1355
1547
 
1356
- return Data_Wrap_Struct(klass, NULL, deallocate_snapshot, (void*)snapshot_info);
1548
+ return TypedData_Make_Struct(klass, SnapshotInfo, &snapshot_type, snapshot_info);
1357
1549
  }
1358
1550
 
1359
1551
  static VALUE allocate_isolate(VALUE klass) {
1360
1552
  IsolateInfo* isolate_info = new IsolateInfo();
1361
1553
 
1362
- return Data_Wrap_Struct(klass, mark_isolate, deallocate_isolate, (void*)isolate_info);
1554
+ return TypedData_Wrap_Struct(klass, &isolate_type, isolate_info);
1363
1555
  }
1364
1556
 
1365
1557
  static VALUE
1366
1558
  rb_heap_stats(VALUE self) {
1367
1559
 
1368
1560
  ContextInfo* context_info;
1369
- Data_Get_Struct(self, ContextInfo, context_info);
1561
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1370
1562
  Isolate* isolate;
1371
1563
  v8::HeapStatistics stats;
1372
1564
 
@@ -1398,7 +1590,9 @@ rb_heap_stats(VALUE self) {
1398
1590
  // https://github.com/bnoordhuis/node-heapdump/blob/master/src/heapdump.cc
1399
1591
  class FileOutputStream : public OutputStream {
1400
1592
  public:
1401
- FileOutputStream(FILE* stream) : stream_(stream) {}
1593
+ int err;
1594
+
1595
+ FileOutputStream(int fd) : fd(fd) { err = 0; }
1402
1596
 
1403
1597
  virtual int GetChunkSize() {
1404
1598
  return 65536;
@@ -1407,17 +1601,27 @@ class FileOutputStream : public OutputStream {
1407
1601
  virtual void EndOfStream() {}
1408
1602
 
1409
1603
  virtual WriteResult WriteAsciiChunk(char* data, int size) {
1410
- const size_t len = static_cast<size_t>(size);
1411
- size_t off = 0;
1412
-
1413
- while (off < len && !feof(stream_) && !ferror(stream_))
1414
- off += fwrite(data + off, 1, len - off, stream_);
1415
-
1416
- return off == len ? kContinue : kAbort;
1604
+ size_t len = static_cast<size_t>(size);
1605
+
1606
+ while (len) {
1607
+ ssize_t w = write(fd, data, len);
1608
+
1609
+ if (w > 0) {
1610
+ data += w;
1611
+ len -= w;
1612
+ } else if (w < 0) {
1613
+ err = errno;
1614
+ return kAbort;
1615
+ } else { /* w == 0, could be out-of-space */
1616
+ err = -1;
1617
+ return kAbort;
1618
+ }
1619
+ }
1620
+ return kContinue;
1417
1621
  }
1418
1622
 
1419
1623
  private:
1420
- FILE* stream_;
1624
+ int fd;
1421
1625
  };
1422
1626
 
1423
1627
 
@@ -1430,13 +1634,11 @@ rb_heap_snapshot(VALUE self, VALUE file) {
1430
1634
 
1431
1635
  if (!fptr) return Qfalse;
1432
1636
 
1433
- FILE* fp;
1434
- fp = fdopen(fptr->fd, "w");
1435
- if (fp == NULL) return Qfalse;
1436
-
1637
+ // prepare for unbuffered write(2) below:
1638
+ rb_funcall(file, rb_intern("flush"), 0);
1437
1639
 
1438
1640
  ContextInfo* context_info;
1439
- Data_Get_Struct(self, ContextInfo, context_info);
1641
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1440
1642
  Isolate* isolate;
1441
1643
  isolate = context_info->isolate_info ? context_info->isolate_info->isolate : NULL;
1442
1644
 
@@ -1450,13 +1652,14 @@ rb_heap_snapshot(VALUE self, VALUE file) {
1450
1652
 
1451
1653
  const HeapSnapshot* const snap = heap_profiler->TakeHeapSnapshot();
1452
1654
 
1453
- FileOutputStream stream(fp);
1655
+ FileOutputStream stream(fptr->fd);
1454
1656
  snap->Serialize(&stream, HeapSnapshot::kJSON);
1455
1657
 
1456
- fflush(fp);
1457
-
1458
1658
  const_cast<HeapSnapshot*>(snap)->Delete();
1459
1659
 
1660
+ /* TODO: perhaps rb_sys_fail here */
1661
+ if (stream.err) return Qfalse;
1662
+
1460
1663
  return Qtrue;
1461
1664
  }
1462
1665
 
@@ -1464,12 +1667,12 @@ static VALUE
1464
1667
  rb_context_stop(VALUE self) {
1465
1668
 
1466
1669
  ContextInfo* context_info;
1467
- Data_Get_Struct(self, ContextInfo, context_info);
1670
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1468
1671
 
1469
1672
  Isolate* isolate = context_info->isolate_info->isolate;
1470
1673
 
1471
1674
  // flag for termination
1472
- isolate->SetData(DO_TERMINATE, (void*)true);
1675
+ IsolateData::Set(isolate, IsolateData::DO_TERMINATE, true);
1473
1676
 
1474
1677
  isolate->TerminateExecution();
1475
1678
  rb_funcall(self, rb_intern("stop_attached"), 0);
@@ -1481,7 +1684,7 @@ static VALUE
1481
1684
  rb_context_dispose(VALUE self) {
1482
1685
 
1483
1686
  ContextInfo* context_info;
1484
- Data_Get_Struct(self, ContextInfo, context_info);
1687
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1485
1688
 
1486
1689
  free_context(context_info);
1487
1690
 
@@ -1498,18 +1701,20 @@ nogvl_context_call(void *args) {
1498
1701
  IsolateInfo *isolate_info = call->context_info->isolate_info;
1499
1702
  Isolate* isolate = isolate_info->isolate;
1500
1703
 
1501
- // in gvl flag
1502
- isolate->SetData(IN_GVL, (void*)false);
1503
- // terminate ASAP
1504
- isolate->SetData(DO_TERMINATE, (void*)false);
1704
+ IsolateData::Set(isolate, IsolateData::IN_GVL, false);
1705
+ IsolateData::Set(isolate, IsolateData::DO_TERMINATE, false);
1505
1706
 
1506
1707
  if (call->max_memory > 0) {
1507
- isolate->SetData(MEM_SOFTLIMIT_VALUE, &call->max_memory);
1508
- isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)false);
1708
+ IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_MAX, call->max_memory);
1709
+ IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_REACHED, false);
1509
1710
  if (!isolate_info->added_gc_cb) {
1510
- isolate->AddGCEpilogueCallback(gc_callback);
1711
+ isolate->AddGCEpilogueCallback(gc_callback);
1511
1712
  isolate_info->added_gc_cb = true;
1713
+ }
1512
1714
  }
1715
+
1716
+ if (call->marshal_stackdepth > 0) {
1717
+ StackCounter::SetMax(isolate, call->marshal_stackdepth);
1513
1718
  }
1514
1719
 
1515
1720
  Isolate::Scope isolate_scope(isolate);
@@ -1527,7 +1732,7 @@ nogvl_context_call(void *args) {
1527
1732
  MaybeLocal<v8::Value> res = fun->Call(context, context->Global(), call->argc, call->argv);
1528
1733
  prepare_result(res, trycatch, isolate, context, eval_res);
1529
1734
 
1530
- isolate->SetData(IN_GVL, (void*)true);
1735
+ IsolateData::Set(isolate, IsolateData::IN_GVL, true);
1531
1736
 
1532
1737
  return NULL;
1533
1738
  }
@@ -1542,7 +1747,7 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1542
1747
  FunctionCall call;
1543
1748
  VALUE *call_argv = NULL;
1544
1749
 
1545
- Data_Get_Struct(self, ContextInfo, context_info);
1750
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1546
1751
  Isolate* isolate = context_info->isolate_info->isolate;
1547
1752
 
1548
1753
  if (argc < 1) {
@@ -1550,9 +1755,7 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1550
1755
  }
1551
1756
 
1552
1757
  VALUE function_name = argv[0];
1553
- if (TYPE(function_name) != T_STRING) {
1554
- rb_raise(rb_eTypeError, "first argument should be a String");
1555
- }
1758
+ Check_Type(function_name, T_STRING);
1556
1759
 
1557
1760
  char *fname = RSTRING_PTR(function_name);
1558
1761
  if (!fname) {
@@ -1575,6 +1778,13 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1575
1778
  unsigned long sl_int = NUM2ULONG(mem_softlimit);
1576
1779
  call.max_memory = (size_t)sl_int;
1577
1780
  }
1781
+
1782
+ call.marshal_stackdepth = 0;
1783
+ VALUE marshal_stackdepth = rb_iv_get(self, "@marshal_stack_depth");
1784
+ if (marshal_stackdepth != Qnil) {
1785
+ unsigned long sl_int = NUM2ULONG(marshal_stackdepth);
1786
+ call.marshal_stackdepth = (size_t)sl_int;
1787
+ }
1578
1788
 
1579
1789
  bool missingFunction = false;
1580
1790
  {
@@ -1596,23 +1806,15 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1596
1806
  if (val.IsEmpty() || !val.ToLocalChecked()->IsFunction()) {
1597
1807
  missingFunction = true;
1598
1808
  } else {
1599
-
1600
1809
  Local<v8::Function> fun = Local<v8::Function>::Cast(val.ToLocalChecked());
1810
+ VALUE tmp;
1601
1811
  call.fun = fun;
1602
- int fun_argc = call.argc;
1603
-
1604
- if (fun_argc > 0) {
1605
- call.argv = (v8::Local<Value> *) malloc(sizeof(void *) * fun_argc);
1606
- if (!call.argv) {
1607
- return Qnil;
1608
- }
1609
- for(int i=0; i < fun_argc; i++) {
1610
- call.argv[i] = convert_ruby_to_v8(isolate, context, call_argv[i]);
1611
- }
1812
+ call.argv = (v8::Local<Value> *)RB_ALLOCV_N(void *, tmp, call.argc);
1813
+ for(int i=0; i < call.argc; i++) {
1814
+ call.argv[i] = convert_ruby_to_v8(isolate, context, call_argv[i]);
1612
1815
  }
1613
1816
  rb_thread_call_without_gvl(nogvl_context_call, &call, unblock_function, &call);
1614
- free(call.argv);
1615
-
1817
+ RB_ALLOCV_END(tmp);
1616
1818
  }
1617
1819
  }
1618
1820
 
@@ -1625,7 +1827,7 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1625
1827
 
1626
1828
  static VALUE rb_context_create_isolate_value(VALUE self) {
1627
1829
  ContextInfo* context_info;
1628
- Data_Get_Struct(self, ContextInfo, context_info);
1830
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1629
1831
  IsolateInfo *isolate_info = context_info->isolate_info;
1630
1832
 
1631
1833
  if (!isolate_info) {
@@ -1633,22 +1835,18 @@ static VALUE rb_context_create_isolate_value(VALUE self) {
1633
1835
  }
1634
1836
 
1635
1837
  isolate_info->hold();
1636
- return Data_Wrap_Struct(rb_cIsolate, NULL, &deallocate_isolate, isolate_info);
1838
+ return TypedData_Wrap_Struct(rb_cIsolate, &isolate_type, isolate_info);
1637
1839
  }
1638
1840
 
1639
1841
  static void set_ruby_exiting(VALUE value) {
1640
1842
  (void)value;
1641
1843
 
1642
- int res = pthread_rwlock_wrlock(&exit_lock);
1643
- ruby_exiting = true;
1644
- if (res == 0) {
1645
- pthread_rwlock_unlock(&exit_lock);
1646
- }
1844
+ ruby_exiting.store(1);
1647
1845
  }
1648
1846
 
1649
1847
  extern "C" {
1650
1848
 
1651
- void Init_mini_racer_extension ( void )
1849
+ __attribute__((visibility("default"))) void Init_mini_racer_extension ( void )
1652
1850
  {
1653
1851
  VALUE rb_mMiniRacer = rb_define_module("MiniRacer");
1654
1852
  rb_cContext = rb_define_class_under(rb_mMiniRacer, "Context", rb_cObject);
@@ -1688,7 +1886,6 @@ extern "C" {
1688
1886
  rb_define_alloc_func(rb_cIsolate, allocate_isolate);
1689
1887
 
1690
1888
  rb_define_private_method(rb_cExternalFunction, "notify_v8", (VALUE(*)(...))&rb_external_function_notify_v8, 0);
1691
- rb_define_alloc_func(rb_cExternalFunction, allocate_external_function);
1692
1889
 
1693
1890
  rb_define_method(rb_cSnapshot, "size", (VALUE(*)(...))&rb_snapshot_size, 0);
1694
1891
  rb_define_method(rb_cSnapshot, "dump", (VALUE(*)(...))&rb_snapshot_dump, 0);