mini_racer 0.5.0 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,7 @@
2
2
  #include <ruby.h>
3
3
  #include <ruby/thread.h>
4
4
  #include <ruby/io.h>
5
+ #include <ruby/version.h>
5
6
  #include <v8.h>
6
7
  #include <v8-profiler.h>
7
8
  #include <libplatform/libplatform.h>
@@ -11,6 +12,15 @@
11
12
  #include <mutex>
12
13
  #include <atomic>
13
14
  #include <math.h>
15
+ #include <errno.h>
16
+
17
+ /* workaround C Ruby <= 2.x problems w/ clang in C++ mode */
18
+ #if defined(ENGINE_IS_CRUBY) && \
19
+ RUBY_API_VERSION_MAJOR == 2 && RUBY_API_VERSION_MINOR <= 6
20
+ # define MR_METHOD_FUNC(fn) RUBY_METHOD_FUNC(fn)
21
+ #else
22
+ # define MR_METHOD_FUNC(fn) fn
23
+ #endif
14
24
 
15
25
  using namespace v8;
16
26
 
@@ -295,17 +305,36 @@ static std::unique_ptr<Platform> current_platform = NULL;
295
305
  static std::mutex platform_lock;
296
306
 
297
307
  static pthread_attr_t *thread_attr_p;
298
- static pthread_rwlock_t exit_lock = PTHREAD_RWLOCK_INITIALIZER;
299
- static bool ruby_exiting = false; // guarded by exit_lock
308
+ static std::atomic_int ruby_exiting(0);
300
309
  static bool single_threaded = false;
301
310
 
311
+ static void mark_context(void *);
312
+ static void deallocate(void *);
313
+ static size_t context_memsize(const void *);
314
+ static const rb_data_type_t context_type = {
315
+ "mini_racer/context_info",
316
+ { mark_context, deallocate, context_memsize }
317
+ };
318
+
319
+ static void deallocate_snapshot(void *);
320
+ static size_t snapshot_memsize(const void *);
321
+ static const rb_data_type_t snapshot_type = {
322
+ "mini_racer/snapshot_info",
323
+ { NULL, deallocate_snapshot, snapshot_memsize }
324
+ };
325
+
326
+ static void mark_isolate(void *);
327
+ static void deallocate_isolate(void *);
328
+ static size_t isolate_memsize(const void *);
329
+ static const rb_data_type_t isolate_type = {
330
+ "mini_racer/isolate_info",
331
+ { mark_isolate, deallocate_isolate, isolate_memsize }
332
+ };
333
+
302
334
  static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
303
335
  bool platform_already_initialized = false;
304
336
 
305
- if(TYPE(flag_as_str) != T_STRING) {
306
- rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE" (should be a string)",
307
- rb_obj_class(flag_as_str));
308
- }
337
+ Check_Type(flag_as_str, T_STRING);
309
338
 
310
339
  platform_lock.lock();
311
340
 
@@ -313,7 +342,7 @@ static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
313
342
  if (!strcmp(RSTRING_PTR(flag_as_str), "--single_threaded")) {
314
343
  single_threaded = true;
315
344
  }
316
- V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), (int)RSTRING_LEN(flag_as_str));
345
+ V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), RSTRING_LENINT(flag_as_str));
317
346
  } else {
318
347
  platform_already_initialized = true;
319
348
  }
@@ -336,7 +365,11 @@ static void init_v8() {
336
365
 
337
366
  if (current_platform == NULL) {
338
367
  V8::InitializeICU();
339
- current_platform = platform::NewDefaultPlatform();
368
+ if (single_threaded) {
369
+ current_platform = platform::NewSingleThreadedDefaultPlatform();
370
+ } else {
371
+ current_platform = platform::NewDefaultPlatform();
372
+ }
340
373
  V8::InitializePlatform(current_platform.get());
341
374
  V8::Initialize();
342
375
  }
@@ -381,28 +414,32 @@ static void prepare_result(MaybeLocal<Value> v8res,
381
414
  Local<Value> local_value = v8res.ToLocalChecked();
382
415
  if ((local_value->IsObject() || local_value->IsArray()) &&
383
416
  !local_value->IsDate() && !local_value->IsFunction()) {
384
- Local<Object> JSON = context->Global()->Get(
385
- context, String::NewFromUtf8Literal(isolate, "JSON"))
386
- .ToLocalChecked().As<Object>();
387
-
388
- Local<Function> stringify = JSON->Get(
389
- context, v8::String::NewFromUtf8Literal(isolate, "stringify"))
390
- .ToLocalChecked().As<Function>();
417
+ MaybeLocal<v8::Value> ml = context->Global()->Get(
418
+ context, String::NewFromUtf8Literal(isolate, "JSON"));
391
419
 
392
- Local<Object> object = local_value->ToObject(context).ToLocalChecked();
393
- const unsigned argc = 1;
394
- Local<Value> argv[argc] = { object };
395
- MaybeLocal<Value> json = stringify->Call(context, JSON, argc, argv);
396
-
397
- if (json.IsEmpty()) {
420
+ if (ml.IsEmpty()) { // exception
398
421
  evalRes.executed = false;
399
422
  } else {
400
- evalRes.json = true;
401
- Persistent<Value>* persistent = new Persistent<Value>();
402
- persistent->Reset(isolate, json.ToLocalChecked());
403
- evalRes.value = persistent;
423
+ Local<Object> JSON = ml.ToLocalChecked().As<Object>();
424
+
425
+ Local<Function> stringify = JSON->Get(
426
+ context, v8::String::NewFromUtf8Literal(isolate, "stringify"))
427
+ .ToLocalChecked().As<Function>();
428
+
429
+ Local<Object> object = local_value->ToObject(context).ToLocalChecked();
430
+ const unsigned argc = 1;
431
+ Local<Value> argv[argc] = { object };
432
+ MaybeLocal<Value> json = stringify->Call(context, JSON, argc, argv);
433
+
434
+ if (json.IsEmpty()) {
435
+ evalRes.executed = false;
436
+ } else {
437
+ evalRes.json = true;
438
+ Persistent<Value>* persistent = new Persistent<Value>();
439
+ persistent->Reset(isolate, json.ToLocalChecked());
440
+ evalRes.value = persistent;
441
+ }
404
442
  }
405
-
406
443
  } else {
407
444
  Persistent<Value>* persistent = new Persistent<Value>();
408
445
  persistent->Reset(isolate, local_value);
@@ -453,7 +490,7 @@ static void prepare_result(MaybeLocal<Value> v8res,
453
490
  }
454
491
  }
455
492
 
456
- void*
493
+ static void*
457
494
  nogvl_context_eval(void* arg) {
458
495
 
459
496
  EvalParams* eval_params = (EvalParams*)arg;
@@ -623,11 +660,7 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
623
660
  v8::String::Utf8Value symbol_name(isolate,
624
661
  Local<Symbol>::Cast(value)->Name());
625
662
 
626
- VALUE str_symbol = rb_enc_str_new(
627
- *symbol_name,
628
- symbol_name.length(),
629
- rb_enc_find("utf-8")
630
- );
663
+ VALUE str_symbol = rb_utf8_str_new(*symbol_name, symbol_name.length());
631
664
 
632
665
  return rb_str_intern(str_symbol);
633
666
  }
@@ -638,7 +671,7 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
638
671
  return Qnil;
639
672
  } else {
640
673
  Local<String> rstr = rstr_maybe.ToLocalChecked();
641
- return rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
674
+ return rb_utf8_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate));
642
675
  }
643
676
  }
644
677
 
@@ -683,7 +716,7 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
683
716
  case T_FLOAT:
684
717
  return scope.Escape(Number::New(isolate, NUM2DBL(value)));
685
718
  case T_STRING:
686
- return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, (int)RSTRING_LEN(value)).ToLocalChecked());
719
+ return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, RSTRING_LENINT(value)).ToLocalChecked());
687
720
  case T_NIL:
688
721
  return scope.Escape(Null(isolate));
689
722
  case T_TRUE:
@@ -711,7 +744,7 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
711
744
  return scope.Escape(object);
712
745
  case T_SYMBOL:
713
746
  value = rb_funcall(value, rb_intern("to_s"), 0);
714
- return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, (int)RSTRING_LEN(value)).ToLocalChecked());
747
+ return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, RSTRING_LENINT(value)).ToLocalChecked());
715
748
  case T_DATA:
716
749
  klass = rb_funcall(value, rb_intern("class"), 0);
717
750
  if (klass == rb_cTime || klass == rb_cDateTime)
@@ -786,6 +819,7 @@ create_snapshot_data_blob(const char *embedded_source = nullptr) {
786
819
  SnapshotCreator::FunctionCodeHandling::kClear);
787
820
  }
788
821
 
822
+ static
789
823
  StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
790
824
  const char *warmup_source) {
791
825
  // Use following steps to create a warmed up snapshot blob from a cold one:
@@ -819,21 +853,18 @@ StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
819
853
  return result;
820
854
  }
821
855
 
822
- static VALUE rb_snapshot_size(VALUE self, VALUE str) {
856
+ static VALUE rb_snapshot_size(VALUE self) {
823
857
  SnapshotInfo* snapshot_info;
824
- Data_Get_Struct(self, SnapshotInfo, snapshot_info);
858
+ TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
825
859
 
826
860
  return INT2NUM(snapshot_info->raw_size);
827
861
  }
828
862
 
829
863
  static VALUE rb_snapshot_load(VALUE self, VALUE str) {
830
864
  SnapshotInfo* snapshot_info;
831
- Data_Get_Struct(self, SnapshotInfo, snapshot_info);
865
+ TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
832
866
 
833
- if(TYPE(str) != T_STRING) {
834
- rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
835
- rb_obj_class(str));
836
- }
867
+ Check_Type(str, T_STRING);
837
868
 
838
869
  init_v8();
839
870
 
@@ -849,21 +880,18 @@ static VALUE rb_snapshot_load(VALUE self, VALUE str) {
849
880
  return Qnil;
850
881
  }
851
882
 
852
- static VALUE rb_snapshot_dump(VALUE self, VALUE str) {
883
+ static VALUE rb_snapshot_dump(VALUE self) {
853
884
  SnapshotInfo* snapshot_info;
854
- Data_Get_Struct(self, SnapshotInfo, snapshot_info);
885
+ TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
855
886
 
856
887
  return rb_str_new(snapshot_info->data, snapshot_info->raw_size);
857
888
  }
858
889
 
859
890
  static VALUE rb_snapshot_warmup_unsafe(VALUE self, VALUE str) {
860
891
  SnapshotInfo* snapshot_info;
861
- Data_Get_Struct(self, SnapshotInfo, snapshot_info);
892
+ TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
862
893
 
863
- if(TYPE(str) != T_STRING) {
864
- rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
865
- rb_obj_class(str));
866
- }
894
+ Check_Type(str, T_STRING);
867
895
 
868
896
  init_v8();
869
897
 
@@ -905,13 +933,13 @@ void IsolateInfo::init(SnapshotInfo* snapshot_info) {
905
933
 
906
934
  static VALUE rb_isolate_init_with_snapshot(VALUE self, VALUE snapshot) {
907
935
  IsolateInfo* isolate_info;
908
- Data_Get_Struct(self, IsolateInfo, isolate_info);
936
+ TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
909
937
 
910
938
  init_v8();
911
939
 
912
940
  SnapshotInfo* snapshot_info = nullptr;
913
941
  if (!NIL_P(snapshot)) {
914
- Data_Get_Struct(snapshot, SnapshotInfo, snapshot_info);
942
+ TypedData_Get_Struct(snapshot, SnapshotInfo, &snapshot_type, snapshot_info);
915
943
  }
916
944
 
917
945
  isolate_info->init(snapshot_info);
@@ -922,7 +950,7 @@ static VALUE rb_isolate_init_with_snapshot(VALUE self, VALUE snapshot) {
922
950
 
923
951
  static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
924
952
  IsolateInfo* isolate_info;
925
- Data_Get_Struct(self, IsolateInfo, isolate_info);
953
+ TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
926
954
 
927
955
  if (current_platform == NULL) return Qfalse;
928
956
 
@@ -933,7 +961,7 @@ static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
933
961
 
934
962
  static VALUE rb_isolate_low_memory_notification(VALUE self) {
935
963
  IsolateInfo* isolate_info;
936
- Data_Get_Struct(self, IsolateInfo, isolate_info);
964
+ TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
937
965
 
938
966
  if (current_platform == NULL) return Qfalse;
939
967
 
@@ -943,7 +971,7 @@ static VALUE rb_isolate_low_memory_notification(VALUE self) {
943
971
 
944
972
  static VALUE rb_isolate_pump_message_loop(VALUE self) {
945
973
  IsolateInfo* isolate_info;
946
- Data_Get_Struct(self, IsolateInfo, isolate_info);
974
+ TypedData_Get_Struct(self, IsolateInfo, &isolate_type, isolate_info);
947
975
 
948
976
  if (current_platform == NULL) return Qfalse;
949
977
 
@@ -956,7 +984,7 @@ static VALUE rb_isolate_pump_message_loop(VALUE self) {
956
984
 
957
985
  static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
958
986
  ContextInfo* context_info;
959
- Data_Get_Struct(self, ContextInfo, context_info);
987
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
960
988
 
961
989
  init_v8();
962
990
 
@@ -967,11 +995,11 @@ static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
967
995
 
968
996
  SnapshotInfo *snapshot_info = nullptr;
969
997
  if (!NIL_P(snap) && rb_obj_is_kind_of(snap, rb_cSnapshot)) {
970
- Data_Get_Struct(snap, SnapshotInfo, snapshot_info);
998
+ TypedData_Get_Struct(snap, SnapshotInfo, &snapshot_type, snapshot_info);
971
999
  }
972
1000
  isolate_info->init(snapshot_info);
973
1001
  } else { // given isolate or snapshot
974
- Data_Get_Struct(isolate, IsolateInfo, isolate_info);
1002
+ TypedData_Get_Struct(isolate, IsolateInfo, &isolate_type, isolate_info);
975
1003
  }
976
1004
 
977
1005
  context_info->isolate_info = isolate_info;
@@ -1001,7 +1029,7 @@ static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
1001
1029
  static VALUE convert_result_to_ruby(VALUE self /* context */,
1002
1030
  EvalResult& result) {
1003
1031
  ContextInfo *context_info;
1004
- Data_Get_Struct(self, ContextInfo, context_info);
1032
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1005
1033
 
1006
1034
  Isolate *isolate = context_info->isolate_info->isolate;
1007
1035
  Persistent<Context> *p_ctx = context_info->context;
@@ -1028,7 +1056,7 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
1028
1056
  // a v8 scope, if we do the scope is never cleaned up properly and we leak
1029
1057
  if (!result.parsed) {
1030
1058
  if(TYPE(message) == T_STRING) {
1031
- rb_raise(rb_eParseError, "%s", RSTRING_PTR(message));
1059
+ rb_raise(rb_eParseError, "%" PRIsVALUE, message);
1032
1060
  } else {
1033
1061
  rb_raise(rb_eParseError, "Unknown JavaScript Error during parse");
1034
1062
  }
@@ -1042,8 +1070,7 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
1042
1070
  // If we were terminated or have the memory softlimit flag set
1043
1071
  if (marshal_stack_maxdepth_reached) {
1044
1072
  ruby_exception = rb_eScriptRuntimeError;
1045
- std::string msg = std::string("Marshal object depth too deep. Script terminated.");
1046
- message = rb_enc_str_new(msg.c_str(), msg.length(), rb_enc_find("utf-8"));
1073
+ message = rb_utf8_str_new_literal("Marshal object depth too deep. Script terminated.");
1047
1074
  } else if (result.terminated || mem_softlimit_reached) {
1048
1075
  ruby_exception = mem_softlimit_reached ? rb_eV8OutOfMemoryError : rb_eScriptTerminatedError;
1049
1076
  } else {
@@ -1052,15 +1079,17 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
1052
1079
 
1053
1080
  // exception report about what happened
1054
1081
  if (TYPE(backtrace) == T_STRING) {
1055
- rb_raise(ruby_exception, "%s", RSTRING_PTR(backtrace));
1082
+ rb_raise(ruby_exception, "%" PRIsVALUE, backtrace);
1056
1083
  } else if(TYPE(message) == T_STRING) {
1057
- rb_raise(ruby_exception, "%s", RSTRING_PTR(message));
1084
+ rb_raise(ruby_exception, "%" PRIsVALUE, message);
1058
1085
  } else {
1059
1086
  rb_raise(ruby_exception, "Unknown JavaScript Error during execution");
1060
1087
  }
1088
+ } else if (rb_obj_is_kind_of(ruby_exception, rb_eException)) {
1089
+ rb_exc_raise(ruby_exception);
1061
1090
  } else {
1062
1091
  VALUE rb_str = rb_funcall(ruby_exception, rb_intern("to_s"), 0);
1063
- rb_raise(CLASS_OF(ruby_exception), "%s", RSTRING_PTR(rb_str));
1092
+ rb_raise(CLASS_OF(ruby_exception), "%" PRIsVALUE, rb_str);
1064
1093
  }
1065
1094
  }
1066
1095
 
@@ -1076,7 +1105,7 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
1076
1105
 
1077
1106
  if (result.json) {
1078
1107
  Local<String> rstr = tmp->ToString(p_ctx->Get(isolate)).ToLocalChecked();
1079
- VALUE json_string = rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
1108
+ VALUE json_string = rb_utf8_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate));
1080
1109
  ret = rb_funcall(rb_mJSON, rb_intern("parse"), 1, json_string);
1081
1110
  } else {
1082
1111
  StackCounter::Reset(isolate);
@@ -1102,16 +1131,13 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
1102
1131
  EvalResult eval_result;
1103
1132
  ContextInfo* context_info;
1104
1133
 
1105
- Data_Get_Struct(self, ContextInfo, context_info);
1134
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1106
1135
  Isolate* isolate = context_info->isolate_info->isolate;
1107
1136
 
1108
- if(TYPE(str) != T_STRING) {
1109
- rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
1110
- rb_obj_class(str));
1111
- }
1112
- if(filename != Qnil && TYPE(filename) != T_STRING) {
1113
- rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be nil or a string)",
1114
- rb_obj_class(filename));
1137
+ Check_Type(str, T_STRING);
1138
+
1139
+ if (!NIL_P(filename)) {
1140
+ Check_Type(filename, T_STRING);
1115
1141
  }
1116
1142
 
1117
1143
  {
@@ -1120,13 +1146,13 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
1120
1146
  HandleScope handle_scope(isolate);
1121
1147
 
1122
1148
  Local<String> eval = String::NewFromUtf8(isolate, RSTRING_PTR(str),
1123
- NewStringType::kNormal, (int)RSTRING_LEN(str)).ToLocalChecked();
1149
+ NewStringType::kNormal, RSTRING_LENINT(str)).ToLocalChecked();
1124
1150
 
1125
1151
  Local<String> local_filename;
1126
1152
 
1127
1153
  if (filename != Qnil) {
1128
1154
  local_filename = String::NewFromUtf8(isolate, RSTRING_PTR(filename),
1129
- NewStringType::kNormal, (int)RSTRING_LEN(filename)).ToLocalChecked();
1155
+ NewStringType::kNormal, RSTRING_LENINT(filename)).ToLocalChecked();
1130
1156
  eval_params.filename = &local_filename;
1131
1157
  } else {
1132
1158
  eval_params.filename = NULL;
@@ -1190,7 +1216,7 @@ VALUE rescue_callback(VALUE rdata, VALUE exception) {
1190
1216
  return exception;
1191
1217
  }
1192
1218
 
1193
- void*
1219
+ static void*
1194
1220
  gvl_ruby_callback(void* data) {
1195
1221
 
1196
1222
  FunctionCallbackInfo<Value>* args = (FunctionCallbackInfo<Value>*)data;
@@ -1206,7 +1232,7 @@ gvl_ruby_callback(void* data) {
1206
1232
  HandleScope scope(args->GetIsolate());
1207
1233
  Local<External> external = Local<External>::Cast(args->Data());
1208
1234
 
1209
- self = *(VALUE*)(external->Value());
1235
+ self = (VALUE)(external->Value());
1210
1236
  callback = rb_iv_get(self, "@callback");
1211
1237
 
1212
1238
  parent = rb_iv_get(self, "@parent");
@@ -1214,7 +1240,7 @@ gvl_ruby_callback(void* data) {
1214
1240
  return NULL;
1215
1241
  }
1216
1242
 
1217
- Data_Get_Struct(parent, ContextInfo, context_info);
1243
+ TypedData_Get_Struct(parent, ContextInfo, &context_type, context_info);
1218
1244
 
1219
1245
  if (length > 0) {
1220
1246
  ruby_args = rb_ary_tmp_new(length);
@@ -1243,7 +1269,6 @@ gvl_ruby_callback(void* data) {
1243
1269
  args->GetIsolate()->TerminateExecution();
1244
1270
  if (length > 0) {
1245
1271
  rb_ary_clear(ruby_args);
1246
- rb_gc_force_recycle(ruby_args);
1247
1272
  }
1248
1273
  return NULL;
1249
1274
  }
@@ -1251,8 +1276,8 @@ gvl_ruby_callback(void* data) {
1251
1276
  VALUE callback_data_value = (VALUE)&callback_data;
1252
1277
 
1253
1278
  // TODO: use rb_vrescue2 in Ruby 2.7 and above
1254
- result = rb_rescue2(RUBY_METHOD_FUNC(protected_callback), callback_data_value,
1255
- RUBY_METHOD_FUNC(rescue_callback), callback_data_value, rb_eException, (VALUE)0);
1279
+ result = rb_rescue2(MR_METHOD_FUNC(protected_callback), callback_data_value,
1280
+ MR_METHOD_FUNC(rescue_callback), callback_data_value, rb_eException, (VALUE)0);
1256
1281
 
1257
1282
  if(callback_data.failed) {
1258
1283
  rb_iv_set(parent, "@current_exception", result);
@@ -1266,7 +1291,6 @@ gvl_ruby_callback(void* data) {
1266
1291
 
1267
1292
  if (length > 0) {
1268
1293
  rb_ary_clear(ruby_args);
1269
- rb_gc_force_recycle(ruby_args);
1270
1294
  }
1271
1295
 
1272
1296
  if (IsolateData::Get(args->GetIsolate(), IsolateData::DO_TERMINATE)) {
@@ -1301,7 +1325,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1301
1325
  bool parse_error = false;
1302
1326
  bool attach_error = false;
1303
1327
 
1304
- Data_Get_Struct(parent, ContextInfo, context_info);
1328
+ TypedData_Get_Struct(parent, ContextInfo, &context_type, context_info);
1305
1329
  Isolate* isolate = context_info->isolate_info->isolate;
1306
1330
 
1307
1331
  {
@@ -1314,15 +1338,12 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1314
1338
 
1315
1339
  Local<String> v8_str =
1316
1340
  String::NewFromUtf8(isolate, RSTRING_PTR(name),
1317
- NewStringType::kNormal, (int)RSTRING_LEN(name))
1341
+ NewStringType::kNormal, RSTRING_LENINT(name))
1318
1342
  .ToLocalChecked();
1319
1343
 
1320
- // copy self so we can access from v8 external
1321
- VALUE* self_copy;
1322
- Data_Get_Struct(self, VALUE, self_copy);
1323
- *self_copy = self;
1324
-
1325
- Local<Value> external = External::New(isolate, self_copy);
1344
+ // Note that self (rb_cExternalFunction) is a pure Ruby T_OBJECT,
1345
+ // not a T_DATA type like most other classes in this file
1346
+ Local<Value> external = External::New(isolate, (void *)self);
1326
1347
 
1327
1348
  if (parent_object == Qnil) {
1328
1349
  Maybe<bool> success = context->Global()->Set(
@@ -1337,7 +1358,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1337
1358
  Local<String> eval =
1338
1359
  String::NewFromUtf8(isolate, RSTRING_PTR(parent_object_eval),
1339
1360
  NewStringType::kNormal,
1340
- (int)RSTRING_LEN(parent_object_eval))
1361
+ RSTRING_LENINT(parent_object_eval))
1341
1362
  .ToLocalChecked();
1342
1363
 
1343
1364
  MaybeLocal<Script> parsed_script = Script::Compile(context, eval);
@@ -1379,7 +1400,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1379
1400
 
1380
1401
  static VALUE rb_context_isolate_mutex(VALUE self) {
1381
1402
  ContextInfo* context_info;
1382
- Data_Get_Struct(self, ContextInfo, context_info);
1403
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1383
1404
 
1384
1405
  if (!context_info->isolate_info) {
1385
1406
  rb_raise(rb_eScriptRuntimeError, "Context has no Isolate available anymore");
@@ -1435,42 +1456,33 @@ static void free_context_raw(void *arg) {
1435
1456
  if (isolate_info) {
1436
1457
  isolate_info->release();
1437
1458
  }
1438
-
1439
- xfree(context_info);
1440
1459
  }
1441
1460
 
1442
1461
  static void *free_context_thr(void* arg) {
1443
- if (pthread_rwlock_tryrdlock(&exit_lock) != 0) {
1444
- return NULL;
1462
+ if (ruby_exiting.load() == 0) {
1463
+ free_context_raw(arg);
1464
+ xfree(arg);
1445
1465
  }
1446
- if (ruby_exiting) {
1447
- return NULL;
1448
- }
1449
-
1450
- free_context_raw(arg);
1451
-
1452
- pthread_rwlock_unlock(&exit_lock);
1453
-
1454
1466
  return NULL;
1455
1467
  }
1456
1468
 
1457
1469
  // destroys everything except freeing the ContextInfo struct (see deallocate())
1458
1470
  static void free_context(ContextInfo* context_info) {
1459
-
1460
1471
  IsolateInfo* isolate_info = context_info->isolate_info;
1461
1472
 
1462
- ContextInfo* context_info_copy = ALLOC(ContextInfo);
1463
- context_info_copy->isolate_info = context_info->isolate_info;
1464
- context_info_copy->context = context_info->context;
1465
-
1466
1473
  if (isolate_info && isolate_info->refs() > 1) {
1467
1474
  pthread_t free_context_thread;
1475
+ ContextInfo* context_info_copy = ALLOC(ContextInfo);
1476
+
1477
+ context_info_copy->isolate_info = context_info->isolate_info;
1478
+ context_info_copy->context = context_info->context;
1468
1479
  if (pthread_create(&free_context_thread, thread_attr_p,
1469
1480
  free_context_thr, (void*)context_info_copy)) {
1470
1481
  fprintf(stderr, "WARNING failed to release memory in MiniRacer, thread to release could not be created, process will leak memory\n");
1482
+ xfree(context_info_copy);
1471
1483
  }
1472
1484
  } else {
1473
- free_context_raw(context_info_copy);
1485
+ free_context_raw(context_info);
1474
1486
  }
1475
1487
 
1476
1488
  context_info->context = NULL;
@@ -1489,6 +1501,11 @@ static void mark_isolate(void* data) {
1489
1501
  isolate_info->mark();
1490
1502
  }
1491
1503
 
1504
+ static size_t isolate_memsize(const void *ptr) {
1505
+ const IsolateInfo *isolate_info = (const IsolateInfo *)ptr;
1506
+ return sizeof(*isolate_info);
1507
+ }
1508
+
1492
1509
  static void deallocate(void* data) {
1493
1510
  ContextInfo* context_info = (ContextInfo*)data;
1494
1511
 
@@ -1497,6 +1514,11 @@ static void deallocate(void* data) {
1497
1514
  xfree(data);
1498
1515
  }
1499
1516
 
1517
+ static size_t context_memsize(const void *ptr)
1518
+ {
1519
+ return sizeof(ContextInfo);
1520
+ }
1521
+
1500
1522
  static void mark_context(void* data) {
1501
1523
  ContextInfo* context_info = (ContextInfo*)data;
1502
1524
  if (context_info->isolate_info) {
@@ -1504,48 +1526,39 @@ static void mark_context(void* data) {
1504
1526
  }
1505
1527
  }
1506
1528
 
1507
- static void deallocate_external_function(void * data) {
1508
- xfree(data);
1509
- }
1510
-
1511
1529
  static void deallocate_snapshot(void * data) {
1512
1530
  SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
1513
1531
  delete[] snapshot_info->data;
1514
1532
  xfree(snapshot_info);
1515
1533
  }
1516
1534
 
1517
- static VALUE allocate_external_function(VALUE klass) {
1518
- VALUE* self = ALLOC(VALUE);
1519
- return Data_Wrap_Struct(klass, NULL, deallocate_external_function, (void*)self);
1535
+ static size_t snapshot_memsize(const void *data) {
1536
+ SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
1537
+ return sizeof(*snapshot_info) + snapshot_info->raw_size;
1520
1538
  }
1521
1539
 
1522
1540
  static VALUE allocate(VALUE klass) {
1523
- ContextInfo* context_info = ALLOC(ContextInfo);
1524
- context_info->isolate_info = NULL;
1525
- context_info->context = NULL;
1526
-
1527
- return Data_Wrap_Struct(klass, mark_context, deallocate, (void*)context_info);
1541
+ ContextInfo* context_info;
1542
+ return TypedData_Make_Struct(klass, ContextInfo, &context_type, context_info);
1528
1543
  }
1529
1544
 
1530
1545
  static VALUE allocate_snapshot(VALUE klass) {
1531
- SnapshotInfo* snapshot_info = ALLOC(SnapshotInfo);
1532
- snapshot_info->data = NULL;
1533
- snapshot_info->raw_size = 0;
1546
+ SnapshotInfo* snapshot_info;
1534
1547
 
1535
- return Data_Wrap_Struct(klass, NULL, deallocate_snapshot, (void*)snapshot_info);
1548
+ return TypedData_Make_Struct(klass, SnapshotInfo, &snapshot_type, snapshot_info);
1536
1549
  }
1537
1550
 
1538
1551
  static VALUE allocate_isolate(VALUE klass) {
1539
1552
  IsolateInfo* isolate_info = new IsolateInfo();
1540
1553
 
1541
- return Data_Wrap_Struct(klass, mark_isolate, deallocate_isolate, (void*)isolate_info);
1554
+ return TypedData_Wrap_Struct(klass, &isolate_type, isolate_info);
1542
1555
  }
1543
1556
 
1544
1557
  static VALUE
1545
1558
  rb_heap_stats(VALUE self) {
1546
1559
 
1547
1560
  ContextInfo* context_info;
1548
- Data_Get_Struct(self, ContextInfo, context_info);
1561
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1549
1562
  Isolate* isolate;
1550
1563
  v8::HeapStatistics stats;
1551
1564
 
@@ -1577,7 +1590,9 @@ rb_heap_stats(VALUE self) {
1577
1590
  // https://github.com/bnoordhuis/node-heapdump/blob/master/src/heapdump.cc
1578
1591
  class FileOutputStream : public OutputStream {
1579
1592
  public:
1580
- FileOutputStream(FILE* stream) : stream_(stream) {}
1593
+ int err;
1594
+
1595
+ FileOutputStream(int fd) : fd(fd) { err = 0; }
1581
1596
 
1582
1597
  virtual int GetChunkSize() {
1583
1598
  return 65536;
@@ -1586,17 +1601,27 @@ class FileOutputStream : public OutputStream {
1586
1601
  virtual void EndOfStream() {}
1587
1602
 
1588
1603
  virtual WriteResult WriteAsciiChunk(char* data, int size) {
1589
- const size_t len = static_cast<size_t>(size);
1590
- size_t off = 0;
1591
-
1592
- while (off < len && !feof(stream_) && !ferror(stream_))
1593
- off += fwrite(data + off, 1, len - off, stream_);
1594
-
1595
- return off == len ? kContinue : kAbort;
1604
+ size_t len = static_cast<size_t>(size);
1605
+
1606
+ while (len) {
1607
+ ssize_t w = write(fd, data, len);
1608
+
1609
+ if (w > 0) {
1610
+ data += w;
1611
+ len -= w;
1612
+ } else if (w < 0) {
1613
+ err = errno;
1614
+ return kAbort;
1615
+ } else { /* w == 0, could be out-of-space */
1616
+ err = -1;
1617
+ return kAbort;
1618
+ }
1619
+ }
1620
+ return kContinue;
1596
1621
  }
1597
1622
 
1598
1623
  private:
1599
- FILE* stream_;
1624
+ int fd;
1600
1625
  };
1601
1626
 
1602
1627
 
@@ -1609,13 +1634,11 @@ rb_heap_snapshot(VALUE self, VALUE file) {
1609
1634
 
1610
1635
  if (!fptr) return Qfalse;
1611
1636
 
1612
- FILE* fp;
1613
- fp = fdopen(fptr->fd, "w");
1614
- if (fp == NULL) return Qfalse;
1615
-
1637
+ // prepare for unbuffered write(2) below:
1638
+ rb_funcall(file, rb_intern("flush"), 0);
1616
1639
 
1617
1640
  ContextInfo* context_info;
1618
- Data_Get_Struct(self, ContextInfo, context_info);
1641
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1619
1642
  Isolate* isolate;
1620
1643
  isolate = context_info->isolate_info ? context_info->isolate_info->isolate : NULL;
1621
1644
 
@@ -1629,13 +1652,14 @@ rb_heap_snapshot(VALUE self, VALUE file) {
1629
1652
 
1630
1653
  const HeapSnapshot* const snap = heap_profiler->TakeHeapSnapshot();
1631
1654
 
1632
- FileOutputStream stream(fp);
1655
+ FileOutputStream stream(fptr->fd);
1633
1656
  snap->Serialize(&stream, HeapSnapshot::kJSON);
1634
1657
 
1635
- fflush(fp);
1636
-
1637
1658
  const_cast<HeapSnapshot*>(snap)->Delete();
1638
1659
 
1660
+ /* TODO: perhaps rb_sys_fail here */
1661
+ if (stream.err) return Qfalse;
1662
+
1639
1663
  return Qtrue;
1640
1664
  }
1641
1665
 
@@ -1643,7 +1667,7 @@ static VALUE
1643
1667
  rb_context_stop(VALUE self) {
1644
1668
 
1645
1669
  ContextInfo* context_info;
1646
- Data_Get_Struct(self, ContextInfo, context_info);
1670
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1647
1671
 
1648
1672
  Isolate* isolate = context_info->isolate_info->isolate;
1649
1673
 
@@ -1660,7 +1684,7 @@ static VALUE
1660
1684
  rb_context_dispose(VALUE self) {
1661
1685
 
1662
1686
  ContextInfo* context_info;
1663
- Data_Get_Struct(self, ContextInfo, context_info);
1687
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1664
1688
 
1665
1689
  free_context(context_info);
1666
1690
 
@@ -1723,7 +1747,7 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1723
1747
  FunctionCall call;
1724
1748
  VALUE *call_argv = NULL;
1725
1749
 
1726
- Data_Get_Struct(self, ContextInfo, context_info);
1750
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1727
1751
  Isolate* isolate = context_info->isolate_info->isolate;
1728
1752
 
1729
1753
  if (argc < 1) {
@@ -1731,9 +1755,7 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1731
1755
  }
1732
1756
 
1733
1757
  VALUE function_name = argv[0];
1734
- if (TYPE(function_name) != T_STRING) {
1735
- rb_raise(rb_eTypeError, "first argument should be a String");
1736
- }
1758
+ Check_Type(function_name, T_STRING);
1737
1759
 
1738
1760
  char *fname = RSTRING_PTR(function_name);
1739
1761
  if (!fname) {
@@ -1784,23 +1806,15 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1784
1806
  if (val.IsEmpty() || !val.ToLocalChecked()->IsFunction()) {
1785
1807
  missingFunction = true;
1786
1808
  } else {
1787
-
1788
1809
  Local<v8::Function> fun = Local<v8::Function>::Cast(val.ToLocalChecked());
1810
+ VALUE tmp;
1789
1811
  call.fun = fun;
1790
- int fun_argc = call.argc;
1791
-
1792
- if (fun_argc > 0) {
1793
- call.argv = (v8::Local<Value> *) malloc(sizeof(void *) * fun_argc);
1794
- if (!call.argv) {
1795
- return Qnil;
1796
- }
1797
- for(int i=0; i < fun_argc; i++) {
1798
- call.argv[i] = convert_ruby_to_v8(isolate, context, call_argv[i]);
1799
- }
1812
+ call.argv = (v8::Local<Value> *)RB_ALLOCV_N(void *, tmp, call.argc);
1813
+ for(int i=0; i < call.argc; i++) {
1814
+ call.argv[i] = convert_ruby_to_v8(isolate, context, call_argv[i]);
1800
1815
  }
1801
1816
  rb_thread_call_without_gvl(nogvl_context_call, &call, unblock_function, &call);
1802
- free(call.argv);
1803
-
1817
+ RB_ALLOCV_END(tmp);
1804
1818
  }
1805
1819
  }
1806
1820
 
@@ -1813,7 +1827,7 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1813
1827
 
1814
1828
  static VALUE rb_context_create_isolate_value(VALUE self) {
1815
1829
  ContextInfo* context_info;
1816
- Data_Get_Struct(self, ContextInfo, context_info);
1830
+ TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1817
1831
  IsolateInfo *isolate_info = context_info->isolate_info;
1818
1832
 
1819
1833
  if (!isolate_info) {
@@ -1821,18 +1835,13 @@ static VALUE rb_context_create_isolate_value(VALUE self) {
1821
1835
  }
1822
1836
 
1823
1837
  isolate_info->hold();
1824
- return Data_Wrap_Struct(rb_cIsolate, NULL, &deallocate_isolate, isolate_info);
1838
+ return TypedData_Wrap_Struct(rb_cIsolate, &isolate_type, isolate_info);
1825
1839
  }
1826
1840
 
1827
1841
  static void set_ruby_exiting(VALUE value) {
1828
1842
  (void)value;
1829
1843
 
1830
- int res = pthread_rwlock_wrlock(&exit_lock);
1831
-
1832
- ruby_exiting = true;
1833
- if (res == 0) {
1834
- pthread_rwlock_unlock(&exit_lock);
1835
- }
1844
+ ruby_exiting.store(1);
1836
1845
  }
1837
1846
 
1838
1847
  extern "C" {
@@ -1877,7 +1886,6 @@ extern "C" {
1877
1886
  rb_define_alloc_func(rb_cIsolate, allocate_isolate);
1878
1887
 
1879
1888
  rb_define_private_method(rb_cExternalFunction, "notify_v8", (VALUE(*)(...))&rb_external_function_notify_v8, 0);
1880
- rb_define_alloc_func(rb_cExternalFunction, allocate_external_function);
1881
1889
 
1882
1890
  rb_define_method(rb_cSnapshot, "size", (VALUE(*)(...))&rb_snapshot_size, 0);
1883
1891
  rb_define_method(rb_cSnapshot, "dump", (VALUE(*)(...))&rb_snapshot_dump, 0);
@@ -1899,9 +1907,5 @@ extern "C" {
1899
1907
  thread_attr_p = &attr;
1900
1908
  }
1901
1909
  }
1902
- auto on_fork_for_child = []() {
1903
- exit_lock = PTHREAD_RWLOCK_INITIALIZER;
1904
- };
1905
- pthread_atfork(nullptr, nullptr, on_fork_for_child);
1906
1910
  }
1907
1911
  }