mini_racer 0.4.0 → 0.6.3

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,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
  }