mini_racer 0.2.12 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
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: []