mini_racer 0.4.0 → 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,17 +305,36 @@ 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 = false; // guarded by exit_lock
308
+ static std::atomic_int ruby_exiting(0);
160
309
  static bool single_threaded = false;
161
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
+ };
333
+
162
334
  static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
163
335
  bool platform_already_initialized = false;
164
336
 
165
- if(TYPE(flag_as_str) != T_STRING) {
166
- rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE" (should be a string)",
167
- rb_obj_class(flag_as_str));
168
- }
337
+ Check_Type(flag_as_str, T_STRING);
169
338
 
170
339
  platform_lock.lock();
171
340
 
@@ -173,7 +342,7 @@ static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
173
342
  if (!strcmp(RSTRING_PTR(flag_as_str), "--single_threaded")) {
174
343
  single_threaded = true;
175
344
  }
176
- V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), (int)RSTRING_LEN(flag_as_str));
345
+ V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), RSTRING_LENINT(flag_as_str));
177
346
  } else {
178
347
  platform_already_initialized = true;
179
348
  }
@@ -196,7 +365,11 @@ static void init_v8() {
196
365
 
197
366
  if (current_platform == NULL) {
198
367
  V8::InitializeICU();
199
- current_platform = platform::NewDefaultPlatform();
368
+ if (single_threaded) {
369
+ current_platform = platform::NewSingleThreadedDefaultPlatform();
370
+ } else {
371
+ current_platform = platform::NewDefaultPlatform();
372
+ }
200
373
  V8::InitializePlatform(current_platform.get());
201
374
  V8::Initialize();
202
375
  }
@@ -205,16 +378,18 @@ static void init_v8() {
205
378
  }
206
379
 
207
380
  static void gc_callback(Isolate *isolate, GCType type, GCCallbackFlags flags) {
208
- if((bool)isolate->GetData(MEM_SOFTLIMIT_REACHED)) return;
381
+ if (IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_REACHED)) {
382
+ return;
383
+ }
209
384
 
210
- size_t softlimit = *(size_t*) isolate->GetData(MEM_SOFTLIMIT_VALUE);
385
+ size_t softlimit = IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_MAX);
211
386
 
212
387
  HeapStatistics stats;
213
388
  isolate->GetHeapStatistics(&stats);
214
389
  size_t used = stats.used_heap_size();
215
390
 
216
391
  if(used > softlimit) {
217
- isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)true);
392
+ IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_REACHED, true);
218
393
  isolate->TerminateExecution();
219
394
  }
220
395
  }
@@ -239,28 +414,32 @@ static void prepare_result(MaybeLocal<Value> v8res,
239
414
  Local<Value> local_value = v8res.ToLocalChecked();
240
415
  if ((local_value->IsObject() || local_value->IsArray()) &&
241
416
  !local_value->IsDate() && !local_value->IsFunction()) {
242
- Local<Object> JSON = context->Global()->Get(
243
- context, String::NewFromUtf8Literal(isolate, "JSON"))
244
- .ToLocalChecked().As<Object>();
417
+ MaybeLocal<v8::Value> ml = context->Global()->Get(
418
+ context, String::NewFromUtf8Literal(isolate, "JSON"));
245
419
 
246
- Local<Function> stringify = JSON->Get(
247
- context, v8::String::NewFromUtf8Literal(isolate, "stringify"))
248
- .ToLocalChecked().As<Function>();
249
-
250
- Local<Object> object = local_value->ToObject(context).ToLocalChecked();
251
- const unsigned argc = 1;
252
- Local<Value> argv[argc] = { object };
253
- MaybeLocal<Value> json = stringify->Call(context, JSON, argc, argv);
254
-
255
- if (json.IsEmpty()) {
420
+ if (ml.IsEmpty()) { // exception
256
421
  evalRes.executed = false;
257
422
  } else {
258
- evalRes.json = true;
259
- Persistent<Value>* persistent = new Persistent<Value>();
260
- persistent->Reset(isolate, json.ToLocalChecked());
261
- 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
+ }
262
442
  }
263
-
264
443
  } else {
265
444
  Persistent<Value>* persistent = new Persistent<Value>();
266
445
  persistent->Reset(isolate, local_value);
@@ -311,7 +490,7 @@ static void prepare_result(MaybeLocal<Value> v8res,
311
490
  }
312
491
  }
313
492
 
314
- void*
493
+ static void*
315
494
  nogvl_context_eval(void* arg) {
316
495
 
317
496
  EvalParams* eval_params = (EvalParams*)arg;
@@ -326,14 +505,15 @@ nogvl_context_eval(void* arg) {
326
505
  Context::Scope context_scope(context);
327
506
  v8::ScriptOrigin *origin = NULL;
328
507
 
329
- // in gvl flag
330
- isolate->SetData(IN_GVL, (void*)false);
331
- // terminate ASAP
332
- isolate->SetData(DO_TERMINATE, (void*)false);
333
- // Memory softlimit
334
- isolate->SetData(MEM_SOFTLIMIT_VALUE, (void*)false);
335
- // Memory softlimit hit flag
336
- 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
+ }
337
517
 
338
518
  MaybeLocal<Script> parsed_script;
339
519
 
@@ -359,12 +539,8 @@ nogvl_context_eval(void* arg) {
359
539
  result->message->Reset(isolate, trycatch.Exception());
360
540
  } else {
361
541
  // parsing successful
362
- if (eval_params->max_memory > 0) {
363
- isolate->SetData(MEM_SOFTLIMIT_VALUE, &eval_params->max_memory);
364
- if (!isolate_info->added_gc_cb) {
365
- isolate->AddGCEpilogueCallback(gc_callback);
366
- isolate_info->added_gc_cb = true;
367
- }
542
+ if (eval_params->marshal_stackdepth > 0) {
543
+ StackCounter::SetMax(isolate, eval_params->marshal_stackdepth);
368
544
  }
369
545
 
370
546
  maybe_value = parsed_script.ToLocalChecked()->Run(context);
@@ -372,7 +548,7 @@ nogvl_context_eval(void* arg) {
372
548
 
373
549
  prepare_result(maybe_value, trycatch, isolate, context, *result);
374
550
 
375
- isolate->SetData(IN_GVL, (void*)true);
551
+ IsolateData::Set(isolate, IsolateData::IN_GVL, true);
376
552
 
377
553
  return NULL;
378
554
  }
@@ -390,6 +566,18 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
390
566
  Isolate::Scope isolate_scope(isolate);
391
567
  HandleScope scope(isolate);
392
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
+
393
581
  if (value->IsNull() || value->IsUndefined()){
394
582
  return Qnil;
395
583
  }
@@ -472,13 +660,9 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
472
660
  v8::String::Utf8Value symbol_name(isolate,
473
661
  Local<Symbol>::Cast(value)->Name());
474
662
 
475
- VALUE str_symbol = rb_enc_str_new(
476
- *symbol_name,
477
- symbol_name.length(),
478
- rb_enc_find("utf-8")
479
- );
663
+ VALUE str_symbol = rb_utf8_str_new(*symbol_name, symbol_name.length());
480
664
 
481
- return ID2SYM(rb_intern_str(str_symbol));
665
+ return rb_str_intern(str_symbol);
482
666
  }
483
667
 
484
668
  MaybeLocal<String> rstr_maybe = value->ToString(context);
@@ -487,7 +671,7 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
487
671
  return Qnil;
488
672
  } else {
489
673
  Local<String> rstr = rstr_maybe.ToLocalChecked();
490
- 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));
491
675
  }
492
676
  }
493
677
 
@@ -532,7 +716,7 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
532
716
  case T_FLOAT:
533
717
  return scope.Escape(Number::New(isolate, NUM2DBL(value)));
534
718
  case T_STRING:
535
- 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());
536
720
  case T_NIL:
537
721
  return scope.Escape(Null(isolate));
538
722
  case T_TRUE:
@@ -543,7 +727,8 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
543
727
  length = RARRAY_LEN(value);
544
728
  array = Array::New(isolate, (int)length);
545
729
  for(i=0; i<length; i++) {
546
- 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);
547
732
  }
548
733
  return scope.Escape(array);
549
734
  case T_HASH:
@@ -552,13 +737,14 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
552
737
  length = RARRAY_LEN(hash_as_array);
553
738
  for(i=0; i<length; i++) {
554
739
  pair = rb_ary_entry(hash_as_array, i);
555
- 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)),
556
741
  convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 1)));
742
+ (void)(success);
557
743
  }
558
744
  return scope.Escape(object);
559
745
  case T_SYMBOL:
560
746
  value = rb_funcall(value, rb_intern("to_s"), 0);
561
- 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());
562
748
  case T_DATA:
563
749
  klass = rb_funcall(value, rb_intern("class"), 0);
564
750
  if (klass == rb_cTime || klass == rb_cDateTime)
@@ -633,6 +819,7 @@ create_snapshot_data_blob(const char *embedded_source = nullptr) {
633
819
  SnapshotCreator::FunctionCodeHandling::kClear);
634
820
  }
635
821
 
822
+ static
636
823
  StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
637
824
  const char *warmup_source) {
638
825
  // Use following steps to create a warmed up snapshot blob from a cold one:
@@ -666,21 +853,18 @@ StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
666
853
  return result;
667
854
  }
668
855
 
669
- static VALUE rb_snapshot_size(VALUE self, VALUE str) {
856
+ static VALUE rb_snapshot_size(VALUE self) {
670
857
  SnapshotInfo* snapshot_info;
671
- Data_Get_Struct(self, SnapshotInfo, snapshot_info);
858
+ TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
672
859
 
673
860
  return INT2NUM(snapshot_info->raw_size);
674
861
  }
675
862
 
676
863
  static VALUE rb_snapshot_load(VALUE self, VALUE str) {
677
864
  SnapshotInfo* snapshot_info;
678
- Data_Get_Struct(self, SnapshotInfo, snapshot_info);
865
+ TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
679
866
 
680
- if(TYPE(str) != T_STRING) {
681
- rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
682
- rb_obj_class(str));
683
- }
867
+ Check_Type(str, T_STRING);
684
868
 
685
869
  init_v8();
686
870
 
@@ -696,21 +880,18 @@ static VALUE rb_snapshot_load(VALUE self, VALUE str) {
696
880
  return Qnil;
697
881
  }
698
882
 
699
- static VALUE rb_snapshot_dump(VALUE self, VALUE str) {
883
+ static VALUE rb_snapshot_dump(VALUE self) {
700
884
  SnapshotInfo* snapshot_info;
701
- Data_Get_Struct(self, SnapshotInfo, snapshot_info);
885
+ TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
702
886
 
703
887
  return rb_str_new(snapshot_info->data, snapshot_info->raw_size);
704
888
  }
705
889
 
706
890
  static VALUE rb_snapshot_warmup_unsafe(VALUE self, VALUE str) {
707
891
  SnapshotInfo* snapshot_info;
708
- Data_Get_Struct(self, SnapshotInfo, snapshot_info);
892
+ TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
709
893
 
710
- if(TYPE(str) != T_STRING) {
711
- rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
712
- rb_obj_class(str));
713
- }
894
+ Check_Type(str, T_STRING);
714
895
 
715
896
  init_v8();
716
897
 
@@ -752,13 +933,13 @@ void IsolateInfo::init(SnapshotInfo* snapshot_info) {
752
933
 
753
934
  static VALUE rb_isolate_init_with_snapshot(VALUE self, VALUE snapshot) {
754
935
  IsolateInfo* isolate_info;
755
- Data_Get_Struct(self, IsolateInfo, isolate_info);
936
+ TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
756
937
 
757
938
  init_v8();
758
939
 
759
940
  SnapshotInfo* snapshot_info = nullptr;
760
941
  if (!NIL_P(snapshot)) {
761
- Data_Get_Struct(snapshot, SnapshotInfo, snapshot_info);
942
+ TypedData_Get_Struct(snapshot, SnapshotInfo, &snapshot_type, snapshot_info);
762
943
  }
763
944
 
764
945
  isolate_info->init(snapshot_info);
@@ -769,7 +950,7 @@ static VALUE rb_isolate_init_with_snapshot(VALUE self, VALUE snapshot) {
769
950
 
770
951
  static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
771
952
  IsolateInfo* isolate_info;
772
- Data_Get_Struct(self, IsolateInfo, isolate_info);
953
+ TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
773
954
 
774
955
  if (current_platform == NULL) return Qfalse;
775
956
 
@@ -780,7 +961,7 @@ static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
780
961
 
781
962
  static VALUE rb_isolate_low_memory_notification(VALUE self) {
782
963
  IsolateInfo* isolate_info;
783
- Data_Get_Struct(self, IsolateInfo, isolate_info);
964
+ TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
784
965
 
785
966
  if (current_platform == NULL) return Qfalse;
786
967
 
@@ -790,7 +971,7 @@ static VALUE rb_isolate_low_memory_notification(VALUE self) {
790
971
 
791
972
  static VALUE rb_isolate_pump_message_loop(VALUE self) {
792
973
  IsolateInfo* isolate_info;
793
- Data_Get_Struct(self, IsolateInfo, isolate_info);
974
+ TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
794
975
 
795
976
  if (current_platform == NULL) return Qfalse;
796
977
 
@@ -803,7 +984,7 @@ static VALUE rb_isolate_pump_message_loop(VALUE self) {
803
984
 
804
985
  static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
805
986
  ContextInfo* context_info;
806
- Data_Get_Struct(self, ContextInfo, context_info);
987
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
807
988
 
808
989
  init_v8();
809
990
 
@@ -814,11 +995,11 @@ static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
814
995
 
815
996
  SnapshotInfo *snapshot_info = nullptr;
816
997
  if (!NIL_P(snap) && rb_obj_is_kind_of(snap, rb_cSnapshot)) {
817
- Data_Get_Struct(snap, SnapshotInfo, snapshot_info);
998
+ TypedData_Get_Struct(snap, SnapshotInfo, &snapshot_type, snapshot_info);
818
999
  }
819
1000
  isolate_info->init(snapshot_info);
820
1001
  } else { // given isolate or snapshot
821
- Data_Get_Struct(isolate, IsolateInfo, isolate_info);
1002
+ TypedData_Get_Struct(isolate, IsolateInfo, &isolate_type, isolate_info);
822
1003
  }
823
1004
 
824
1005
  context_info->isolate_info = isolate_info;
@@ -848,7 +1029,7 @@ static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
848
1029
  static VALUE convert_result_to_ruby(VALUE self /* context */,
849
1030
  EvalResult& result) {
850
1031
  ContextInfo *context_info;
851
- Data_Get_Struct(self, ContextInfo, context_info);
1032
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
852
1033
 
853
1034
  Isolate *isolate = context_info->isolate_info->isolate;
854
1035
  Persistent<Context> *p_ctx = context_info->context;
@@ -875,7 +1056,7 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
875
1056
  // a v8 scope, if we do the scope is never cleaned up properly and we leak
876
1057
  if (!result.parsed) {
877
1058
  if(TYPE(message) == T_STRING) {
878
- rb_raise(rb_eParseError, "%s", RSTRING_PTR(message));
1059
+ rb_raise(rb_eParseError, "%" PRIsVALUE, message);
879
1060
  } else {
880
1061
  rb_raise(rb_eParseError, "Unknown JavaScript Error during parse");
881
1062
  }
@@ -884,9 +1065,13 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
884
1065
  if (!result.executed) {
885
1066
  VALUE ruby_exception = rb_iv_get(self, "@current_exception");
886
1067
  if (ruby_exception == Qnil) {
887
- 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);
888
1070
  // If we were terminated or have the memory softlimit flag set
889
- 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) {
890
1075
  ruby_exception = mem_softlimit_reached ? rb_eV8OutOfMemoryError : rb_eScriptTerminatedError;
891
1076
  } else {
892
1077
  ruby_exception = rb_eScriptRuntimeError;
@@ -894,15 +1079,17 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
894
1079
 
895
1080
  // exception report about what happened
896
1081
  if (TYPE(backtrace) == T_STRING) {
897
- rb_raise(ruby_exception, "%s", RSTRING_PTR(backtrace));
1082
+ rb_raise(ruby_exception, "%" PRIsVALUE, backtrace);
898
1083
  } else if(TYPE(message) == T_STRING) {
899
- rb_raise(ruby_exception, "%s", RSTRING_PTR(message));
1084
+ rb_raise(ruby_exception, "%" PRIsVALUE, message);
900
1085
  } else {
901
1086
  rb_raise(ruby_exception, "Unknown JavaScript Error during execution");
902
1087
  }
1088
+ } else if (rb_obj_is_kind_of(ruby_exception, rb_eException)) {
1089
+ rb_exc_raise(ruby_exception);
903
1090
  } else {
904
1091
  VALUE rb_str = rb_funcall(ruby_exception, rb_intern("to_s"), 0);
905
- rb_raise(CLASS_OF(ruby_exception), "%s", RSTRING_PTR(rb_str));
1092
+ rb_raise(CLASS_OF(ruby_exception), "%" PRIsVALUE, rb_str);
906
1093
  }
907
1094
  }
908
1095
 
@@ -918,9 +1105,10 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
918
1105
 
919
1106
  if (result.json) {
920
1107
  Local<String> rstr = tmp->ToString(p_ctx->Get(isolate)).ToLocalChecked();
921
- 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));
922
1109
  ret = rb_funcall(rb_mJSON, rb_intern("parse"), 1, json_string);
923
1110
  } else {
1111
+ StackCounter::Reset(isolate);
924
1112
  ret = convert_v8_to_ruby(isolate, *p_ctx, tmp);
925
1113
  }
926
1114
 
@@ -943,16 +1131,13 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
943
1131
  EvalResult eval_result;
944
1132
  ContextInfo* context_info;
945
1133
 
946
- Data_Get_Struct(self, ContextInfo, context_info);
1134
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
947
1135
  Isolate* isolate = context_info->isolate_info->isolate;
948
1136
 
949
- if(TYPE(str) != T_STRING) {
950
- rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
951
- rb_obj_class(str));
952
- }
953
- if(filename != Qnil && TYPE(filename) != T_STRING) {
954
- rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be nil or a string)",
955
- rb_obj_class(filename));
1137
+ Check_Type(str, T_STRING);
1138
+
1139
+ if (!NIL_P(filename)) {
1140
+ Check_Type(filename, T_STRING);
956
1141
  }
957
1142
 
958
1143
  {
@@ -961,13 +1146,13 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
961
1146
  HandleScope handle_scope(isolate);
962
1147
 
963
1148
  Local<String> eval = String::NewFromUtf8(isolate, RSTRING_PTR(str),
964
- NewStringType::kNormal, (int)RSTRING_LEN(str)).ToLocalChecked();
1149
+ NewStringType::kNormal, RSTRING_LENINT(str)).ToLocalChecked();
965
1150
 
966
1151
  Local<String> local_filename;
967
1152
 
968
1153
  if (filename != Qnil) {
969
1154
  local_filename = String::NewFromUtf8(isolate, RSTRING_PTR(filename),
970
- NewStringType::kNormal, (int)RSTRING_LEN(filename)).ToLocalChecked();
1155
+ NewStringType::kNormal, RSTRING_LENINT(filename)).ToLocalChecked();
971
1156
  eval_params.filename = &local_filename;
972
1157
  } else {
973
1158
  eval_params.filename = NULL;
@@ -978,6 +1163,7 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
978
1163
  eval_params.result = &eval_result;
979
1164
  eval_params.timeout = 0;
980
1165
  eval_params.max_memory = 0;
1166
+ eval_params.marshal_stackdepth = 0;
981
1167
  VALUE timeout = rb_iv_get(self, "@timeout");
982
1168
  if (timeout != Qnil) {
983
1169
  eval_params.timeout = (useconds_t)NUM2LONG(timeout);
@@ -988,6 +1174,11 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
988
1174
  eval_params.max_memory = (size_t)NUM2ULONG(mem_softlimit);
989
1175
  }
990
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
+
991
1182
  eval_result.message = NULL;
992
1183
  eval_result.backtrace = NULL;
993
1184
 
@@ -1025,7 +1216,7 @@ VALUE rescue_callback(VALUE rdata, VALUE exception) {
1025
1216
  return exception;
1026
1217
  }
1027
1218
 
1028
- void*
1219
+ static void*
1029
1220
  gvl_ruby_callback(void* data) {
1030
1221
 
1031
1222
  FunctionCallbackInfo<Value>* args = (FunctionCallbackInfo<Value>*)data;
@@ -1041,7 +1232,7 @@ gvl_ruby_callback(void* data) {
1041
1232
  HandleScope scope(args->GetIsolate());
1042
1233
  Local<External> external = Local<External>::Cast(args->Data());
1043
1234
 
1044
- self = *(VALUE*)(external->Value());
1235
+ self = (VALUE)(external->Value());
1045
1236
  callback = rb_iv_get(self, "@callback");
1046
1237
 
1047
1238
  parent = rb_iv_get(self, "@parent");
@@ -1049,7 +1240,7 @@ gvl_ruby_callback(void* data) {
1049
1240
  return NULL;
1050
1241
  }
1051
1242
 
1052
- Data_Get_Struct(parent, ContextInfo, context_info);
1243
+ TypedData_Get_Struct(parent, ContextInfo, &context_type, context_info);
1053
1244
 
1054
1245
  if (length > 0) {
1055
1246
  ruby_args = rb_ary_tmp_new(length);
@@ -1057,6 +1248,7 @@ gvl_ruby_callback(void* data) {
1057
1248
 
1058
1249
  for (int i = 0; i < length; i++) {
1059
1250
  Local<Value> value = ((*args)[i]).As<Value>();
1251
+ StackCounter::Reset(args->GetIsolate());
1060
1252
  VALUE tmp = convert_v8_to_ruby(args->GetIsolate(),
1061
1253
  *context_info->context, value);
1062
1254
  rb_ary_push(ruby_args, tmp);
@@ -1070,20 +1262,22 @@ gvl_ruby_callback(void* data) {
1070
1262
  callback_data.ruby_args = ruby_args;
1071
1263
  callback_data.failed = false;
1072
1264
 
1073
- if ((bool)args->GetIsolate()->GetData(DO_TERMINATE) == true) {
1265
+ if (IsolateData::Get(args->GetIsolate(), IsolateData::DO_TERMINATE)) {
1074
1266
  args->GetIsolate()->ThrowException(
1075
1267
  String::NewFromUtf8Literal(args->GetIsolate(),
1076
1268
  "Terminated execution during transition from Ruby to JS"));
1077
1269
  args->GetIsolate()->TerminateExecution();
1078
1270
  if (length > 0) {
1079
1271
  rb_ary_clear(ruby_args);
1080
- rb_gc_force_recycle(ruby_args);
1081
1272
  }
1082
1273
  return NULL;
1083
1274
  }
1084
1275
 
1085
- result = rb_rescue2((VALUE(*)(...))&protected_callback, (VALUE)(&callback_data),
1086
- (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);
1087
1281
 
1088
1282
  if(callback_data.failed) {
1089
1283
  rb_iv_set(parent, "@current_exception", result);
@@ -1097,26 +1291,24 @@ gvl_ruby_callback(void* data) {
1097
1291
 
1098
1292
  if (length > 0) {
1099
1293
  rb_ary_clear(ruby_args);
1100
- rb_gc_force_recycle(ruby_args);
1101
1294
  }
1102
1295
 
1103
- if ((bool)args->GetIsolate()->GetData(DO_TERMINATE) == true) {
1104
- Isolate* isolate = args->GetIsolate();
1105
- isolate->TerminateExecution();
1296
+ if (IsolateData::Get(args->GetIsolate(), IsolateData::DO_TERMINATE)) {
1297
+ args->GetIsolate()->TerminateExecution();
1106
1298
  }
1107
1299
 
1108
1300
  return NULL;
1109
1301
  }
1110
1302
 
1111
1303
  static void ruby_callback(const FunctionCallbackInfo<Value>& args) {
1112
- bool has_gvl = (bool)args.GetIsolate()->GetData(IN_GVL);
1304
+ bool has_gvl = IsolateData::Get(args.GetIsolate(), IsolateData::IN_GVL);
1113
1305
 
1114
1306
  if(has_gvl) {
1115
1307
  gvl_ruby_callback((void*)&args);
1116
1308
  } else {
1117
- args.GetIsolate()->SetData(IN_GVL, (void*)true);
1309
+ IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, true);
1118
1310
  rb_thread_call_with_gvl(gvl_ruby_callback, (void*)(&args));
1119
- args.GetIsolate()->SetData(IN_GVL, (void*)false);
1311
+ IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, false);
1120
1312
  }
1121
1313
  }
1122
1314
 
@@ -1133,7 +1325,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1133
1325
  bool parse_error = false;
1134
1326
  bool attach_error = false;
1135
1327
 
1136
- Data_Get_Struct(parent, ContextInfo, context_info);
1328
+ TypedData_Get_Struct(parent, ContextInfo, &context_type, context_info);
1137
1329
  Isolate* isolate = context_info->isolate_info->isolate;
1138
1330
 
1139
1331
  {
@@ -1146,29 +1338,27 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1146
1338
 
1147
1339
  Local<String> v8_str =
1148
1340
  String::NewFromUtf8(isolate, RSTRING_PTR(name),
1149
- NewStringType::kNormal, (int)RSTRING_LEN(name))
1341
+ NewStringType::kNormal, RSTRING_LENINT(name))
1150
1342
  .ToLocalChecked();
1151
1343
 
1152
- // copy self so we can access from v8 external
1153
- VALUE* self_copy;
1154
- Data_Get_Struct(self, VALUE, self_copy);
1155
- *self_copy = self;
1156
-
1157
- 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);
1158
1347
 
1159
1348
  if (parent_object == Qnil) {
1160
- context->Global()->Set(
1349
+ Maybe<bool> success = context->Global()->Set(
1161
1350
  context,
1162
1351
  v8_str,
1163
1352
  FunctionTemplate::New(isolate, ruby_callback, external)
1164
1353
  ->GetFunction(context)
1165
1354
  .ToLocalChecked());
1355
+ (void)success;
1166
1356
 
1167
1357
  } else {
1168
1358
  Local<String> eval =
1169
1359
  String::NewFromUtf8(isolate, RSTRING_PTR(parent_object_eval),
1170
1360
  NewStringType::kNormal,
1171
- (int)RSTRING_LEN(parent_object_eval))
1361
+ RSTRING_LENINT(parent_object_eval))
1172
1362
  .ToLocalChecked();
1173
1363
 
1174
1364
  MaybeLocal<Script> parsed_script = Script::Compile(context, eval);
@@ -1182,12 +1372,13 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1182
1372
  if (!maybe_value.IsEmpty()) {
1183
1373
  Local<Value> value = maybe_value.ToLocalChecked();
1184
1374
  if (value->IsObject()) {
1185
- value.As<Object>()->Set(
1375
+ Maybe<bool> success = value.As<Object>()->Set(
1186
1376
  context,
1187
1377
  v8_str,
1188
1378
  FunctionTemplate::New(isolate, ruby_callback, external)
1189
1379
  ->GetFunction(context)
1190
1380
  .ToLocalChecked());
1381
+ (void)success;
1191
1382
  attach_error = false;
1192
1383
  }
1193
1384
  }
@@ -1209,7 +1400,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1209
1400
 
1210
1401
  static VALUE rb_context_isolate_mutex(VALUE self) {
1211
1402
  ContextInfo* context_info;
1212
- Data_Get_Struct(self, ContextInfo, context_info);
1403
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1213
1404
 
1214
1405
  if (!context_info->isolate_info) {
1215
1406
  rb_raise(rb_eScriptRuntimeError, "Context has no Isolate available anymore");
@@ -1265,42 +1456,33 @@ static void free_context_raw(void *arg) {
1265
1456
  if (isolate_info) {
1266
1457
  isolate_info->release();
1267
1458
  }
1268
-
1269
- xfree(context_info);
1270
1459
  }
1271
1460
 
1272
1461
  static void *free_context_thr(void* arg) {
1273
- if (pthread_rwlock_tryrdlock(&exit_lock) != 0) {
1274
- return NULL;
1275
- }
1276
- if (ruby_exiting) {
1277
- return NULL;
1462
+ if (ruby_exiting.load() == 0) {
1463
+ free_context_raw(arg);
1464
+ xfree(arg);
1278
1465
  }
1279
-
1280
- free_context_raw(arg);
1281
-
1282
- pthread_rwlock_unlock(&exit_lock);
1283
-
1284
1466
  return NULL;
1285
1467
  }
1286
1468
 
1287
1469
  // destroys everything except freeing the ContextInfo struct (see deallocate())
1288
1470
  static void free_context(ContextInfo* context_info) {
1289
-
1290
1471
  IsolateInfo* isolate_info = context_info->isolate_info;
1291
1472
 
1292
- ContextInfo* context_info_copy = ALLOC(ContextInfo);
1293
- context_info_copy->isolate_info = context_info->isolate_info;
1294
- context_info_copy->context = context_info->context;
1295
-
1296
1473
  if (isolate_info && isolate_info->refs() > 1) {
1297
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;
1298
1479
  if (pthread_create(&free_context_thread, thread_attr_p,
1299
1480
  free_context_thr, (void*)context_info_copy)) {
1300
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);
1301
1483
  }
1302
1484
  } else {
1303
- free_context_raw(context_info_copy);
1485
+ free_context_raw(context_info);
1304
1486
  }
1305
1487
 
1306
1488
  context_info->context = NULL;
@@ -1319,6 +1501,11 @@ static void mark_isolate(void* data) {
1319
1501
  isolate_info->mark();
1320
1502
  }
1321
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
+
1322
1509
  static void deallocate(void* data) {
1323
1510
  ContextInfo* context_info = (ContextInfo*)data;
1324
1511
 
@@ -1327,6 +1514,11 @@ static void deallocate(void* data) {
1327
1514
  xfree(data);
1328
1515
  }
1329
1516
 
1517
+ static size_t context_memsize(const void *ptr)
1518
+ {
1519
+ return sizeof(ContextInfo);
1520
+ }
1521
+
1330
1522
  static void mark_context(void* data) {
1331
1523
  ContextInfo* context_info = (ContextInfo*)data;
1332
1524
  if (context_info->isolate_info) {
@@ -1334,48 +1526,39 @@ static void mark_context(void* data) {
1334
1526
  }
1335
1527
  }
1336
1528
 
1337
- static void deallocate_external_function(void * data) {
1338
- xfree(data);
1339
- }
1340
-
1341
1529
  static void deallocate_snapshot(void * data) {
1342
1530
  SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
1343
1531
  delete[] snapshot_info->data;
1344
1532
  xfree(snapshot_info);
1345
1533
  }
1346
1534
 
1347
- static VALUE allocate_external_function(VALUE klass) {
1348
- VALUE* self = ALLOC(VALUE);
1349
- 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;
1350
1538
  }
1351
1539
 
1352
1540
  static VALUE allocate(VALUE klass) {
1353
- ContextInfo* context_info = ALLOC(ContextInfo);
1354
- context_info->isolate_info = NULL;
1355
- context_info->context = NULL;
1356
-
1357
- 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);
1358
1543
  }
1359
1544
 
1360
1545
  static VALUE allocate_snapshot(VALUE klass) {
1361
- SnapshotInfo* snapshot_info = ALLOC(SnapshotInfo);
1362
- snapshot_info->data = NULL;
1363
- snapshot_info->raw_size = 0;
1546
+ SnapshotInfo* snapshot_info;
1364
1547
 
1365
- return Data_Wrap_Struct(klass, NULL, deallocate_snapshot, (void*)snapshot_info);
1548
+ return TypedData_Make_Struct(klass, SnapshotInfo, &snapshot_type, snapshot_info);
1366
1549
  }
1367
1550
 
1368
1551
  static VALUE allocate_isolate(VALUE klass) {
1369
1552
  IsolateInfo* isolate_info = new IsolateInfo();
1370
1553
 
1371
- return Data_Wrap_Struct(klass, mark_isolate, deallocate_isolate, (void*)isolate_info);
1554
+ return TypedData_Wrap_Struct(klass, &isolate_type, isolate_info);
1372
1555
  }
1373
1556
 
1374
1557
  static VALUE
1375
1558
  rb_heap_stats(VALUE self) {
1376
1559
 
1377
1560
  ContextInfo* context_info;
1378
- Data_Get_Struct(self, ContextInfo, context_info);
1561
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1379
1562
  Isolate* isolate;
1380
1563
  v8::HeapStatistics stats;
1381
1564
 
@@ -1407,7 +1590,9 @@ rb_heap_stats(VALUE self) {
1407
1590
  // https://github.com/bnoordhuis/node-heapdump/blob/master/src/heapdump.cc
1408
1591
  class FileOutputStream : public OutputStream {
1409
1592
  public:
1410
- FileOutputStream(FILE* stream) : stream_(stream) {}
1593
+ int err;
1594
+
1595
+ FileOutputStream(int fd) : fd(fd) { err = 0; }
1411
1596
 
1412
1597
  virtual int GetChunkSize() {
1413
1598
  return 65536;
@@ -1416,17 +1601,27 @@ class FileOutputStream : public OutputStream {
1416
1601
  virtual void EndOfStream() {}
1417
1602
 
1418
1603
  virtual WriteResult WriteAsciiChunk(char* data, int size) {
1419
- const size_t len = static_cast<size_t>(size);
1420
- size_t off = 0;
1421
-
1422
- while (off < len && !feof(stream_) && !ferror(stream_))
1423
- off += fwrite(data + off, 1, len - off, stream_);
1424
-
1425
- 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;
1426
1621
  }
1427
1622
 
1428
1623
  private:
1429
- FILE* stream_;
1624
+ int fd;
1430
1625
  };
1431
1626
 
1432
1627
 
@@ -1439,13 +1634,11 @@ rb_heap_snapshot(VALUE self, VALUE file) {
1439
1634
 
1440
1635
  if (!fptr) return Qfalse;
1441
1636
 
1442
- FILE* fp;
1443
- fp = fdopen(fptr->fd, "w");
1444
- if (fp == NULL) return Qfalse;
1445
-
1637
+ // prepare for unbuffered write(2) below:
1638
+ rb_funcall(file, rb_intern("flush"), 0);
1446
1639
 
1447
1640
  ContextInfo* context_info;
1448
- Data_Get_Struct(self, ContextInfo, context_info);
1641
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1449
1642
  Isolate* isolate;
1450
1643
  isolate = context_info->isolate_info ? context_info->isolate_info->isolate : NULL;
1451
1644
 
@@ -1459,13 +1652,14 @@ rb_heap_snapshot(VALUE self, VALUE file) {
1459
1652
 
1460
1653
  const HeapSnapshot* const snap = heap_profiler->TakeHeapSnapshot();
1461
1654
 
1462
- FileOutputStream stream(fp);
1655
+ FileOutputStream stream(fptr->fd);
1463
1656
  snap->Serialize(&stream, HeapSnapshot::kJSON);
1464
1657
 
1465
- fflush(fp);
1466
-
1467
1658
  const_cast<HeapSnapshot*>(snap)->Delete();
1468
1659
 
1660
+ /* TODO: perhaps rb_sys_fail here */
1661
+ if (stream.err) return Qfalse;
1662
+
1469
1663
  return Qtrue;
1470
1664
  }
1471
1665
 
@@ -1473,12 +1667,12 @@ static VALUE
1473
1667
  rb_context_stop(VALUE self) {
1474
1668
 
1475
1669
  ContextInfo* context_info;
1476
- Data_Get_Struct(self, ContextInfo, context_info);
1670
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1477
1671
 
1478
1672
  Isolate* isolate = context_info->isolate_info->isolate;
1479
1673
 
1480
1674
  // flag for termination
1481
- isolate->SetData(DO_TERMINATE, (void*)true);
1675
+ IsolateData::Set(isolate, IsolateData::DO_TERMINATE, true);
1482
1676
 
1483
1677
  isolate->TerminateExecution();
1484
1678
  rb_funcall(self, rb_intern("stop_attached"), 0);
@@ -1490,7 +1684,7 @@ static VALUE
1490
1684
  rb_context_dispose(VALUE self) {
1491
1685
 
1492
1686
  ContextInfo* context_info;
1493
- Data_Get_Struct(self, ContextInfo, context_info);
1687
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1494
1688
 
1495
1689
  free_context(context_info);
1496
1690
 
@@ -1507,18 +1701,20 @@ nogvl_context_call(void *args) {
1507
1701
  IsolateInfo *isolate_info = call->context_info->isolate_info;
1508
1702
  Isolate* isolate = isolate_info->isolate;
1509
1703
 
1510
- // in gvl flag
1511
- isolate->SetData(IN_GVL, (void*)false);
1512
- // terminate ASAP
1513
- isolate->SetData(DO_TERMINATE, (void*)false);
1704
+ IsolateData::Set(isolate, IsolateData::IN_GVL, false);
1705
+ IsolateData::Set(isolate, IsolateData::DO_TERMINATE, false);
1514
1706
 
1515
1707
  if (call->max_memory > 0) {
1516
- isolate->SetData(MEM_SOFTLIMIT_VALUE, &call->max_memory);
1517
- 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);
1518
1710
  if (!isolate_info->added_gc_cb) {
1519
- isolate->AddGCEpilogueCallback(gc_callback);
1711
+ isolate->AddGCEpilogueCallback(gc_callback);
1520
1712
  isolate_info->added_gc_cb = true;
1713
+ }
1521
1714
  }
1715
+
1716
+ if (call->marshal_stackdepth > 0) {
1717
+ StackCounter::SetMax(isolate, call->marshal_stackdepth);
1522
1718
  }
1523
1719
 
1524
1720
  Isolate::Scope isolate_scope(isolate);
@@ -1536,7 +1732,7 @@ nogvl_context_call(void *args) {
1536
1732
  MaybeLocal<v8::Value> res = fun->Call(context, context->Global(), call->argc, call->argv);
1537
1733
  prepare_result(res, trycatch, isolate, context, eval_res);
1538
1734
 
1539
- isolate->SetData(IN_GVL, (void*)true);
1735
+ IsolateData::Set(isolate, IsolateData::IN_GVL, true);
1540
1736
 
1541
1737
  return NULL;
1542
1738
  }
@@ -1551,7 +1747,7 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1551
1747
  FunctionCall call;
1552
1748
  VALUE *call_argv = NULL;
1553
1749
 
1554
- Data_Get_Struct(self, ContextInfo, context_info);
1750
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1555
1751
  Isolate* isolate = context_info->isolate_info->isolate;
1556
1752
 
1557
1753
  if (argc < 1) {
@@ -1559,9 +1755,7 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1559
1755
  }
1560
1756
 
1561
1757
  VALUE function_name = argv[0];
1562
- if (TYPE(function_name) != T_STRING) {
1563
- rb_raise(rb_eTypeError, "first argument should be a String");
1564
- }
1758
+ Check_Type(function_name, T_STRING);
1565
1759
 
1566
1760
  char *fname = RSTRING_PTR(function_name);
1567
1761
  if (!fname) {
@@ -1584,6 +1778,13 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1584
1778
  unsigned long sl_int = NUM2ULONG(mem_softlimit);
1585
1779
  call.max_memory = (size_t)sl_int;
1586
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
+ }
1587
1788
 
1588
1789
  bool missingFunction = false;
1589
1790
  {
@@ -1605,23 +1806,15 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1605
1806
  if (val.IsEmpty() || !val.ToLocalChecked()->IsFunction()) {
1606
1807
  missingFunction = true;
1607
1808
  } else {
1608
-
1609
1809
  Local<v8::Function> fun = Local<v8::Function>::Cast(val.ToLocalChecked());
1810
+ VALUE tmp;
1610
1811
  call.fun = fun;
1611
- int fun_argc = call.argc;
1612
-
1613
- if (fun_argc > 0) {
1614
- call.argv = (v8::Local<Value> *) malloc(sizeof(void *) * fun_argc);
1615
- if (!call.argv) {
1616
- return Qnil;
1617
- }
1618
- for(int i=0; i < fun_argc; i++) {
1619
- call.argv[i] = convert_ruby_to_v8(isolate, context, call_argv[i]);
1620
- }
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]);
1621
1815
  }
1622
1816
  rb_thread_call_without_gvl(nogvl_context_call, &call, unblock_function, &call);
1623
- free(call.argv);
1624
-
1817
+ RB_ALLOCV_END(tmp);
1625
1818
  }
1626
1819
  }
1627
1820
 
@@ -1634,7 +1827,7 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1634
1827
 
1635
1828
  static VALUE rb_context_create_isolate_value(VALUE self) {
1636
1829
  ContextInfo* context_info;
1637
- Data_Get_Struct(self, ContextInfo, context_info);
1830
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1638
1831
  IsolateInfo *isolate_info = context_info->isolate_info;
1639
1832
 
1640
1833
  if (!isolate_info) {
@@ -1642,18 +1835,13 @@ static VALUE rb_context_create_isolate_value(VALUE self) {
1642
1835
  }
1643
1836
 
1644
1837
  isolate_info->hold();
1645
- return Data_Wrap_Struct(rb_cIsolate, NULL, &deallocate_isolate, isolate_info);
1838
+ return TypedData_Wrap_Struct(rb_cIsolate, &isolate_type, isolate_info);
1646
1839
  }
1647
1840
 
1648
1841
  static void set_ruby_exiting(VALUE value) {
1649
1842
  (void)value;
1650
1843
 
1651
- int res = pthread_rwlock_wrlock(&exit_lock);
1652
-
1653
- ruby_exiting = true;
1654
- if (res == 0) {
1655
- pthread_rwlock_unlock(&exit_lock);
1656
- }
1844
+ ruby_exiting.store(1);
1657
1845
  }
1658
1846
 
1659
1847
  extern "C" {
@@ -1698,7 +1886,6 @@ extern "C" {
1698
1886
  rb_define_alloc_func(rb_cIsolate, allocate_isolate);
1699
1887
 
1700
1888
  rb_define_private_method(rb_cExternalFunction, "notify_v8", (VALUE(*)(...))&rb_external_function_notify_v8, 0);
1701
- rb_define_alloc_func(rb_cExternalFunction, allocate_external_function);
1702
1889
 
1703
1890
  rb_define_method(rb_cSnapshot, "size", (VALUE(*)(...))&rb_snapshot_size, 0);
1704
1891
  rb_define_method(rb_cSnapshot, "dump", (VALUE(*)(...))&rb_snapshot_dump, 0);
@@ -1720,9 +1907,5 @@ extern "C" {
1720
1907
  thread_attr_p = &attr;
1721
1908
  }
1722
1909
  }
1723
- auto on_fork_for_child = []() {
1724
- exit_lock = PTHREAD_RWLOCK_INITIALIZER;
1725
- };
1726
- pthread_atfork(nullptr, nullptr, on_fork_for_child);
1727
1910
  }
1728
1911
  }