mini_racer 0.4.0 → 0.6.2

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;
@@ -159,6 +309,29 @@ static pthread_rwlock_t exit_lock = PTHREAD_RWLOCK_INITIALIZER;
159
309
  static bool ruby_exiting = false; // guarded by exit_lock
160
310
  static bool single_threaded = false;
161
311
 
312
+ static void mark_context(void *);
313
+ static void deallocate(void *);
314
+ static size_t context_memsize(const void *);
315
+ static const rb_data_type_t context_type = {
316
+ "mini_racer/context_info",
317
+ { mark_context, deallocate, context_memsize }
318
+ };
319
+
320
+ static void deallocate_snapshot(void *);
321
+ static size_t snapshot_memsize(const void *);
322
+ static const rb_data_type_t snapshot_type = {
323
+ "mini_racer/snapshot_info",
324
+ { NULL, deallocate_snapshot, snapshot_memsize }
325
+ };
326
+
327
+ static void mark_isolate(void *);
328
+ static void deallocate_isolate(void *);
329
+ static size_t isolate_memsize(const void *);
330
+ static const rb_data_type_t isolate_type = {
331
+ "mini_racer/isolate_info",
332
+ { mark_isolate, deallocate_isolate, isolate_memsize }
333
+ };
334
+
162
335
  static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
163
336
  bool platform_already_initialized = false;
164
337
 
@@ -173,7 +346,7 @@ static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
173
346
  if (!strcmp(RSTRING_PTR(flag_as_str), "--single_threaded")) {
174
347
  single_threaded = true;
175
348
  }
176
- V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), (int)RSTRING_LEN(flag_as_str));
349
+ V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), RSTRING_LENINT(flag_as_str));
177
350
  } else {
178
351
  platform_already_initialized = true;
179
352
  }
@@ -196,7 +369,11 @@ static void init_v8() {
196
369
 
197
370
  if (current_platform == NULL) {
198
371
  V8::InitializeICU();
199
- current_platform = platform::NewDefaultPlatform();
372
+ if (single_threaded) {
373
+ current_platform = platform::NewSingleThreadedDefaultPlatform();
374
+ } else {
375
+ current_platform = platform::NewDefaultPlatform();
376
+ }
200
377
  V8::InitializePlatform(current_platform.get());
201
378
  V8::Initialize();
202
379
  }
@@ -205,16 +382,18 @@ static void init_v8() {
205
382
  }
206
383
 
207
384
  static void gc_callback(Isolate *isolate, GCType type, GCCallbackFlags flags) {
208
- if((bool)isolate->GetData(MEM_SOFTLIMIT_REACHED)) return;
385
+ if (IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_REACHED)) {
386
+ return;
387
+ }
209
388
 
210
- size_t softlimit = *(size_t*) isolate->GetData(MEM_SOFTLIMIT_VALUE);
389
+ size_t softlimit = IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_MAX);
211
390
 
212
391
  HeapStatistics stats;
213
392
  isolate->GetHeapStatistics(&stats);
214
393
  size_t used = stats.used_heap_size();
215
394
 
216
395
  if(used > softlimit) {
217
- isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)true);
396
+ IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_REACHED, true);
218
397
  isolate->TerminateExecution();
219
398
  }
220
399
  }
@@ -239,28 +418,32 @@ static void prepare_result(MaybeLocal<Value> v8res,
239
418
  Local<Value> local_value = v8res.ToLocalChecked();
240
419
  if ((local_value->IsObject() || local_value->IsArray()) &&
241
420
  !local_value->IsDate() && !local_value->IsFunction()) {
242
- Local<Object> JSON = context->Global()->Get(
243
- context, String::NewFromUtf8Literal(isolate, "JSON"))
244
- .ToLocalChecked().As<Object>();
421
+ MaybeLocal<v8::Value> ml = context->Global()->Get(
422
+ context, String::NewFromUtf8Literal(isolate, "JSON"));
245
423
 
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()) {
424
+ if (ml.IsEmpty()) { // exception
256
425
  evalRes.executed = false;
257
426
  } else {
258
- evalRes.json = true;
259
- Persistent<Value>* persistent = new Persistent<Value>();
260
- persistent->Reset(isolate, json.ToLocalChecked());
261
- evalRes.value = persistent;
427
+ Local<Object> JSON = ml.ToLocalChecked().As<Object>();
428
+
429
+ Local<Function> stringify = JSON->Get(
430
+ context, v8::String::NewFromUtf8Literal(isolate, "stringify"))
431
+ .ToLocalChecked().As<Function>();
432
+
433
+ Local<Object> object = local_value->ToObject(context).ToLocalChecked();
434
+ const unsigned argc = 1;
435
+ Local<Value> argv[argc] = { object };
436
+ MaybeLocal<Value> json = stringify->Call(context, JSON, argc, argv);
437
+
438
+ if (json.IsEmpty()) {
439
+ evalRes.executed = false;
440
+ } else {
441
+ evalRes.json = true;
442
+ Persistent<Value>* persistent = new Persistent<Value>();
443
+ persistent->Reset(isolate, json.ToLocalChecked());
444
+ evalRes.value = persistent;
445
+ }
262
446
  }
263
-
264
447
  } else {
265
448
  Persistent<Value>* persistent = new Persistent<Value>();
266
449
  persistent->Reset(isolate, local_value);
@@ -311,7 +494,7 @@ static void prepare_result(MaybeLocal<Value> v8res,
311
494
  }
312
495
  }
313
496
 
314
- void*
497
+ static void*
315
498
  nogvl_context_eval(void* arg) {
316
499
 
317
500
  EvalParams* eval_params = (EvalParams*)arg;
@@ -326,14 +509,15 @@ nogvl_context_eval(void* arg) {
326
509
  Context::Scope context_scope(context);
327
510
  v8::ScriptOrigin *origin = NULL;
328
511
 
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);
512
+ IsolateData::Init(isolate);
513
+
514
+ if (eval_params->max_memory > 0) {
515
+ IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_MAX, eval_params->max_memory);
516
+ if (!isolate_info->added_gc_cb) {
517
+ isolate->AddGCEpilogueCallback(gc_callback);
518
+ isolate_info->added_gc_cb = true;
519
+ }
520
+ }
337
521
 
338
522
  MaybeLocal<Script> parsed_script;
339
523
 
@@ -359,12 +543,8 @@ nogvl_context_eval(void* arg) {
359
543
  result->message->Reset(isolate, trycatch.Exception());
360
544
  } else {
361
545
  // 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
- }
546
+ if (eval_params->marshal_stackdepth > 0) {
547
+ StackCounter::SetMax(isolate, eval_params->marshal_stackdepth);
368
548
  }
369
549
 
370
550
  maybe_value = parsed_script.ToLocalChecked()->Run(context);
@@ -372,7 +552,7 @@ nogvl_context_eval(void* arg) {
372
552
 
373
553
  prepare_result(maybe_value, trycatch, isolate, context, *result);
374
554
 
375
- isolate->SetData(IN_GVL, (void*)true);
555
+ IsolateData::Set(isolate, IsolateData::IN_GVL, true);
376
556
 
377
557
  return NULL;
378
558
  }
@@ -390,6 +570,18 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
390
570
  Isolate::Scope isolate_scope(isolate);
391
571
  HandleScope scope(isolate);
392
572
 
573
+ StackCounter stackCounter(isolate);
574
+
575
+ if (IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED)) {
576
+ return Qnil;
577
+ }
578
+
579
+ if (stackCounter.IsTooDeep()) {
580
+ IsolateData::Set(isolate, IsolateData::DO_TERMINATE, true);
581
+ isolate->TerminateExecution();
582
+ return Qnil;
583
+ }
584
+
393
585
  if (value->IsNull() || value->IsUndefined()){
394
586
  return Qnil;
395
587
  }
@@ -478,7 +670,7 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
478
670
  rb_enc_find("utf-8")
479
671
  );
480
672
 
481
- return ID2SYM(rb_intern_str(str_symbol));
673
+ return rb_str_intern(str_symbol);
482
674
  }
483
675
 
484
676
  MaybeLocal<String> rstr_maybe = value->ToString(context);
@@ -532,7 +724,7 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
532
724
  case T_FLOAT:
533
725
  return scope.Escape(Number::New(isolate, NUM2DBL(value)));
534
726
  case T_STRING:
535
- return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, (int)RSTRING_LEN(value)).ToLocalChecked());
727
+ return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, RSTRING_LENINT(value)).ToLocalChecked());
536
728
  case T_NIL:
537
729
  return scope.Escape(Null(isolate));
538
730
  case T_TRUE:
@@ -543,7 +735,8 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
543
735
  length = RARRAY_LEN(value);
544
736
  array = Array::New(isolate, (int)length);
545
737
  for(i=0; i<length; i++) {
546
- array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
738
+ Maybe<bool> success = array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
739
+ (void)(success);
547
740
  }
548
741
  return scope.Escape(array);
549
742
  case T_HASH:
@@ -552,13 +745,14 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
552
745
  length = RARRAY_LEN(hash_as_array);
553
746
  for(i=0; i<length; i++) {
554
747
  pair = rb_ary_entry(hash_as_array, i);
555
- object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
748
+ Maybe<bool> success = object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
556
749
  convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 1)));
750
+ (void)(success);
557
751
  }
558
752
  return scope.Escape(object);
559
753
  case T_SYMBOL:
560
754
  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());
755
+ return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, RSTRING_LENINT(value)).ToLocalChecked());
562
756
  case T_DATA:
563
757
  klass = rb_funcall(value, rb_intern("class"), 0);
564
758
  if (klass == rb_cTime || klass == rb_cDateTime)
@@ -633,6 +827,7 @@ create_snapshot_data_blob(const char *embedded_source = nullptr) {
633
827
  SnapshotCreator::FunctionCodeHandling::kClear);
634
828
  }
635
829
 
830
+ static
636
831
  StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
637
832
  const char *warmup_source) {
638
833
  // Use following steps to create a warmed up snapshot blob from a cold one:
@@ -668,14 +863,14 @@ StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
668
863
 
669
864
  static VALUE rb_snapshot_size(VALUE self, VALUE str) {
670
865
  SnapshotInfo* snapshot_info;
671
- Data_Get_Struct(self, SnapshotInfo, snapshot_info);
866
+ TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
672
867
 
673
868
  return INT2NUM(snapshot_info->raw_size);
674
869
  }
675
870
 
676
871
  static VALUE rb_snapshot_load(VALUE self, VALUE str) {
677
872
  SnapshotInfo* snapshot_info;
678
- Data_Get_Struct(self, SnapshotInfo, snapshot_info);
873
+ TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
679
874
 
680
875
  if(TYPE(str) != T_STRING) {
681
876
  rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
@@ -698,14 +893,14 @@ static VALUE rb_snapshot_load(VALUE self, VALUE str) {
698
893
 
699
894
  static VALUE rb_snapshot_dump(VALUE self, VALUE str) {
700
895
  SnapshotInfo* snapshot_info;
701
- Data_Get_Struct(self, SnapshotInfo, snapshot_info);
896
+ TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
702
897
 
703
898
  return rb_str_new(snapshot_info->data, snapshot_info->raw_size);
704
899
  }
705
900
 
706
901
  static VALUE rb_snapshot_warmup_unsafe(VALUE self, VALUE str) {
707
902
  SnapshotInfo* snapshot_info;
708
- Data_Get_Struct(self, SnapshotInfo, snapshot_info);
903
+ TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
709
904
 
710
905
  if(TYPE(str) != T_STRING) {
711
906
  rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
@@ -752,13 +947,13 @@ void IsolateInfo::init(SnapshotInfo* snapshot_info) {
752
947
 
753
948
  static VALUE rb_isolate_init_with_snapshot(VALUE self, VALUE snapshot) {
754
949
  IsolateInfo* isolate_info;
755
- Data_Get_Struct(self, IsolateInfo, isolate_info);
950
+ TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
756
951
 
757
952
  init_v8();
758
953
 
759
954
  SnapshotInfo* snapshot_info = nullptr;
760
955
  if (!NIL_P(snapshot)) {
761
- Data_Get_Struct(snapshot, SnapshotInfo, snapshot_info);
956
+ TypedData_Get_Struct(snapshot, SnapshotInfo, &snapshot_type, snapshot_info);
762
957
  }
763
958
 
764
959
  isolate_info->init(snapshot_info);
@@ -769,7 +964,7 @@ static VALUE rb_isolate_init_with_snapshot(VALUE self, VALUE snapshot) {
769
964
 
770
965
  static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
771
966
  IsolateInfo* isolate_info;
772
- Data_Get_Struct(self, IsolateInfo, isolate_info);
967
+ TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
773
968
 
774
969
  if (current_platform == NULL) return Qfalse;
775
970
 
@@ -780,7 +975,7 @@ static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
780
975
 
781
976
  static VALUE rb_isolate_low_memory_notification(VALUE self) {
782
977
  IsolateInfo* isolate_info;
783
- Data_Get_Struct(self, IsolateInfo, isolate_info);
978
+ TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
784
979
 
785
980
  if (current_platform == NULL) return Qfalse;
786
981
 
@@ -790,7 +985,7 @@ static VALUE rb_isolate_low_memory_notification(VALUE self) {
790
985
 
791
986
  static VALUE rb_isolate_pump_message_loop(VALUE self) {
792
987
  IsolateInfo* isolate_info;
793
- Data_Get_Struct(self, IsolateInfo, isolate_info);
988
+ TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
794
989
 
795
990
  if (current_platform == NULL) return Qfalse;
796
991
 
@@ -803,7 +998,7 @@ static VALUE rb_isolate_pump_message_loop(VALUE self) {
803
998
 
804
999
  static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
805
1000
  ContextInfo* context_info;
806
- Data_Get_Struct(self, ContextInfo, context_info);
1001
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
807
1002
 
808
1003
  init_v8();
809
1004
 
@@ -814,11 +1009,11 @@ static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
814
1009
 
815
1010
  SnapshotInfo *snapshot_info = nullptr;
816
1011
  if (!NIL_P(snap) && rb_obj_is_kind_of(snap, rb_cSnapshot)) {
817
- Data_Get_Struct(snap, SnapshotInfo, snapshot_info);
1012
+ TypedData_Get_Struct(snap, SnapshotInfo, &snapshot_type, snapshot_info);
818
1013
  }
819
1014
  isolate_info->init(snapshot_info);
820
1015
  } else { // given isolate or snapshot
821
- Data_Get_Struct(isolate, IsolateInfo, isolate_info);
1016
+ TypedData_Get_Struct(isolate, IsolateInfo, &isolate_type, isolate_info);
822
1017
  }
823
1018
 
824
1019
  context_info->isolate_info = isolate_info;
@@ -848,7 +1043,7 @@ static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
848
1043
  static VALUE convert_result_to_ruby(VALUE self /* context */,
849
1044
  EvalResult& result) {
850
1045
  ContextInfo *context_info;
851
- Data_Get_Struct(self, ContextInfo, context_info);
1046
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
852
1047
 
853
1048
  Isolate *isolate = context_info->isolate_info->isolate;
854
1049
  Persistent<Context> *p_ctx = context_info->context;
@@ -875,7 +1070,7 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
875
1070
  // a v8 scope, if we do the scope is never cleaned up properly and we leak
876
1071
  if (!result.parsed) {
877
1072
  if(TYPE(message) == T_STRING) {
878
- rb_raise(rb_eParseError, "%s", RSTRING_PTR(message));
1073
+ rb_raise(rb_eParseError, "%" PRIsVALUE, message);
879
1074
  } else {
880
1075
  rb_raise(rb_eParseError, "Unknown JavaScript Error during parse");
881
1076
  }
@@ -884,9 +1079,14 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
884
1079
  if (!result.executed) {
885
1080
  VALUE ruby_exception = rb_iv_get(self, "@current_exception");
886
1081
  if (ruby_exception == Qnil) {
887
- bool mem_softlimit_reached = (bool)isolate->GetData(MEM_SOFTLIMIT_REACHED);
1082
+ bool mem_softlimit_reached = IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_REACHED);
1083
+ bool marshal_stack_maxdepth_reached = IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED);
888
1084
  // If we were terminated or have the memory softlimit flag set
889
- if (result.terminated || mem_softlimit_reached) {
1085
+ if (marshal_stack_maxdepth_reached) {
1086
+ ruby_exception = rb_eScriptRuntimeError;
1087
+ std::string msg = std::string("Marshal object depth too deep. Script terminated.");
1088
+ message = rb_enc_str_new(msg.c_str(), msg.length(), rb_enc_find("utf-8"));
1089
+ } else if (result.terminated || mem_softlimit_reached) {
890
1090
  ruby_exception = mem_softlimit_reached ? rb_eV8OutOfMemoryError : rb_eScriptTerminatedError;
891
1091
  } else {
892
1092
  ruby_exception = rb_eScriptRuntimeError;
@@ -894,15 +1094,17 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
894
1094
 
895
1095
  // exception report about what happened
896
1096
  if (TYPE(backtrace) == T_STRING) {
897
- rb_raise(ruby_exception, "%s", RSTRING_PTR(backtrace));
1097
+ rb_raise(ruby_exception, "%" PRIsVALUE, backtrace);
898
1098
  } else if(TYPE(message) == T_STRING) {
899
- rb_raise(ruby_exception, "%s", RSTRING_PTR(message));
1099
+ rb_raise(ruby_exception, "%" PRIsVALUE, message);
900
1100
  } else {
901
1101
  rb_raise(ruby_exception, "Unknown JavaScript Error during execution");
902
1102
  }
1103
+ } else if (rb_obj_is_kind_of(ruby_exception, rb_eException)) {
1104
+ rb_exc_raise(ruby_exception);
903
1105
  } else {
904
1106
  VALUE rb_str = rb_funcall(ruby_exception, rb_intern("to_s"), 0);
905
- rb_raise(CLASS_OF(ruby_exception), "%s", RSTRING_PTR(rb_str));
1107
+ rb_raise(CLASS_OF(ruby_exception), "%" PRIsVALUE, rb_str);
906
1108
  }
907
1109
  }
908
1110
 
@@ -921,6 +1123,7 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
921
1123
  VALUE json_string = rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
922
1124
  ret = rb_funcall(rb_mJSON, rb_intern("parse"), 1, json_string);
923
1125
  } else {
1126
+ StackCounter::Reset(isolate);
924
1127
  ret = convert_v8_to_ruby(isolate, *p_ctx, tmp);
925
1128
  }
926
1129
 
@@ -943,7 +1146,7 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
943
1146
  EvalResult eval_result;
944
1147
  ContextInfo* context_info;
945
1148
 
946
- Data_Get_Struct(self, ContextInfo, context_info);
1149
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
947
1150
  Isolate* isolate = context_info->isolate_info->isolate;
948
1151
 
949
1152
  if(TYPE(str) != T_STRING) {
@@ -961,13 +1164,13 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
961
1164
  HandleScope handle_scope(isolate);
962
1165
 
963
1166
  Local<String> eval = String::NewFromUtf8(isolate, RSTRING_PTR(str),
964
- NewStringType::kNormal, (int)RSTRING_LEN(str)).ToLocalChecked();
1167
+ NewStringType::kNormal, RSTRING_LENINT(str)).ToLocalChecked();
965
1168
 
966
1169
  Local<String> local_filename;
967
1170
 
968
1171
  if (filename != Qnil) {
969
1172
  local_filename = String::NewFromUtf8(isolate, RSTRING_PTR(filename),
970
- NewStringType::kNormal, (int)RSTRING_LEN(filename)).ToLocalChecked();
1173
+ NewStringType::kNormal, RSTRING_LENINT(filename)).ToLocalChecked();
971
1174
  eval_params.filename = &local_filename;
972
1175
  } else {
973
1176
  eval_params.filename = NULL;
@@ -978,6 +1181,7 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
978
1181
  eval_params.result = &eval_result;
979
1182
  eval_params.timeout = 0;
980
1183
  eval_params.max_memory = 0;
1184
+ eval_params.marshal_stackdepth = 0;
981
1185
  VALUE timeout = rb_iv_get(self, "@timeout");
982
1186
  if (timeout != Qnil) {
983
1187
  eval_params.timeout = (useconds_t)NUM2LONG(timeout);
@@ -988,6 +1192,11 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
988
1192
  eval_params.max_memory = (size_t)NUM2ULONG(mem_softlimit);
989
1193
  }
990
1194
 
1195
+ VALUE stack_depth = rb_iv_get(self, "@marshal_stack_depth");
1196
+ if (stack_depth != Qnil) {
1197
+ eval_params.marshal_stackdepth = (size_t)NUM2ULONG(stack_depth);
1198
+ }
1199
+
991
1200
  eval_result.message = NULL;
992
1201
  eval_result.backtrace = NULL;
993
1202
 
@@ -1025,7 +1234,7 @@ VALUE rescue_callback(VALUE rdata, VALUE exception) {
1025
1234
  return exception;
1026
1235
  }
1027
1236
 
1028
- void*
1237
+ static void*
1029
1238
  gvl_ruby_callback(void* data) {
1030
1239
 
1031
1240
  FunctionCallbackInfo<Value>* args = (FunctionCallbackInfo<Value>*)data;
@@ -1041,7 +1250,7 @@ gvl_ruby_callback(void* data) {
1041
1250
  HandleScope scope(args->GetIsolate());
1042
1251
  Local<External> external = Local<External>::Cast(args->Data());
1043
1252
 
1044
- self = *(VALUE*)(external->Value());
1253
+ self = (VALUE)(external->Value());
1045
1254
  callback = rb_iv_get(self, "@callback");
1046
1255
 
1047
1256
  parent = rb_iv_get(self, "@parent");
@@ -1049,7 +1258,7 @@ gvl_ruby_callback(void* data) {
1049
1258
  return NULL;
1050
1259
  }
1051
1260
 
1052
- Data_Get_Struct(parent, ContextInfo, context_info);
1261
+ TypedData_Get_Struct(parent, ContextInfo, &context_type, context_info);
1053
1262
 
1054
1263
  if (length > 0) {
1055
1264
  ruby_args = rb_ary_tmp_new(length);
@@ -1057,6 +1266,7 @@ gvl_ruby_callback(void* data) {
1057
1266
 
1058
1267
  for (int i = 0; i < length; i++) {
1059
1268
  Local<Value> value = ((*args)[i]).As<Value>();
1269
+ StackCounter::Reset(args->GetIsolate());
1060
1270
  VALUE tmp = convert_v8_to_ruby(args->GetIsolate(),
1061
1271
  *context_info->context, value);
1062
1272
  rb_ary_push(ruby_args, tmp);
@@ -1070,20 +1280,22 @@ gvl_ruby_callback(void* data) {
1070
1280
  callback_data.ruby_args = ruby_args;
1071
1281
  callback_data.failed = false;
1072
1282
 
1073
- if ((bool)args->GetIsolate()->GetData(DO_TERMINATE) == true) {
1283
+ if (IsolateData::Get(args->GetIsolate(), IsolateData::DO_TERMINATE)) {
1074
1284
  args->GetIsolate()->ThrowException(
1075
1285
  String::NewFromUtf8Literal(args->GetIsolate(),
1076
1286
  "Terminated execution during transition from Ruby to JS"));
1077
1287
  args->GetIsolate()->TerminateExecution();
1078
1288
  if (length > 0) {
1079
1289
  rb_ary_clear(ruby_args);
1080
- rb_gc_force_recycle(ruby_args);
1081
1290
  }
1082
1291
  return NULL;
1083
1292
  }
1084
1293
 
1085
- result = rb_rescue2((VALUE(*)(...))&protected_callback, (VALUE)(&callback_data),
1086
- (VALUE(*)(...))&rescue_callback, (VALUE)(&callback_data), rb_eException, (VALUE)0);
1294
+ VALUE callback_data_value = (VALUE)&callback_data;
1295
+
1296
+ // TODO: use rb_vrescue2 in Ruby 2.7 and above
1297
+ result = rb_rescue2(MR_METHOD_FUNC(protected_callback), callback_data_value,
1298
+ MR_METHOD_FUNC(rescue_callback), callback_data_value, rb_eException, (VALUE)0);
1087
1299
 
1088
1300
  if(callback_data.failed) {
1089
1301
  rb_iv_set(parent, "@current_exception", result);
@@ -1097,26 +1309,24 @@ gvl_ruby_callback(void* data) {
1097
1309
 
1098
1310
  if (length > 0) {
1099
1311
  rb_ary_clear(ruby_args);
1100
- rb_gc_force_recycle(ruby_args);
1101
1312
  }
1102
1313
 
1103
- if ((bool)args->GetIsolate()->GetData(DO_TERMINATE) == true) {
1104
- Isolate* isolate = args->GetIsolate();
1105
- isolate->TerminateExecution();
1314
+ if (IsolateData::Get(args->GetIsolate(), IsolateData::DO_TERMINATE)) {
1315
+ args->GetIsolate()->TerminateExecution();
1106
1316
  }
1107
1317
 
1108
1318
  return NULL;
1109
1319
  }
1110
1320
 
1111
1321
  static void ruby_callback(const FunctionCallbackInfo<Value>& args) {
1112
- bool has_gvl = (bool)args.GetIsolate()->GetData(IN_GVL);
1322
+ bool has_gvl = IsolateData::Get(args.GetIsolate(), IsolateData::IN_GVL);
1113
1323
 
1114
1324
  if(has_gvl) {
1115
1325
  gvl_ruby_callback((void*)&args);
1116
1326
  } else {
1117
- args.GetIsolate()->SetData(IN_GVL, (void*)true);
1327
+ IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, true);
1118
1328
  rb_thread_call_with_gvl(gvl_ruby_callback, (void*)(&args));
1119
- args.GetIsolate()->SetData(IN_GVL, (void*)false);
1329
+ IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, false);
1120
1330
  }
1121
1331
  }
1122
1332
 
@@ -1133,7 +1343,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1133
1343
  bool parse_error = false;
1134
1344
  bool attach_error = false;
1135
1345
 
1136
- Data_Get_Struct(parent, ContextInfo, context_info);
1346
+ TypedData_Get_Struct(parent, ContextInfo, &context_type, context_info);
1137
1347
  Isolate* isolate = context_info->isolate_info->isolate;
1138
1348
 
1139
1349
  {
@@ -1146,29 +1356,27 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1146
1356
 
1147
1357
  Local<String> v8_str =
1148
1358
  String::NewFromUtf8(isolate, RSTRING_PTR(name),
1149
- NewStringType::kNormal, (int)RSTRING_LEN(name))
1359
+ NewStringType::kNormal, RSTRING_LENINT(name))
1150
1360
  .ToLocalChecked();
1151
1361
 
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);
1362
+ // Note that self (rb_cExternalFunction) is a pure Ruby T_OBJECT,
1363
+ // not a T_DATA type like most other classes in this file
1364
+ Local<Value> external = External::New(isolate, (void *)self);
1158
1365
 
1159
1366
  if (parent_object == Qnil) {
1160
- context->Global()->Set(
1367
+ Maybe<bool> success = context->Global()->Set(
1161
1368
  context,
1162
1369
  v8_str,
1163
1370
  FunctionTemplate::New(isolate, ruby_callback, external)
1164
1371
  ->GetFunction(context)
1165
1372
  .ToLocalChecked());
1373
+ (void)success;
1166
1374
 
1167
1375
  } else {
1168
1376
  Local<String> eval =
1169
1377
  String::NewFromUtf8(isolate, RSTRING_PTR(parent_object_eval),
1170
1378
  NewStringType::kNormal,
1171
- (int)RSTRING_LEN(parent_object_eval))
1379
+ RSTRING_LENINT(parent_object_eval))
1172
1380
  .ToLocalChecked();
1173
1381
 
1174
1382
  MaybeLocal<Script> parsed_script = Script::Compile(context, eval);
@@ -1182,12 +1390,13 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1182
1390
  if (!maybe_value.IsEmpty()) {
1183
1391
  Local<Value> value = maybe_value.ToLocalChecked();
1184
1392
  if (value->IsObject()) {
1185
- value.As<Object>()->Set(
1393
+ Maybe<bool> success = value.As<Object>()->Set(
1186
1394
  context,
1187
1395
  v8_str,
1188
1396
  FunctionTemplate::New(isolate, ruby_callback, external)
1189
1397
  ->GetFunction(context)
1190
1398
  .ToLocalChecked());
1399
+ (void)success;
1191
1400
  attach_error = false;
1192
1401
  }
1193
1402
  }
@@ -1209,7 +1418,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1209
1418
 
1210
1419
  static VALUE rb_context_isolate_mutex(VALUE self) {
1211
1420
  ContextInfo* context_info;
1212
- Data_Get_Struct(self, ContextInfo, context_info);
1421
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1213
1422
 
1214
1423
  if (!context_info->isolate_info) {
1215
1424
  rb_raise(rb_eScriptRuntimeError, "Context has no Isolate available anymore");
@@ -1319,6 +1528,11 @@ static void mark_isolate(void* data) {
1319
1528
  isolate_info->mark();
1320
1529
  }
1321
1530
 
1531
+ static size_t isolate_memsize(const void *ptr) {
1532
+ const IsolateInfo *isolate_info = (const IsolateInfo *)ptr;
1533
+ return sizeof(*isolate_info);
1534
+ }
1535
+
1322
1536
  static void deallocate(void* data) {
1323
1537
  ContextInfo* context_info = (ContextInfo*)data;
1324
1538
 
@@ -1327,6 +1541,11 @@ static void deallocate(void* data) {
1327
1541
  xfree(data);
1328
1542
  }
1329
1543
 
1544
+ static size_t context_memsize(const void *ptr)
1545
+ {
1546
+ return sizeof(ContextInfo);
1547
+ }
1548
+
1330
1549
  static void mark_context(void* data) {
1331
1550
  ContextInfo* context_info = (ContextInfo*)data;
1332
1551
  if (context_info->isolate_info) {
@@ -1334,48 +1553,39 @@ static void mark_context(void* data) {
1334
1553
  }
1335
1554
  }
1336
1555
 
1337
- static void deallocate_external_function(void * data) {
1338
- xfree(data);
1339
- }
1340
-
1341
1556
  static void deallocate_snapshot(void * data) {
1342
1557
  SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
1343
1558
  delete[] snapshot_info->data;
1344
1559
  xfree(snapshot_info);
1345
1560
  }
1346
1561
 
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);
1562
+ static size_t snapshot_memsize(const void *data) {
1563
+ SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
1564
+ return sizeof(*snapshot_info) + snapshot_info->raw_size;
1350
1565
  }
1351
1566
 
1352
1567
  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);
1568
+ ContextInfo* context_info;
1569
+ return TypedData_Make_Struct(klass, ContextInfo, &context_type, context_info);
1358
1570
  }
1359
1571
 
1360
1572
  static VALUE allocate_snapshot(VALUE klass) {
1361
- SnapshotInfo* snapshot_info = ALLOC(SnapshotInfo);
1362
- snapshot_info->data = NULL;
1363
- snapshot_info->raw_size = 0;
1573
+ SnapshotInfo* snapshot_info;
1364
1574
 
1365
- return Data_Wrap_Struct(klass, NULL, deallocate_snapshot, (void*)snapshot_info);
1575
+ return TypedData_Make_Struct(klass, SnapshotInfo, &snapshot_type, snapshot_info);
1366
1576
  }
1367
1577
 
1368
1578
  static VALUE allocate_isolate(VALUE klass) {
1369
1579
  IsolateInfo* isolate_info = new IsolateInfo();
1370
1580
 
1371
- return Data_Wrap_Struct(klass, mark_isolate, deallocate_isolate, (void*)isolate_info);
1581
+ return TypedData_Wrap_Struct(klass, &isolate_type, isolate_info);
1372
1582
  }
1373
1583
 
1374
1584
  static VALUE
1375
1585
  rb_heap_stats(VALUE self) {
1376
1586
 
1377
1587
  ContextInfo* context_info;
1378
- Data_Get_Struct(self, ContextInfo, context_info);
1588
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1379
1589
  Isolate* isolate;
1380
1590
  v8::HeapStatistics stats;
1381
1591
 
@@ -1407,7 +1617,9 @@ rb_heap_stats(VALUE self) {
1407
1617
  // https://github.com/bnoordhuis/node-heapdump/blob/master/src/heapdump.cc
1408
1618
  class FileOutputStream : public OutputStream {
1409
1619
  public:
1410
- FileOutputStream(FILE* stream) : stream_(stream) {}
1620
+ int err;
1621
+
1622
+ FileOutputStream(int fd) : fd(fd) { err = 0; }
1411
1623
 
1412
1624
  virtual int GetChunkSize() {
1413
1625
  return 65536;
@@ -1416,17 +1628,27 @@ class FileOutputStream : public OutputStream {
1416
1628
  virtual void EndOfStream() {}
1417
1629
 
1418
1630
  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;
1631
+ size_t len = static_cast<size_t>(size);
1632
+
1633
+ while (len) {
1634
+ ssize_t w = write(fd, data, len);
1635
+
1636
+ if (w > 0) {
1637
+ data += w;
1638
+ len -= w;
1639
+ } else if (w < 0) {
1640
+ err = errno;
1641
+ return kAbort;
1642
+ } else { /* w == 0, could be out-of-space */
1643
+ err = -1;
1644
+ return kAbort;
1645
+ }
1646
+ }
1647
+ return kContinue;
1426
1648
  }
1427
1649
 
1428
1650
  private:
1429
- FILE* stream_;
1651
+ int fd;
1430
1652
  };
1431
1653
 
1432
1654
 
@@ -1439,13 +1661,11 @@ rb_heap_snapshot(VALUE self, VALUE file) {
1439
1661
 
1440
1662
  if (!fptr) return Qfalse;
1441
1663
 
1442
- FILE* fp;
1443
- fp = fdopen(fptr->fd, "w");
1444
- if (fp == NULL) return Qfalse;
1445
-
1664
+ // prepare for unbuffered write(2) below:
1665
+ rb_funcall(file, rb_intern("flush"), 0);
1446
1666
 
1447
1667
  ContextInfo* context_info;
1448
- Data_Get_Struct(self, ContextInfo, context_info);
1668
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1449
1669
  Isolate* isolate;
1450
1670
  isolate = context_info->isolate_info ? context_info->isolate_info->isolate : NULL;
1451
1671
 
@@ -1459,13 +1679,14 @@ rb_heap_snapshot(VALUE self, VALUE file) {
1459
1679
 
1460
1680
  const HeapSnapshot* const snap = heap_profiler->TakeHeapSnapshot();
1461
1681
 
1462
- FileOutputStream stream(fp);
1682
+ FileOutputStream stream(fptr->fd);
1463
1683
  snap->Serialize(&stream, HeapSnapshot::kJSON);
1464
1684
 
1465
- fflush(fp);
1466
-
1467
1685
  const_cast<HeapSnapshot*>(snap)->Delete();
1468
1686
 
1687
+ /* TODO: perhaps rb_sys_fail here */
1688
+ if (stream.err) return Qfalse;
1689
+
1469
1690
  return Qtrue;
1470
1691
  }
1471
1692
 
@@ -1473,12 +1694,12 @@ static VALUE
1473
1694
  rb_context_stop(VALUE self) {
1474
1695
 
1475
1696
  ContextInfo* context_info;
1476
- Data_Get_Struct(self, ContextInfo, context_info);
1697
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1477
1698
 
1478
1699
  Isolate* isolate = context_info->isolate_info->isolate;
1479
1700
 
1480
1701
  // flag for termination
1481
- isolate->SetData(DO_TERMINATE, (void*)true);
1702
+ IsolateData::Set(isolate, IsolateData::DO_TERMINATE, true);
1482
1703
 
1483
1704
  isolate->TerminateExecution();
1484
1705
  rb_funcall(self, rb_intern("stop_attached"), 0);
@@ -1490,7 +1711,7 @@ static VALUE
1490
1711
  rb_context_dispose(VALUE self) {
1491
1712
 
1492
1713
  ContextInfo* context_info;
1493
- Data_Get_Struct(self, ContextInfo, context_info);
1714
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1494
1715
 
1495
1716
  free_context(context_info);
1496
1717
 
@@ -1507,18 +1728,20 @@ nogvl_context_call(void *args) {
1507
1728
  IsolateInfo *isolate_info = call->context_info->isolate_info;
1508
1729
  Isolate* isolate = isolate_info->isolate;
1509
1730
 
1510
- // in gvl flag
1511
- isolate->SetData(IN_GVL, (void*)false);
1512
- // terminate ASAP
1513
- isolate->SetData(DO_TERMINATE, (void*)false);
1731
+ IsolateData::Set(isolate, IsolateData::IN_GVL, false);
1732
+ IsolateData::Set(isolate, IsolateData::DO_TERMINATE, false);
1514
1733
 
1515
1734
  if (call->max_memory > 0) {
1516
- isolate->SetData(MEM_SOFTLIMIT_VALUE, &call->max_memory);
1517
- isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)false);
1735
+ IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_MAX, call->max_memory);
1736
+ IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_REACHED, false);
1518
1737
  if (!isolate_info->added_gc_cb) {
1519
- isolate->AddGCEpilogueCallback(gc_callback);
1738
+ isolate->AddGCEpilogueCallback(gc_callback);
1520
1739
  isolate_info->added_gc_cb = true;
1740
+ }
1521
1741
  }
1742
+
1743
+ if (call->marshal_stackdepth > 0) {
1744
+ StackCounter::SetMax(isolate, call->marshal_stackdepth);
1522
1745
  }
1523
1746
 
1524
1747
  Isolate::Scope isolate_scope(isolate);
@@ -1536,7 +1759,7 @@ nogvl_context_call(void *args) {
1536
1759
  MaybeLocal<v8::Value> res = fun->Call(context, context->Global(), call->argc, call->argv);
1537
1760
  prepare_result(res, trycatch, isolate, context, eval_res);
1538
1761
 
1539
- isolate->SetData(IN_GVL, (void*)true);
1762
+ IsolateData::Set(isolate, IsolateData::IN_GVL, true);
1540
1763
 
1541
1764
  return NULL;
1542
1765
  }
@@ -1551,7 +1774,7 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1551
1774
  FunctionCall call;
1552
1775
  VALUE *call_argv = NULL;
1553
1776
 
1554
- Data_Get_Struct(self, ContextInfo, context_info);
1777
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1555
1778
  Isolate* isolate = context_info->isolate_info->isolate;
1556
1779
 
1557
1780
  if (argc < 1) {
@@ -1584,6 +1807,13 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1584
1807
  unsigned long sl_int = NUM2ULONG(mem_softlimit);
1585
1808
  call.max_memory = (size_t)sl_int;
1586
1809
  }
1810
+
1811
+ call.marshal_stackdepth = 0;
1812
+ VALUE marshal_stackdepth = rb_iv_get(self, "@marshal_stack_depth");
1813
+ if (marshal_stackdepth != Qnil) {
1814
+ unsigned long sl_int = NUM2ULONG(marshal_stackdepth);
1815
+ call.marshal_stackdepth = (size_t)sl_int;
1816
+ }
1587
1817
 
1588
1818
  bool missingFunction = false;
1589
1819
  {
@@ -1605,23 +1835,15 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1605
1835
  if (val.IsEmpty() || !val.ToLocalChecked()->IsFunction()) {
1606
1836
  missingFunction = true;
1607
1837
  } else {
1608
-
1609
1838
  Local<v8::Function> fun = Local<v8::Function>::Cast(val.ToLocalChecked());
1839
+ VALUE tmp;
1610
1840
  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
- }
1841
+ call.argv = (v8::Local<Value> *)RB_ALLOCV_N(void *, tmp, call.argc);
1842
+ for(int i=0; i < call.argc; i++) {
1843
+ call.argv[i] = convert_ruby_to_v8(isolate, context, call_argv[i]);
1621
1844
  }
1622
1845
  rb_thread_call_without_gvl(nogvl_context_call, &call, unblock_function, &call);
1623
- free(call.argv);
1624
-
1846
+ RB_ALLOCV_END(tmp);
1625
1847
  }
1626
1848
  }
1627
1849
 
@@ -1634,7 +1856,7 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1634
1856
 
1635
1857
  static VALUE rb_context_create_isolate_value(VALUE self) {
1636
1858
  ContextInfo* context_info;
1637
- Data_Get_Struct(self, ContextInfo, context_info);
1859
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1638
1860
  IsolateInfo *isolate_info = context_info->isolate_info;
1639
1861
 
1640
1862
  if (!isolate_info) {
@@ -1642,7 +1864,7 @@ static VALUE rb_context_create_isolate_value(VALUE self) {
1642
1864
  }
1643
1865
 
1644
1866
  isolate_info->hold();
1645
- return Data_Wrap_Struct(rb_cIsolate, NULL, &deallocate_isolate, isolate_info);
1867
+ return TypedData_Wrap_Struct(rb_cIsolate, &isolate_type, isolate_info);
1646
1868
  }
1647
1869
 
1648
1870
  static void set_ruby_exiting(VALUE value) {
@@ -1698,7 +1920,6 @@ extern "C" {
1698
1920
  rb_define_alloc_func(rb_cIsolate, allocate_isolate);
1699
1921
 
1700
1922
  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
1923
 
1703
1924
  rb_define_method(rb_cSnapshot, "size", (VALUE(*)(...))&rb_snapshot_size, 0);
1704
1925
  rb_define_method(rb_cSnapshot, "dump", (VALUE(*)(...))&rb_snapshot_dump, 0);