mini_racer 0.2.12 → 0.3.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 35ced47bf0cd66e605b6bd14005c23381b9fa42e438b0ab45a3c99414d107634
4
- data.tar.gz: '02968be5850b866d1c399995cb6d942dc826730447524df008f604035449050e'
3
+ metadata.gz: e370d4752aeb013f408e1df0b94521d25b81ea3fc576a53e28ecbe857bec7336
4
+ data.tar.gz: 5f8eae12d493d3c70ce07f32d41da6d847de31762d6d546e42d5148843112808
5
5
  SHA512:
6
- metadata.gz: 3c4fc5f71bcfddd5d58f5b0ae0f68e4b59067005362afbf29f096752f63fab9ea0d0526f2ba02d5a85f89976e607eae395c3714b6186282a6bce19217ae86ec0
7
- data.tar.gz: 3d6268a03f472c01fa68278e4349c18b270765a8e831b020286a929b0fd0f2a1422b2c0c11432a33a348ec28044a38acdcd55d4cff93b436acc2fbed07ddfdf8
6
+ metadata.gz: 9a7f5d67e29902e7fec4f516ff1037bb8c62da2dbda8cfa8604c0d0c59f3a222e37776c7d42f7cfb17bead48dc201c95209c4bcdd7f5202b0ba6d98e5678a5a2
7
+ data.tar.gz: 77cef34527ace5fa77c4c4ef138809cddf1238c3388db845e595f614653feac7e8c77a378162f73bac35d9d84899a1f52c2265e27264a8368771e797e6d86189
@@ -1,6 +1,5 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.4
4
3
  - 2.5
5
4
  - 2.6
6
5
  - 2.7
@@ -10,12 +9,6 @@ matrix:
10
9
  - rvm: 2.5.1
11
10
  os: osx
12
11
  osx_image: xcode9.4
13
- - rvm: 2.4.0
14
- os: osx
15
- osx_image: xcode8.3
16
- - rvm: 2.4.0
17
- os: osx
18
- osx_image: xcode7.3
19
12
  dist: trusty
20
13
  sudo: true
21
14
  before_install:
data/CHANGELOG CHANGED
@@ -1,3 +1,34 @@
1
+ - 23-07-202
2
+
3
+ - 0.3.1
4
+
5
+ - FIX: specify that libv8 must be larger than 8.4.255 but smaller than 8.5, this avoids issues going forward
6
+
7
+ - 22-07-2020
8
+
9
+ - 0.3.0
10
+
11
+ - FEATURE: upgraded to libv8 version 8.4.255.0
12
+
13
+ - 29-06-2020
14
+
15
+ - 0.2.15
16
+
17
+ - FEATURE: basic wasm support via pump_message_loop
18
+
19
+ - 15-05-2020
20
+
21
+ - 0.2.14
22
+
23
+ - FIX: ensure_gc_after_idle should take in milliseconds like the rest of the APIs not seconds
24
+ - FEATURE: strict params on MiniRacer::Context.new
25
+
26
+ - 15-05-2020
27
+
28
+ - 0.2.13
29
+
30
+ - FIX: edge case around ensure_gc_after_idle possibly firing when context is not idle
31
+
1
32
  - 15-05-2020
2
33
 
3
34
  - 0.2.12
data/README.md CHANGED
@@ -239,7 +239,7 @@ This can come in handy to force V8 GC runs for example in between requests if yo
239
239
 
240
240
  Note that this method maps directly to [`v8::Isolate::IdleNotification`](http://bespin.cz/~ondras/html/classv8_1_1Isolate.html#aea16cbb2e351de9a3ae7be2b7cb48297), and that in particular its return value is the same (true if there is no further garbage to collect, false otherwise) and the same caveats apply, in particular that `there is no guarantee that the [call will return] within the time limit.`
241
241
 
242
- Additionally you may automate this process on a context by defining it with `MiniRacer::Content.new(ensure_gc_after_idle: 1)`. Using this will ensure V8 will run a full GC using `context.isolate.low_memory_notification` 1 second after the last eval on the context. Low memory notification is both slower and more aggressive than an idle_notification and will ensure long living isolates use minimal amounts of memory.
242
+ Additionally you may automate this process on a context by defining it with `MiniRacer::Content.new(ensure_gc_after_idle: 1000)`. Using this will ensure V8 will run a full GC using `context.isolate.low_memory_notification` 1 second after the last eval on the context. Low memory notification is both slower and more aggressive than an idle_notification and will ensure long living isolates use minimal amounts of memory.
243
243
 
244
244
  ### V8 Runtime flags
245
245
 
@@ -315,6 +315,16 @@ context.eval("a = 2")
315
315
  # nothing works on the context from now on, its a shell waiting to be disposed
316
316
  ```
317
317
 
318
+ A MiniRacer context can also be dumped in a heapsnapshot file using `#write_heap_snapshot(file_or_io)`
319
+
320
+ ```ruby
321
+ context = MiniRacer::Context.new(timeout: 5)
322
+ context.eval("let a='testing';")
323
+ context.write_heap_snapshot("test.heapsnapshot")
324
+ ```
325
+
326
+ This file can then be loaded in the memory tab of the chrome dev console.
327
+
318
328
  ### Function call
319
329
 
320
330
  This calls the function passed as first argument:
@@ -11,6 +11,7 @@ $CPPFLAGS += " -rdynamic" unless $CPPFLAGS.split.include? "-rdynamic"
11
11
  $CPPFLAGS += " -fPIC" unless $CPPFLAGS.split.include? "-rdynamic" or IS_DARWIN
12
12
  $CPPFLAGS += " -std=c++0x"
13
13
  $CPPFLAGS += " -fpermissive"
14
+ $CPPFLAGS += " -DV8_COMPRESS_POINTERS"
14
15
 
15
16
  $CPPFLAGS += " -Wno-reserved-user-defined-literal" if IS_DARWIN
16
17
 
@@ -25,6 +25,7 @@ public:
25
25
  ArrayBuffer::Allocator* allocator;
26
26
  StartupData* startup_data;
27
27
  bool interrupted;
28
+ bool added_gc_cb;
28
29
  pid_t pid;
29
30
  VALUE mutex;
30
31
 
@@ -42,15 +43,12 @@ public:
42
43
 
43
44
 
44
45
  IsolateInfo() : isolate(nullptr), allocator(nullptr), startup_data(nullptr),
45
- interrupted(false), pid(getpid()), refs_count(0) {
46
+ interrupted(false), added_gc_cb(false), pid(getpid()), refs_count(0) {
46
47
  VALUE cMutex = rb_const_get(rb_cThread, rb_intern("Mutex"));
47
48
  mutex = rb_class_new_instance(0, nullptr, cMutex);
48
49
  }
49
50
 
50
- ~IsolateInfo() {
51
- void free_isolate(IsolateInfo*);
52
- free_isolate(this);
53
- }
51
+ ~IsolateInfo();
54
52
 
55
53
  void init(SnapshotInfo* snapshot_info = nullptr);
56
54
 
@@ -237,11 +235,13 @@ static void prepare_result(MaybeLocal v8res,
237
235
  Local<Value> local_value = v8res.ToLocalChecked();
238
236
  if ((local_value->IsObject() || local_value->IsArray()) &&
239
237
  !local_value->IsDate() && !local_value->IsFunction()) {
240
- Local<Object> JSON = context->Global()->Get(String::NewFromUtf8(isolate, "JSON"))
241
- ->ToObject(context).ToLocalChecked();
238
+ Local<Object> JSON = context->Global()->Get(
239
+ context, String::NewFromUtf8Literal(isolate, "JSON"))
240
+ .ToLocalChecked().As<Object>();
242
241
 
243
- Local<Function> stringify = JSON->Get(v8::String::NewFromUtf8(isolate, "stringify"))
244
- .As<Function>();
242
+ Local<Function> stringify = JSON->Get(
243
+ context, v8::String::NewFromUtf8Literal(isolate, "stringify"))
244
+ .ToLocalChecked().As<Function>();
245
245
 
246
246
  Local<Object> object = local_value->ToObject(context).ToLocalChecked();
247
247
  const unsigned argc = 1;
@@ -295,7 +295,7 @@ static void prepare_result(MaybeLocal v8res,
295
295
  } else if(trycatch.HasTerminated()) {
296
296
  evalRes.terminated = true;
297
297
  evalRes.message = new Persistent<Value>();
298
- Local<String> tmp = String::NewFromUtf8(isolate, "JavaScript was terminated (either by timeout or explicitly)");
298
+ Local<String> tmp = String::NewFromUtf8Literal(isolate, "JavaScript was terminated (either by timeout or explicitly)");
299
299
  evalRes.message->Reset(isolate, tmp);
300
300
  }
301
301
  if (!trycatch.StackTrace(context).IsEmpty()) {
@@ -312,7 +312,8 @@ nogvl_context_eval(void* arg) {
312
312
 
313
313
  EvalParams* eval_params = (EvalParams*)arg;
314
314
  EvalResult* result = eval_params->result;
315
- Isolate* isolate = eval_params->context_info->isolate_info->isolate;
315
+ IsolateInfo* isolate_info = eval_params->context_info->isolate_info;
316
+ Isolate* isolate = isolate_info->isolate;
316
317
 
317
318
  Isolate::Scope isolate_scope(isolate);
318
319
  HandleScope handle_scope(isolate);
@@ -356,7 +357,10 @@ nogvl_context_eval(void* arg) {
356
357
  // parsing successful
357
358
  if (eval_params->max_memory > 0) {
358
359
  isolate->SetData(MEM_SOFTLIMIT_VALUE, &eval_params->max_memory);
360
+ if (!isolate_info->added_gc_cb) {
359
361
  isolate->AddGCEpilogueCallback(gc_callback);
362
+ isolate_info->added_gc_cb = true;
363
+ }
360
364
  }
361
365
 
362
366
  maybe_value = parsed_script.ToLocalChecked()->Run(context);
@@ -369,6 +373,12 @@ nogvl_context_eval(void* arg) {
369
373
  return NULL;
370
374
  }
371
375
 
376
+ static VALUE new_empty_failed_conv_obj() {
377
+ // TODO isolate code that translates execption to ruby
378
+ // exception so we can properly return it
379
+ return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
380
+ }
381
+
372
382
  // assumes isolate locking is in place
373
383
  static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
374
384
  Local<Value> value) {
@@ -400,8 +410,11 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local context,
400
410
  VALUE rb_array = rb_ary_new();
401
411
  Local<Array> arr = Local<Array>::Cast(value);
402
412
  for(uint32_t i=0; i < arr->Length(); i++) {
403
- Local<Value> element = arr->Get(i);
404
- VALUE rb_elem = convert_v8_to_ruby(isolate, context, element);
413
+ MaybeLocal<Value> element = arr->Get(context, i);
414
+ if (element.IsEmpty()) {
415
+ continue;
416
+ }
417
+ VALUE rb_elem = convert_v8_to_ruby(isolate, context, element.ToLocalChecked());
405
418
  if (rb_funcall(rb_elem, rb_intern("class"), 0) == rb_cFailedV8Conversion) {
406
419
  return rb_elem;
407
420
  }
@@ -431,18 +444,20 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local context,
431
444
  if (!maybe_props.IsEmpty()) {
432
445
  Local<Array> props = maybe_props.ToLocalChecked();
433
446
  for(uint32_t i=0; i < props->Length(); i++) {
434
- Local<Value> key = props->Get(i);
435
- VALUE rb_key = convert_v8_to_ruby(isolate, context, key);
436
- Local<Value> prop_value = object->Get(key);
437
- // this may have failed due to Get raising
447
+ MaybeLocal<Value> key = props->Get(context, i);
448
+ if (key.IsEmpty()) {
449
+ return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
450
+ }
451
+ VALUE rb_key = convert_v8_to_ruby(isolate, context, key.ToLocalChecked());
438
452
 
439
- if (trycatch.HasCaught()) {
440
- // TODO isolate code that translates execption to ruby
441
- // exception so we can properly return it
442
- return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
453
+ MaybeLocal<Value> prop_value = object->Get(context, key.ToLocalChecked());
454
+ // this may have failed due to Get raising
455
+ if (prop_value.IsEmpty() || trycatch.HasCaught()) {
456
+ return new_empty_failed_conv_obj();
443
457
  }
444
458
 
445
- VALUE rb_value = convert_v8_to_ruby(isolate, context, prop_value);
459
+ VALUE rb_value = convert_v8_to_ruby(
460
+ isolate, context, prop_value.ToLocalChecked());
446
461
  rb_hash_aset(rb_hash, rb_key, rb_value);
447
462
  }
448
463
  }
@@ -524,7 +539,7 @@ static Local convert_ruby_to_v8(Isolate* isolate, Local context,
524
539
  length = RARRAY_LEN(value);
525
540
  array = Array::New(isolate, (int)length);
526
541
  for(i=0; i<length; i++) {
527
- array->Set(i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
542
+ array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
528
543
  }
529
544
  return scope.Escape(array);
530
545
  case T_HASH:
@@ -533,7 +548,7 @@ static Local convert_ruby_to_v8(Isolate* isolate, Local context,
533
548
  length = RARRAY_LEN(hash_as_array);
534
549
  for(i=0; i<length; i++) {
535
550
  pair = rb_ary_entry(hash_as_array, i);
536
- object->Set(convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
551
+ object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
537
552
  convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 1)));
538
553
  }
539
554
  return scope.Escape(object);
@@ -563,9 +578,9 @@ static Local convert_ruby_to_v8(Isolate* isolate, Local context,
563
578
  case T_UNDEF:
564
579
  case T_NODE:
565
580
  default:
566
- return scope.Escape(String::NewFromUtf8(isolate, "Undefined Conversion"));
581
+ return scope.Escape(String::NewFromUtf8Literal(isolate, "Undefined Conversion"));
582
+ }
567
583
  }
568
- }
569
584
 
570
585
  static void unblock_eval(void *ptr) {
571
586
  EvalParams* eval = (EvalParams*)ptr;
@@ -576,53 +591,43 @@ static void unblock_eval(void *ptr) {
576
591
  * The implementations of the run_extra_code(), create_snapshot_data_blob() and
577
592
  * warm_up_snapshot_data_blob() functions have been derived from V8's test suite.
578
593
  */
579
- bool run_extra_code(Isolate *isolate, Local<v8::Context> context,
594
+ static bool run_extra_code(Isolate *isolate, Local<v8::Context> context,
580
595
  const char *utf8_source, const char *name) {
581
596
  Context::Scope context_scope(context);
582
597
  TryCatch try_catch(isolate);
583
598
  Local<String> source_string;
584
- if (!String::NewFromUtf8(isolate, utf8_source,
585
- NewStringType::kNormal)
586
- .ToLocal(&source_string)) {
599
+ if (!String::NewFromUtf8(isolate, utf8_source).ToLocal(&source_string)) {
587
600
  return false;
588
601
  }
589
- Local<v8::String> resource_name =
590
- String::NewFromUtf8(isolate, name, NewStringType::kNormal)
591
- .ToLocalChecked();
602
+ Local<String> resource_name =
603
+ String::NewFromUtf8(isolate, name).ToLocalChecked();
592
604
  ScriptOrigin origin(resource_name);
593
605
  ScriptCompiler::Source source(source_string, origin);
594
606
  Local<Script> script;
595
607
  if (!ScriptCompiler::Compile(context, &source).ToLocal(&script))
596
608
  return false;
597
- if (script->Run(context).IsEmpty())
598
- return false;
599
- // CHECK(!try_catch.HasCaught());
609
+ if (script->Run(context).IsEmpty()) return false;
600
610
  return true;
601
611
  }
602
612
 
603
- StartupData
613
+ static StartupData
604
614
  create_snapshot_data_blob(const char *embedded_source = nullptr) {
605
- // Create a new isolate and a new context from scratch, optionally run
606
- // a script to embed, and serialize to create a snapshot blob.
607
- StartupData result = {nullptr, 0};
608
- {
609
- SnapshotCreator snapshot_creator;
610
- Isolate *isolate = snapshot_creator.GetIsolate();
615
+ Isolate *isolate = Isolate::Allocate();
616
+
617
+ // Optionally run a script to embed, and serialize to create a snapshot blob.
618
+ SnapshotCreator snapshot_creator(isolate);
611
619
  {
612
620
  HandleScope scope(isolate);
613
- Local<Context> context = Context::New(isolate);
621
+ Local<v8::Context> context = v8::Context::New(isolate);
614
622
  if (embedded_source != nullptr &&
615
- !run_extra_code(isolate, context, embedded_source,
616
- "<embedded>")) {
617
- return result;
623
+ !run_extra_code(isolate, context, embedded_source, "<embedded>")) {
624
+ return {};
618
625
  }
619
626
  snapshot_creator.SetDefaultContext(context);
620
627
  }
621
- result = snapshot_creator.CreateBlob(
628
+ return snapshot_creator.CreateBlob(
622
629
  SnapshotCreator::FunctionCodeHandling::kClear);
623
630
  }
624
- return result;
625
- }
626
631
 
627
632
  StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
628
633
  const char *warmup_source) {
@@ -779,6 +784,19 @@ static VALUE rb_isolate_low_memory_notification(VALUE self) {
779
784
  return Qnil;
780
785
  }
781
786
 
787
+ static VALUE rb_isolate_pump_message_loop(VALUE self) {
788
+ IsolateInfo* isolate_info;
789
+ Data_Get_Struct(self, IsolateInfo, isolate_info);
790
+
791
+ if (current_platform == NULL) return Qfalse;
792
+
793
+ if (platform::PumpMessageLoop(current_platform.get(), isolate_info->isolate)){
794
+ return Qtrue;
795
+ } else {
796
+ return Qfalse;
797
+ }
798
+ }
799
+
782
800
  static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
783
801
  ContextInfo* context_info;
784
802
  Data_Get_Struct(self, ContextInfo, context_info);
@@ -1049,7 +1067,9 @@ gvl_ruby_callback(void* data) {
1049
1067
  callback_data.failed = false;
1050
1068
 
1051
1069
  if ((bool)args->GetIsolate()->GetData(DO_TERMINATE) == true) {
1052
- args->GetIsolate()->ThrowException(String::NewFromUtf8(args->GetIsolate(), "Terminated execution during transition from Ruby to JS"));
1070
+ args->GetIsolate()->ThrowException(
1071
+ String::NewFromUtf8Literal(args->GetIsolate(),
1072
+ "Terminated execution during transition from Ruby to JS"));
1053
1073
  args->GetIsolate()->TerminateExecution();
1054
1074
  if (length > 0) {
1055
1075
  rb_ary_clear(ruby_args);
@@ -1063,7 +1083,7 @@ gvl_ruby_callback(void* data) {
1063
1083
 
1064
1084
  if(callback_data.failed) {
1065
1085
  rb_iv_set(parent, "@current_exception", result);
1066
- args->GetIsolate()->ThrowException(String::NewFromUtf8(args->GetIsolate(), "Ruby exception"));
1086
+ args->GetIsolate()->ThrowException(String::NewFromUtf8Literal(args->GetIsolate(), "Ruby exception"));
1067
1087
  }
1068
1088
  else {
1069
1089
  HandleScope scope(args->GetIsolate());
@@ -1134,7 +1154,9 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1134
1154
 
1135
1155
  if (parent_object == Qnil) {
1136
1156
  context->Global()->Set(
1137
- v8_str, FunctionTemplate::New(isolate, ruby_callback, external)
1157
+ context,
1158
+ v8_str,
1159
+ FunctionTemplate::New(isolate, ruby_callback, external)
1138
1160
  ->GetFunction(context)
1139
1161
  .ToLocalChecked());
1140
1162
 
@@ -1147,7 +1169,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1147
1169
 
1148
1170
  MaybeLocal<Script> parsed_script = Script::Compile(context, eval);
1149
1171
  if (parsed_script.IsEmpty()) {
1150
- parse_error = true;
1172
+ parse_error = true;
1151
1173
  } else {
1152
1174
  MaybeLocal<Value> maybe_value =
1153
1175
  parsed_script.ToLocalChecked()->Run(context);
@@ -1157,11 +1179,12 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1157
1179
  Local<Value> value = maybe_value.ToLocalChecked();
1158
1180
  if (value->IsObject()) {
1159
1181
  value.As<Object>()->Set(
1160
- v8_str, FunctionTemplate::New(
1161
- isolate, ruby_callback, external)
1182
+ context,
1183
+ v8_str,
1184
+ FunctionTemplate::New(isolate, ruby_callback, external)
1162
1185
  ->GetFunction(context)
1163
1186
  .ToLocalChecked());
1164
- attach_error = false;
1187
+ attach_error = false;
1165
1188
  }
1166
1189
  }
1167
1190
  }
@@ -1191,32 +1214,31 @@ static VALUE rb_context_isolate_mutex(VALUE self) {
1191
1214
  return context_info->isolate_info->mutex;
1192
1215
  }
1193
1216
 
1194
- void free_isolate(IsolateInfo* isolate_info) {
1195
-
1196
- if (isolate_info->isolate) {
1197
- Locker lock(isolate_info->isolate);
1198
- }
1199
-
1200
- if (isolate_info->isolate) {
1201
- if (isolate_info->interrupted) {
1202
- fprintf(stderr, "WARNING: V8 isolate was interrupted by Ruby, it can not be disposed and memory will not be reclaimed till the Ruby process exits.\n");
1217
+ IsolateInfo::~IsolateInfo() {
1218
+ if (isolate) {
1219
+ if (this->interrupted) {
1220
+ fprintf(stderr, "WARNING: V8 isolate was interrupted by Ruby, "
1221
+ "it can not be disposed and memory will not be "
1222
+ "reclaimed till the Ruby process exits.\n");
1203
1223
  } else {
1204
-
1205
- if (isolate_info->pid != getpid()) {
1206
- fprintf(stderr, "WARNING: V8 isolate was forked, it can not be disposed and memory will not be reclaimed till the Ruby process exits.\n");
1224
+ if (this->pid != getpid()) {
1225
+ fprintf(stderr, "WARNING: V8 isolate was forked, "
1226
+ "it can not be disposed and "
1227
+ "memory will not be reclaimed "
1228
+ "till the Ruby process exits.\n");
1207
1229
  } else {
1208
- isolate_info->isolate->Dispose();
1230
+ isolate->Dispose();
1209
1231
  }
1210
1232
  }
1211
- isolate_info->isolate = NULL;
1233
+ isolate = nullptr;
1212
1234
  }
1213
1235
 
1214
- if (isolate_info->startup_data) {
1215
- delete[] isolate_info->startup_data->data;
1216
- delete isolate_info->startup_data;
1236
+ if (startup_data) {
1237
+ delete[] startup_data->data;
1238
+ delete startup_data;
1217
1239
  }
1218
1240
 
1219
- delete isolate_info->allocator;
1241
+ delete allocator;
1220
1242
  }
1221
1243
 
1222
1244
  static void free_context_raw(void *arg) {
@@ -1288,7 +1310,7 @@ static void mark_isolate(void* data) {
1288
1310
  isolate_info->mark();
1289
1311
  }
1290
1312
 
1291
- void deallocate(void* data) {
1313
+ static void deallocate(void* data) {
1292
1314
  ContextInfo* context_info = (ContextInfo*)data;
1293
1315
 
1294
1316
  free_context(context_info);
@@ -1303,22 +1325,22 @@ static void mark_context(void* data) {
1303
1325
  }
1304
1326
  }
1305
1327
 
1306
- void deallocate_external_function(void * data) {
1328
+ static void deallocate_external_function(void * data) {
1307
1329
  xfree(data);
1308
1330
  }
1309
1331
 
1310
- void deallocate_snapshot(void * data) {
1332
+ static void deallocate_snapshot(void * data) {
1311
1333
  SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
1312
1334
  delete[] snapshot_info->data;
1313
1335
  xfree(snapshot_info);
1314
1336
  }
1315
1337
 
1316
- VALUE allocate_external_function(VALUE klass) {
1338
+ static VALUE allocate_external_function(VALUE klass) {
1317
1339
  VALUE* self = ALLOC(VALUE);
1318
1340
  return Data_Wrap_Struct(klass, NULL, deallocate_external_function, (void*)self);
1319
1341
  }
1320
1342
 
1321
- VALUE allocate(VALUE klass) {
1343
+ static VALUE allocate(VALUE klass) {
1322
1344
  ContextInfo* context_info = ALLOC(ContextInfo);
1323
1345
  context_info->isolate_info = NULL;
1324
1346
  context_info->context = NULL;
@@ -1326,7 +1348,7 @@ VALUE allocate(VALUE klass) {
1326
1348
  return Data_Wrap_Struct(klass, mark_context, deallocate, (void*)context_info);
1327
1349
  }
1328
1350
 
1329
- VALUE allocate_snapshot(VALUE klass) {
1351
+ static VALUE allocate_snapshot(VALUE klass) {
1330
1352
  SnapshotInfo* snapshot_info = ALLOC(SnapshotInfo);
1331
1353
  snapshot_info->data = NULL;
1332
1354
  snapshot_info->raw_size = 0;
@@ -1334,7 +1356,7 @@ VALUE allocate_snapshot(VALUE klass) {
1334
1356
  return Data_Wrap_Struct(klass, NULL, deallocate_snapshot, (void*)snapshot_info);
1335
1357
  }
1336
1358
 
1337
- VALUE allocate_isolate(VALUE klass) {
1359
+ static VALUE allocate_isolate(VALUE klass) {
1338
1360
  IsolateInfo* isolate_info = new IsolateInfo();
1339
1361
 
1340
1362
  return Data_Wrap_Struct(klass, mark_isolate, deallocate_isolate, (void*)isolate_info);
@@ -1473,7 +1495,8 @@ nogvl_context_call(void *args) {
1473
1495
  if (!call) {
1474
1496
  return NULL;
1475
1497
  }
1476
- Isolate* isolate = call->context_info->isolate_info->isolate;
1498
+ IsolateInfo *isolate_info = call->context_info->isolate_info;
1499
+ Isolate* isolate = isolate_info->isolate;
1477
1500
 
1478
1501
  // in gvl flag
1479
1502
  isolate->SetData(IN_GVL, (void*)false);
@@ -1483,7 +1506,10 @@ nogvl_context_call(void *args) {
1483
1506
  if (call->max_memory > 0) {
1484
1507
  isolate->SetData(MEM_SOFTLIMIT_VALUE, &call->max_memory);
1485
1508
  isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)false);
1509
+ if (!isolate_info->added_gc_cb) {
1486
1510
  isolate->AddGCEpilogueCallback(gc_callback);
1511
+ isolate_info->added_gc_cb = true;
1512
+ }
1487
1513
  }
1488
1514
 
1489
1515
  Isolate::Scope isolate_scope(isolate);
@@ -1561,8 +1587,11 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1561
1587
 
1562
1588
  // examples of such usage can be found in
1563
1589
  // https://github.com/v8/v8/blob/36b32aa28db5e993312f4588d60aad5c8330c8a5/test/cctest/test-api.cc#L15711
1564
- Local<String> fname = String::NewFromUtf8(isolate, call.function_name);
1565
- MaybeLocal<v8::Value> val = context->Global()->Get(fname);
1590
+ MaybeLocal<String> fname = String::NewFromUtf8(isolate, call.function_name);
1591
+ MaybeLocal<v8::Value> val;
1592
+ if (!fname.IsEmpty()) {
1593
+ val = context->Global()->Get(context, fname.ToLocalChecked());
1594
+ }
1566
1595
 
1567
1596
  if (val.IsEmpty() || !val.ToLocalChecked()->IsFunction()) {
1568
1597
  missingFunction = true;
@@ -1668,7 +1697,7 @@ extern "C" {
1668
1697
 
1669
1698
  rb_define_method(rb_cIsolate, "idle_notification", (VALUE(*)(...))&rb_isolate_idle_notification, 1);
1670
1699
  rb_define_method(rb_cIsolate, "low_memory_notification", (VALUE(*)(...))&rb_isolate_low_memory_notification, 0);
1671
-
1700
+ rb_define_method(rb_cIsolate, "pump_message_loop", (VALUE(*)(...))&rb_isolate_pump_message_loop, 0);
1672
1701
  rb_define_private_method(rb_cIsolate, "init_with_snapshot",(VALUE(*)(...))&rb_isolate_init_with_snapshot, 1);
1673
1702
 
1674
1703
  rb_define_singleton_method(rb_cPlatform, "set_flag_as_str!", (VALUE(*)(...))&rb_platform_set_flag_as_str, 1);
@@ -130,23 +130,22 @@ module MiniRacer
130
130
  end
131
131
  end
132
132
 
133
- def initialize(options = nil)
133
+ def initialize(max_memory: nil, timeout: nil, isolate: nil, ensure_gc_after_idle: nil, snapshot: nil)
134
134
  options ||= {}
135
135
 
136
- check_init_options!(options)
136
+ check_init_options!(isolate: isolate, snapshot: snapshot, max_memory: max_memory, ensure_gc_after_idle: ensure_gc_after_idle, timeout: timeout)
137
137
 
138
138
  @functions = {}
139
139
  @timeout = nil
140
140
  @max_memory = nil
141
141
  @current_exception = nil
142
- @timeout = options[:timeout]
143
- if options[:max_memory].is_a?(Numeric) && options[:max_memory] > 0
144
- @max_memory = options[:max_memory]
145
- end
142
+ @timeout = timeout
143
+ @max_memory = max_memory
144
+
146
145
  # false signals it should be fetched if requested
147
- @isolate = options[:isolate] || false
146
+ @isolate = isolate || false
148
147
 
149
- @ensure_gc_after_idle = options[:ensure_gc_after_idle]
148
+ @ensure_gc_after_idle = ensure_gc_after_idle
150
149
 
151
150
  if @ensure_gc_after_idle
152
151
  @last_eval = nil
@@ -162,7 +161,7 @@ module MiniRacer
162
161
  @eval_thread = nil
163
162
 
164
163
  # defined in the C class
165
- init_unsafe(options[:isolate], options[:snapshot])
164
+ init_unsafe(isolate, snapshot)
166
165
  end
167
166
 
168
167
  def isolate
@@ -290,6 +289,7 @@ module MiniRacer
290
289
  @ensure_gc_mutex.synchronize do
291
290
  @ensure_gc_thread = nil if !@ensure_gc_thread&.alive?
292
291
  @ensure_gc_thread ||= Thread.new do
292
+ ensure_gc_after_idle_seconds = @ensure_gc_after_idle / 1000.0
293
293
  done = false
294
294
  while !done
295
295
  now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
@@ -299,17 +299,18 @@ module MiniRacer
299
299
  break
300
300
  end
301
301
 
302
- if @ensure_gc_after_idle < now - @last_eval
302
+ if !@eval_thread && ensure_gc_after_idle_seconds < now - @last_eval
303
303
  @ensure_gc_mutex.synchronize do
304
304
  isolate_mutex.synchronize do
305
- # extra 50ms to make sure that we really have enough time
306
- isolate.low_memory_notification if !@disposed
307
- @ensure_gc_thread = nil
308
- done = true
305
+ if !@eval_thread
306
+ isolate.low_memory_notification if !@disposed
307
+ @ensure_gc_thread = nil
308
+ done = true
309
+ end
309
310
  end
310
311
  end
311
312
  end
312
- sleep @ensure_gc_after_idle if !done
313
+ sleep ensure_gc_after_idle_seconds if !done
313
314
  end
314
315
  end
315
316
  end
@@ -368,15 +369,29 @@ module MiniRacer
368
369
  rp.close if rp
369
370
  end
370
371
 
371
- def check_init_options!(options)
372
- assert_option_is_nil_or_a('isolate', options[:isolate], Isolate)
373
- assert_option_is_nil_or_a('snapshot', options[:snapshot], Snapshot)
372
+ def check_init_options!(isolate:, snapshot:, max_memory:, ensure_gc_after_idle:, timeout:)
373
+ assert_option_is_nil_or_a('isolate', isolate, Isolate)
374
+ assert_option_is_nil_or_a('snapshot', snapshot, Snapshot)
374
375
 
375
- if options[:isolate] && options[:snapshot]
376
+ assert_numeric_or_nil('max_memory', max_memory, min_value: 10_000)
377
+ assert_numeric_or_nil('ensure_gc_after_idle', ensure_gc_after_idle, min_value: 1)
378
+ assert_numeric_or_nil('timeout', timeout, min_value: 1)
379
+
380
+ if isolate && snapshot
376
381
  raise ArgumentError, 'can only pass one of isolate and snapshot options'
377
382
  end
378
383
  end
379
384
 
385
+ def assert_numeric_or_nil(option_name, object, min_value:)
386
+ if object.is_a?(Numeric) && object < min_value
387
+ raise ArgumentError, "#{option_name} must be larger than #{min_value}"
388
+ end
389
+
390
+ if !object.nil? && !object.is_a?(Numeric)
391
+ raise ArgumentError, "#{option_name} must be a number, passed a #{object.inspect}"
392
+ end
393
+ end
394
+
380
395
  def assert_option_is_nil_or_a(option_name, object, klass)
381
396
  unless object.nil? || object.is_a?(klass)
382
397
  raise ArgumentError, "#{option_name} must be a #{klass} object, passed a #{object.inspect}"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module MiniRacer
2
- VERSION = "0.2.12"
4
+ VERSION = "0.3.1"
3
5
  end
@@ -30,8 +30,9 @@ Gem::Specification.new do |spec|
30
30
  spec.add_development_dependency "rake", ">= 12.3.3"
31
31
  spec.add_development_dependency "minitest", "~> 5.0"
32
32
  spec.add_development_dependency "rake-compiler"
33
+ spec.add_development_dependency "m"
33
34
 
34
- spec.add_dependency 'libv8', '> 7.3'
35
+ spec.add_dependency 'libv8', '~> 8.4.255'
35
36
  spec.require_paths = ["lib", "ext"]
36
37
 
37
38
  spec.extensions = ["ext/mini_racer_extension/extconf.rb"]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mini_racer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.12
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Saffron
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-05-15 00:00:00.000000000 Z
11
+ date: 2020-07-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -66,20 +66,34 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: m
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: libv8
71
85
  requirement: !ruby/object:Gem::Requirement
72
86
  requirements:
73
- - - ">"
87
+ - - "~>"
74
88
  - !ruby/object:Gem::Version
75
- version: '7.3'
89
+ version: 8.4.255
76
90
  type: :runtime
77
91
  prerelease: false
78
92
  version_requirements: !ruby/object:Gem::Requirement
79
93
  requirements:
80
- - - ">"
94
+ - - "~>"
81
95
  - !ruby/object:Gem::Version
82
- version: '7.3'
96
+ version: 8.4.255
83
97
  description: Minimal embedded v8 engine for Ruby
84
98
  email:
85
99
  - sam.saffron@gmail.com
@@ -108,10 +122,10 @@ licenses:
108
122
  - MIT
109
123
  metadata:
110
124
  bug_tracker_uri: https://github.com/discourse/mini_racer/issues
111
- changelog_uri: https://github.com/discourse/mini_racer/blob/v0.2.12/CHANGELOG
112
- documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.2.12
113
- source_code_uri: https://github.com/discourse/mini_racer/tree/v0.2.12
114
- post_install_message:
125
+ changelog_uri: https://github.com/discourse/mini_racer/blob/v0.3.1/CHANGELOG
126
+ documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.3.1
127
+ source_code_uri: https://github.com/discourse/mini_racer/tree/v0.3.1
128
+ post_install_message:
115
129
  rdoc_options: []
116
130
  require_paths:
117
131
  - lib
@@ -128,7 +142,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
128
142
  version: '0'
129
143
  requirements: []
130
144
  rubygems_version: 3.0.3
131
- signing_key:
145
+ signing_key:
132
146
  specification_version: 4
133
147
  summary: Minimal embedded v8 for Ruby
134
148
  test_files: []