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 +4 -4
- data/.travis.yml +0 -7
- data/CHANGELOG +31 -0
- data/README.md +11 -1
- data/ext/mini_racer_extension/extconf.rb +1 -0
- data/ext/mini_racer_extension/mini_racer_extension.cc +115 -86
- data/lib/mini_racer.rb +34 -19
- data/lib/mini_racer/version.rb +3 -1
- data/mini_racer.gemspec +2 -1
- metadata +26 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e370d4752aeb013f408e1df0b94521d25b81ea3fc576a53e28ecbe857bec7336
|
4
|
+
data.tar.gz: 5f8eae12d493d3c70ce07f32d41da6d847de31762d6d546e42d5148843112808
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9a7f5d67e29902e7fec4f516ff1037bb8c62da2dbda8cfa8604c0d0c59f3a222e37776c7d42f7cfb17bead48dc201c95209c4bcdd7f5202b0ba6d98e5678a5a2
|
7
|
+
data.tar.gz: 77cef34527ace5fa77c4c4ef138809cddf1238c3388db845e595f614653feac7e8c77a378162f73bac35d9d84899a1f52c2265e27264a8368771e797e6d86189
|
data/.travis.yml
CHANGED
@@ -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:
|
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
|
|
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(
|
241
|
-
|
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(
|
244
|
-
|
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
|
|
295
295
|
} else if(trycatch.HasTerminated()) {
|
296
296
|
evalRes.terminated = true;
|
297
297
|
evalRes.message = new Persistent<Value>();
|
298
|
-
Local<String> tmp = String::
|
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
|
-
|
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
|
|
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
|
-
|
404
|
-
|
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
|
|
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
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
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
|
-
|
440
|
-
//
|
441
|
-
|
442
|
-
|
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(
|
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
|
|
524
539
|
length = RARRAY_LEN(value);
|
525
540
|
array = Array::New(isolate, (int)length);
|
526
541
|
for(i=0; i<length; i++) {
|
527
|
-
|
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
|
|
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
|
-
|
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
|
|
563
578
|
case T_UNDEF:
|
564
579
|
case T_NODE:
|
565
580
|
default:
|
566
|
-
return scope.Escape(String::
|
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<
|
590
|
-
|
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
|
-
|
606
|
-
|
607
|
-
|
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
|
-
|
621
|
+
Local<v8::Context> context = v8::Context::New(isolate);
|
614
622
|
if (embedded_source != nullptr &&
|
615
|
-
!run_extra_code(isolate, context, embedded_source,
|
616
|
-
|
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
|
-
|
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(
|
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::
|
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
|
-
|
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
|
-
|
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
|
-
|
1161
|
-
|
1182
|
+
context,
|
1183
|
+
v8_str,
|
1184
|
+
FunctionTemplate::New(isolate, ruby_callback, external)
|
1162
1185
|
->GetFunction(context)
|
1163
1186
|
.ToLocalChecked());
|
1164
|
-
|
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
|
-
|
1195
|
-
|
1196
|
-
|
1197
|
-
|
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
|
-
|
1206
|
-
|
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
|
-
|
1230
|
+
isolate->Dispose();
|
1209
1231
|
}
|
1210
1232
|
}
|
1211
|
-
|
1233
|
+
isolate = nullptr;
|
1212
1234
|
}
|
1213
1235
|
|
1214
|
-
if (
|
1215
|
-
delete[]
|
1216
|
-
delete
|
1236
|
+
if (startup_data) {
|
1237
|
+
delete[] startup_data->data;
|
1238
|
+
delete startup_data;
|
1217
1239
|
}
|
1218
1240
|
|
1219
|
-
delete
|
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
|
-
|
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
|
-
|
1565
|
-
MaybeLocal<v8::Value> val
|
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);
|
data/lib/mini_racer.rb
CHANGED
@@ -130,23 +130,22 @@ module MiniRacer
|
|
130
130
|
end
|
131
131
|
end
|
132
132
|
|
133
|
-
def initialize(
|
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!(
|
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 =
|
143
|
-
|
144
|
-
|
145
|
-
end
|
142
|
+
@timeout = timeout
|
143
|
+
@max_memory = max_memory
|
144
|
+
|
146
145
|
# false signals it should be fetched if requested
|
147
|
-
@isolate =
|
146
|
+
@isolate = isolate || false
|
148
147
|
|
149
|
-
@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(
|
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
|
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
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
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
|
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!(
|
372
|
-
assert_option_is_nil_or_a('isolate',
|
373
|
-
assert_option_is_nil_or_a('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
|
-
|
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}"
|
data/lib/mini_racer/version.rb
CHANGED
data/mini_racer.gemspec
CHANGED
@@ -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', '
|
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.
|
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-
|
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:
|
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:
|
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.
|
112
|
-
documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.
|
113
|
-
source_code_uri: https://github.com/discourse/mini_racer/tree/v0.
|
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: []
|