mini_racer 0.4.0 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,7 @@
2
2
  #include <ruby.h>
3
3
  #include <ruby/thread.h>
4
4
  #include <ruby/io.h>
5
+ #include <ruby/version.h>
5
6
  #include <v8.h>
6
7
  #include <v8-profiler.h>
7
8
  #include <libplatform/libplatform.h>
@@ -11,6 +12,15 @@
11
12
  #include <mutex>
12
13
  #include <atomic>
13
14
  #include <math.h>
15
+ #include <errno.h>
16
+
17
+ /* workaround C Ruby <= 2.x problems w/ clang in C++ mode */
18
+ #if defined(ENGINE_IS_CRUBY) && \
19
+ RUBY_API_VERSION_MAJOR == 2 && RUBY_API_VERSION_MINOR <= 6
20
+ # define MR_METHOD_FUNC(fn) RUBY_METHOD_FUNC(fn)
21
+ #else
22
+ # define MR_METHOD_FUNC(fn) fn
23
+ #endif
14
24
 
15
25
  using namespace v8;
16
26
 
@@ -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);