sq_mini_racer 0.2.5.0.1.beta3 → 0.3.1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.dockerignore +12 -0
- data/.gitignore +9 -0
- data/CHANGELOG +73 -0
- data/Dockerfile +22 -0
- data/README.md +17 -2
- data/Rakefile +44 -62
- data/azure-pipelines.yml +69 -0
- data/azure-template.yml +101 -0
- data/ext/mini_racer_extension/extconf.rb +30 -16
- data/ext/mini_racer_extension/mini_racer_extension.cc +239 -97
- data/ext/mini_racer_loader/extconf.rb +8 -0
- data/ext/{prv_ext_loader/prv_ext_loader.c → mini_racer_loader/mini_racer_loader.c} +24 -5
- data/lib/sqreen/mini_racer.rb +90 -24
- data/lib/sqreen/mini_racer/version.rb +3 -3
- data/mini_racer.gemspec +14 -6
- data/valgrind.supp +74 -0
- metadata +61 -20
- data/.travis.yml +0 -73
- data/ext/prv_ext_loader/extconf.rb +0 -5
@@ -26,37 +26,43 @@ def cppflags_add_cpu_extension!
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def libv8_gem_name
|
29
|
-
|
30
|
-
return "libv8-alpine" if IS_LINUX_MUSL
|
31
|
-
|
32
|
-
'libv8'
|
29
|
+
'libv8-node'
|
33
30
|
end
|
34
31
|
|
35
32
|
def libv8_version
|
36
|
-
'
|
33
|
+
'14.14.0.0.beta2'
|
37
34
|
end
|
38
35
|
|
39
36
|
def libv8_basename
|
40
37
|
"#{libv8_gem_name}-#{libv8_version}-#{ruby_platform}"
|
41
38
|
end
|
42
39
|
|
40
|
+
def libv8_gemspec_no_libc
|
41
|
+
platform_no_libc = ruby_platform.to_s.split('-')[0..1].join('-')
|
42
|
+
"#{libv8_gem_name}-#{libv8_version}-#{platform_no_libc}.gemspec"
|
43
|
+
end
|
44
|
+
|
43
45
|
def libv8_gemspec
|
44
46
|
"#{libv8_basename}.gemspec"
|
45
47
|
end
|
46
48
|
|
47
49
|
def libv8_local_path(path=Gem.path)
|
48
|
-
|
49
|
-
|
50
|
+
gemspecs = [libv8_gemspec, libv8_gemspec_no_libc].uniq
|
51
|
+
puts "looking for #{gemspecs.join(', ')} in installed gems"
|
52
|
+
candidates = path.product(gemspecs)
|
53
|
+
.map { |(p, gemspec)| File.join(p, 'specifications', gemspec) }
|
54
|
+
p candidates
|
50
55
|
found = candidates.select { |f| File.exist?(f) }.first
|
51
56
|
|
52
57
|
unless found
|
53
|
-
puts "#{
|
58
|
+
puts "#{gemspecs.join(', ')} not found in installed gems"
|
54
59
|
return
|
55
60
|
end
|
56
61
|
|
57
62
|
puts "found in installed specs: #{found}"
|
58
63
|
|
59
|
-
|
64
|
+
gemdir = File.basename(found, '.gemspec')
|
65
|
+
dir = File.expand_path(File.join(found, '..', '..', 'gems', gemdir))
|
60
66
|
|
61
67
|
unless Dir.exist?(dir)
|
62
68
|
puts "not found in installed gems: #{dir}"
|
@@ -81,8 +87,8 @@ def libv8_vendor_path
|
|
81
87
|
return
|
82
88
|
end
|
83
89
|
|
84
|
-
puts "looking for #{libv8_basename}/lib/libv8.rb in #{vendor_path}"
|
85
|
-
unless Dir.glob(File.join(vendor_path, libv8_basename, 'lib', 'libv8.rb')).first
|
90
|
+
puts "looking for #{libv8_basename}/lib/libv8-node.rb in #{vendor_path}"
|
91
|
+
unless Dir.glob(File.join(vendor_path, libv8_basename, 'lib', 'libv8-node.rb')).first
|
86
92
|
puts "#{libv8_basename}/lib/libv8.rb not found in #{vendor_path}"
|
87
93
|
return
|
88
94
|
end
|
@@ -92,7 +98,6 @@ end
|
|
92
98
|
|
93
99
|
def parse_platform(str)
|
94
100
|
Gem::Platform.new(str).tap do |p|
|
95
|
-
p.instance_eval { @os = 'linux-musl' } if str =~ /musl/
|
96
101
|
p.instance_eval { @cpu = 'x86_64' } if str =~ /universal.*darwin/
|
97
102
|
end
|
98
103
|
end
|
@@ -119,7 +124,7 @@ def libv8_remote_search
|
|
119
124
|
Gem::Version.new(v['number']) == Gem::Version.new(libv8_version)
|
120
125
|
end
|
121
126
|
abort(<<-ERROR) if versions.empty?
|
122
|
-
ERROR: could not find #{libv8_version}
|
127
|
+
ERROR: could not find #{libv8_gem_name} (version #{libv8_version}) in rubygems.org
|
123
128
|
ERROR
|
124
129
|
|
125
130
|
platform_versions = versions.select do |v|
|
@@ -177,7 +182,10 @@ end
|
|
177
182
|
def ensure_libv8_load_path
|
178
183
|
puts "detected platform #{RUBY_PLATFORM} => #{ruby_platform}"
|
179
184
|
|
180
|
-
libv8_path = libv8_local_path
|
185
|
+
libv8_path = libv8_local_path
|
186
|
+
unless ENV['ONLY_INSTALLED_LIBV8_GEM']
|
187
|
+
libv8_path ||= libv8_vendor_path || libv8_vendor!
|
188
|
+
end
|
181
189
|
|
182
190
|
abort(<<-ERROR) unless libv8_path
|
183
191
|
ERROR: could not find #{libv8_gem_name}
|
@@ -189,7 +197,7 @@ end
|
|
189
197
|
|
190
198
|
ensure_libv8_load_path
|
191
199
|
|
192
|
-
require 'libv8'
|
200
|
+
require 'libv8-node'
|
193
201
|
|
194
202
|
IS_DARWIN = RUBY_PLATFORM =~ /darwin/
|
195
203
|
|
@@ -202,17 +210,23 @@ $CPPFLAGS += " -rdynamic" unless $CPPFLAGS.split.include? "-rdynamic"
|
|
202
210
|
$CPPFLAGS += " -fPIC" unless $CPPFLAGS.split.include? "-rdynamic" or IS_DARWIN
|
203
211
|
$CPPFLAGS += " -std=c++0x"
|
204
212
|
$CPPFLAGS += " -fpermissive"
|
213
|
+
$CPPFLAGS += " -DV8_COMPRESS_POINTERS"
|
214
|
+
$CPPFLAGS += " -fvisibility=hidden "
|
205
215
|
cppflags_add_frame_pointer!
|
206
216
|
cppflags_add_cpu_extension!
|
207
217
|
|
208
218
|
$CPPFLAGS += " -Wno-reserved-user-defined-literal" if IS_DARWIN
|
209
219
|
|
210
220
|
$LDFLAGS.insert(0, " -stdlib=libc++ ") if IS_DARWIN
|
221
|
+
$LDFLAGS += " -Wl,--no-undefined " unless IS_DARWIN
|
211
222
|
|
212
223
|
if ENV['CXX']
|
213
224
|
puts "SETTING CXX"
|
214
225
|
CONFIG['CXX'] = ENV['CXX']
|
215
226
|
end
|
227
|
+
# 1.9 has no $CXXFLAGS
|
228
|
+
$CPPFLAGS += " #{ENV['CPPFLAGS']}" if ENV['CPPFLAGS']
|
229
|
+
$LDFLAGS += " #{ENV['LDFLAGS']}" if ENV['LDFLAGS']
|
216
230
|
|
217
231
|
CXX11_TEST = <<EOS
|
218
232
|
#if __cplusplus <= 199711L
|
@@ -246,7 +260,7 @@ if enable_config('debug') || enable_config('asan')
|
|
246
260
|
CONFIG['debugflags'] << ' -ggdb3 -O0'
|
247
261
|
end
|
248
262
|
|
249
|
-
Libv8.configure_makefile
|
263
|
+
Libv8::Node.configure_makefile
|
250
264
|
|
251
265
|
if enable_config('asan')
|
252
266
|
$CPPFLAGS.insert(0, " -fsanitize=address ")
|
@@ -36,6 +36,8 @@
|
|
36
36
|
#include "compat.hpp"
|
37
37
|
#include "simdutf8check.h"
|
38
38
|
|
39
|
+
#include <time.h>
|
40
|
+
|
39
41
|
using namespace v8;
|
40
42
|
|
41
43
|
typedef struct {
|
@@ -49,6 +51,7 @@ public:
|
|
49
51
|
ArrayBuffer::Allocator* allocator;
|
50
52
|
StartupData* startup_data;
|
51
53
|
bool interrupted;
|
54
|
+
bool added_gc_cb;
|
52
55
|
pid_t pid;
|
53
56
|
VALUE mutex;
|
54
57
|
|
@@ -66,15 +69,12 @@ public:
|
|
66
69
|
|
67
70
|
|
68
71
|
IsolateInfo() : isolate(nullptr), allocator(nullptr), startup_data(nullptr),
|
69
|
-
interrupted(false), pid(getpid()), refs_count(0) {
|
72
|
+
interrupted(false), added_gc_cb(false), pid(getpid()), refs_count(0) {
|
70
73
|
VALUE cMutex = rb_const_get(rb_cThread, rb_intern("Mutex"));
|
71
74
|
mutex = rb_class_new_instance(0, nullptr, cMutex);
|
72
75
|
}
|
73
76
|
|
74
|
-
~IsolateInfo()
|
75
|
-
void free_isolate(IsolateInfo*);
|
76
|
-
free_isolate(this);
|
77
|
-
}
|
77
|
+
~IsolateInfo();
|
78
78
|
|
79
79
|
void init(SnapshotInfo* snapshot_info = nullptr);
|
80
80
|
|
@@ -151,6 +151,7 @@ typedef struct {
|
|
151
151
|
Local<Function> fun;
|
152
152
|
Local<Value> *argv;
|
153
153
|
EvalResult result;
|
154
|
+
size_t max_memory;
|
154
155
|
} FunctionCall;
|
155
156
|
|
156
157
|
enum IsolateFlags {
|
@@ -179,6 +180,11 @@ static VALUE rb_cDateTime = Qnil;
|
|
179
180
|
static std::unique_ptr<Platform> current_platform = NULL;
|
180
181
|
static std::mutex platform_lock;
|
181
182
|
|
183
|
+
static pthread_attr_t *thread_attr_p;
|
184
|
+
static pthread_rwlock_t exit_lock = PTHREAD_RWLOCK_INITIALIZER;
|
185
|
+
static bool ruby_exiting = false; // guarded by exit_lock
|
186
|
+
static bool single_threaded = false;
|
187
|
+
|
182
188
|
static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
183
189
|
bool platform_already_initialized = false;
|
184
190
|
|
@@ -190,6 +196,9 @@ static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
|
190
196
|
platform_lock.lock();
|
191
197
|
|
192
198
|
if (current_platform == NULL) {
|
199
|
+
if (!strcmp(RSTRING_PTR(flag_as_str), "--single_threaded")) {
|
200
|
+
single_threaded = true;
|
201
|
+
}
|
193
202
|
V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), (int)RSTRING_LEN(flag_as_str));
|
194
203
|
} else {
|
195
204
|
platform_already_initialized = true;
|
@@ -256,11 +265,13 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
256
265
|
Local<Value> local_value = v8res.ToLocalChecked();
|
257
266
|
if ((local_value->IsObject() || local_value->IsArray()) &&
|
258
267
|
!local_value->IsDate() && !local_value->IsFunction()) {
|
259
|
-
Local<Object> JSON = context->Global()->Get(
|
260
|
-
|
268
|
+
Local<Object> JSON = context->Global()->Get(
|
269
|
+
context, String::NewFromUtf8Literal(isolate, "JSON"))
|
270
|
+
.ToLocalChecked().As<Object>();
|
261
271
|
|
262
|
-
Local<Function> stringify = JSON->Get(
|
263
|
-
|
272
|
+
Local<Function> stringify = JSON->Get(
|
273
|
+
context, v8::String::NewFromUtf8Literal(isolate, "stringify"))
|
274
|
+
.ToLocalChecked().As<Function>();
|
264
275
|
|
265
276
|
Local<Object> object = local_value->ToObject(context).ToLocalChecked();
|
266
277
|
const unsigned argc = 1;
|
@@ -314,7 +325,7 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
314
325
|
} else if(trycatch.HasTerminated()) {
|
315
326
|
evalRes.terminated = true;
|
316
327
|
evalRes.message = new Persistent<Value>();
|
317
|
-
Local<String> tmp = String::
|
328
|
+
Local<String> tmp = String::NewFromUtf8Literal(isolate, "JavaScript was terminated (either by timeout or explicitly)");
|
318
329
|
evalRes.message->Reset(isolate, tmp);
|
319
330
|
}
|
320
331
|
if (!trycatch.StackTrace(context).IsEmpty()) {
|
@@ -335,7 +346,8 @@ nogvl_context_eval(void* arg) {
|
|
335
346
|
|
336
347
|
EvalParams* eval_params = (EvalParams*)arg;
|
337
348
|
EvalResult* result = eval_params->result;
|
338
|
-
|
349
|
+
IsolateInfo* isolate_info = eval_params->context_info->isolate_info;
|
350
|
+
Isolate* isolate = isolate_info->isolate;
|
339
351
|
|
340
352
|
Isolate::Scope isolate_scope(isolate);
|
341
353
|
HandleScope handle_scope(isolate);
|
@@ -379,7 +391,10 @@ nogvl_context_eval(void* arg) {
|
|
379
391
|
// parsing successful
|
380
392
|
if (eval_params->max_memory > 0) {
|
381
393
|
isolate->SetData(MEM_SOFTLIMIT_VALUE, &eval_params->max_memory);
|
394
|
+
if (!isolate_info->added_gc_cb) {
|
382
395
|
isolate->AddGCEpilogueCallback(gc_callback);
|
396
|
+
isolate_info->added_gc_cb = true;
|
397
|
+
}
|
383
398
|
}
|
384
399
|
|
385
400
|
maybe_value = parsed_script.ToLocalChecked()->Run(context);
|
@@ -392,6 +407,12 @@ nogvl_context_eval(void* arg) {
|
|
392
407
|
return 0;
|
393
408
|
}
|
394
409
|
|
410
|
+
static VALUE new_empty_failed_conv_obj() {
|
411
|
+
// TODO isolate code that translates execption to ruby
|
412
|
+
// exception so we can properly return it
|
413
|
+
return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
|
414
|
+
}
|
415
|
+
|
395
416
|
// assumes isolate locking is in place
|
396
417
|
static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
397
418
|
Local<Value> value) {
|
@@ -423,8 +444,11 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
423
444
|
VALUE rb_array = rb_ary_new();
|
424
445
|
Local<Array> arr = Local<Array>::Cast(value);
|
425
446
|
for(uint32_t i=0; i < arr->Length(); i++) {
|
426
|
-
|
427
|
-
|
447
|
+
MaybeLocal<Value> element = arr->Get(context, i);
|
448
|
+
if (element.IsEmpty()) {
|
449
|
+
continue;
|
450
|
+
}
|
451
|
+
VALUE rb_elem = convert_v8_to_ruby(isolate, context, element.ToLocalChecked());
|
428
452
|
if (rb_funcall(rb_elem, rb_intern("class"), 0) == rb_cFailedV8Conversion) {
|
429
453
|
return rb_elem;
|
430
454
|
}
|
@@ -454,26 +478,47 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
454
478
|
if (!maybe_props.IsEmpty()) {
|
455
479
|
Local<Array> props = maybe_props.ToLocalChecked();
|
456
480
|
for(uint32_t i=0; i < props->Length(); i++) {
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
481
|
+
MaybeLocal<Value> key = props->Get(context, i);
|
482
|
+
if (key.IsEmpty()) {
|
483
|
+
return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
|
484
|
+
}
|
485
|
+
VALUE rb_key = convert_v8_to_ruby(isolate, context, key.ToLocalChecked());
|
461
486
|
|
462
|
-
|
463
|
-
//
|
464
|
-
|
465
|
-
|
487
|
+
MaybeLocal<Value> prop_value = object->Get(context, key.ToLocalChecked());
|
488
|
+
// this may have failed due to Get raising
|
489
|
+
if (prop_value.IsEmpty() || trycatch.HasCaught()) {
|
490
|
+
return new_empty_failed_conv_obj();
|
466
491
|
}
|
467
492
|
|
468
|
-
VALUE rb_value = convert_v8_to_ruby(
|
493
|
+
VALUE rb_value = convert_v8_to_ruby(
|
494
|
+
isolate, context, prop_value.ToLocalChecked());
|
469
495
|
rb_hash_aset(rb_hash, rb_key, rb_value);
|
470
496
|
}
|
471
497
|
}
|
472
498
|
return rb_hash;
|
473
499
|
}
|
474
500
|
|
475
|
-
|
476
|
-
|
501
|
+
if (value->IsSymbol()) {
|
502
|
+
v8::String::Utf8Value symbol_name(isolate,
|
503
|
+
Local<Symbol>::Cast(value)->Name());
|
504
|
+
|
505
|
+
VALUE str_symbol = rb_enc_str_new(
|
506
|
+
*symbol_name,
|
507
|
+
symbol_name.length(),
|
508
|
+
rb_enc_find("utf-8")
|
509
|
+
);
|
510
|
+
|
511
|
+
return ID2SYM(rb_intern_str(str_symbol));
|
512
|
+
}
|
513
|
+
|
514
|
+
MaybeLocal<String> rstr_maybe = value->ToString(context);
|
515
|
+
|
516
|
+
if (rstr_maybe.IsEmpty()) {
|
517
|
+
return Qnil;
|
518
|
+
} else {
|
519
|
+
Local<String> rstr = rstr_maybe.ToLocalChecked();
|
520
|
+
return rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
|
521
|
+
}
|
477
522
|
}
|
478
523
|
|
479
524
|
static VALUE convert_v8_to_ruby(Isolate* isolate,
|
@@ -608,7 +653,7 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
608
653
|
length = RARRAY_LEN(value);
|
609
654
|
array = Array::New(isolate, (int)length);
|
610
655
|
for(i=0; i<length; i++) {
|
611
|
-
|
656
|
+
array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
|
612
657
|
}
|
613
658
|
return scope.Escape(array);
|
614
659
|
}
|
@@ -619,7 +664,7 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
619
664
|
length = RARRAY_LEN(hash_as_array);
|
620
665
|
for(i=0; i<length; i++) {
|
621
666
|
pair = rb_ary_entry(hash_as_array, i);
|
622
|
-
|
667
|
+
object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
|
623
668
|
convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 1)));
|
624
669
|
}
|
625
670
|
return scope.Escape(object);
|
@@ -661,8 +706,9 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
661
706
|
value = rb_funcall(value, rb_intern("to_s"), 0);
|
662
707
|
return scope.Escape(convert_ruby_str_to_v8(scope, isolate, value));
|
663
708
|
}
|
664
|
-
|
665
|
-
|
709
|
+
return scope.Escape(
|
710
|
+
String::NewFromUtf8Literal(isolate, "Undefined Conversion"));
|
711
|
+
}
|
666
712
|
}
|
667
713
|
}
|
668
714
|
|
@@ -675,53 +721,43 @@ static void unblock_eval(void *ptr) {
|
|
675
721
|
* The implementations of the run_extra_code(), create_snapshot_data_blob() and
|
676
722
|
* warm_up_snapshot_data_blob() functions have been derived from V8's test suite.
|
677
723
|
*/
|
678
|
-
bool run_extra_code(Isolate *isolate, Local<v8::Context> context,
|
724
|
+
static bool run_extra_code(Isolate *isolate, Local<v8::Context> context,
|
679
725
|
const char *utf8_source, const char *name) {
|
680
726
|
Context::Scope context_scope(context);
|
681
727
|
TryCatch try_catch(isolate);
|
682
728
|
Local<String> source_string;
|
683
|
-
if (!String::NewFromUtf8(isolate, utf8_source
|
684
|
-
NewStringType::kNormal)
|
685
|
-
.ToLocal(&source_string)) {
|
729
|
+
if (!String::NewFromUtf8(isolate, utf8_source).ToLocal(&source_string)) {
|
686
730
|
return false;
|
687
731
|
}
|
688
|
-
Local<
|
689
|
-
|
690
|
-
.ToLocalChecked();
|
732
|
+
Local<String> resource_name =
|
733
|
+
String::NewFromUtf8(isolate, name).ToLocalChecked();
|
691
734
|
ScriptOrigin origin(resource_name);
|
692
735
|
ScriptCompiler::Source source(source_string, origin);
|
693
736
|
Local<Script> script;
|
694
737
|
if (!ScriptCompiler::Compile(context, &source).ToLocal(&script))
|
695
738
|
return false;
|
696
|
-
if (script->Run(context).IsEmpty())
|
697
|
-
return false;
|
698
|
-
// CHECK(!try_catch.HasCaught());
|
739
|
+
if (script->Run(context).IsEmpty()) return false;
|
699
740
|
return true;
|
700
741
|
}
|
701
742
|
|
702
|
-
StartupData
|
743
|
+
static StartupData
|
703
744
|
create_snapshot_data_blob(const char *embedded_source = nullptr) {
|
704
|
-
|
705
|
-
|
706
|
-
|
745
|
+
Isolate *isolate = Isolate::Allocate();
|
746
|
+
|
747
|
+
// Optionally run a script to embed, and serialize to create a snapshot blob.
|
748
|
+
SnapshotCreator snapshot_creator(isolate);
|
707
749
|
{
|
708
|
-
SnapshotCreator snapshot_creator;
|
709
|
-
Isolate *isolate = snapshot_creator.GetIsolate();
|
710
|
-
{
|
711
750
|
HandleScope scope(isolate);
|
712
|
-
|
751
|
+
Local<v8::Context> context = v8::Context::New(isolate);
|
713
752
|
if (embedded_source != nullptr &&
|
714
|
-
!run_extra_code(isolate, context, embedded_source,
|
715
|
-
|
716
|
-
return result;
|
753
|
+
!run_extra_code(isolate, context, embedded_source, "<embedded>")) {
|
754
|
+
return {};
|
717
755
|
}
|
718
756
|
snapshot_creator.SetDefaultContext(context);
|
719
757
|
}
|
720
|
-
|
758
|
+
return snapshot_creator.CreateBlob(
|
721
759
|
SnapshotCreator::FunctionCodeHandling::kClear);
|
722
760
|
}
|
723
|
-
return result;
|
724
|
-
}
|
725
761
|
|
726
762
|
StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
|
727
763
|
const char *warmup_source) {
|
@@ -868,6 +904,29 @@ static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
|
|
868
904
|
return isolate_info->isolate->IdleNotificationDeadline(now + duration) ? Qtrue : Qfalse;
|
869
905
|
}
|
870
906
|
|
907
|
+
static VALUE rb_isolate_low_memory_notification(VALUE self) {
|
908
|
+
IsolateInfo* isolate_info;
|
909
|
+
Data_Get_Struct(self, IsolateInfo, isolate_info);
|
910
|
+
|
911
|
+
if (current_platform == NULL) return Qfalse;
|
912
|
+
|
913
|
+
isolate_info->isolate->LowMemoryNotification();
|
914
|
+
return Qnil;
|
915
|
+
}
|
916
|
+
|
917
|
+
static VALUE rb_isolate_pump_message_loop(VALUE self) {
|
918
|
+
IsolateInfo* isolate_info;
|
919
|
+
Data_Get_Struct(self, IsolateInfo, isolate_info);
|
920
|
+
|
921
|
+
if (current_platform == NULL) return Qfalse;
|
922
|
+
|
923
|
+
if (platform::PumpMessageLoop(current_platform.get(), isolate_info->isolate)){
|
924
|
+
return Qtrue;
|
925
|
+
} else {
|
926
|
+
return Qfalse;
|
927
|
+
}
|
928
|
+
}
|
929
|
+
|
871
930
|
static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
|
872
931
|
ContextInfo* context_info;
|
873
932
|
Data_Get_Struct(self, ContextInfo, context_info);
|
@@ -1142,7 +1201,9 @@ gvl_ruby_callback(void* data) {
|
|
1142
1201
|
callback_data.failed = false;
|
1143
1202
|
|
1144
1203
|
if ((bool)args->GetIsolate()->GetData(DO_TERMINATE) == true) {
|
1145
|
-
args->GetIsolate()->ThrowException(
|
1204
|
+
args->GetIsolate()->ThrowException(
|
1205
|
+
String::NewFromUtf8Literal(args->GetIsolate(),
|
1206
|
+
"Terminated execution during transition from Ruby to JS"));
|
1146
1207
|
args->GetIsolate()->TerminateExecution();
|
1147
1208
|
if (length > 0) {
|
1148
1209
|
rb_ary_clear(ruby_args);
|
@@ -1156,7 +1217,7 @@ gvl_ruby_callback(void* data) {
|
|
1156
1217
|
|
1157
1218
|
if(callback_data.failed) {
|
1158
1219
|
rb_iv_set(parent, "@current_exception", result);
|
1159
|
-
args->GetIsolate()->ThrowException(String::
|
1220
|
+
args->GetIsolate()->ThrowException(String::NewFromUtf8Literal(args->GetIsolate(), "Ruby exception"));
|
1160
1221
|
}
|
1161
1222
|
else {
|
1162
1223
|
HandleScope scope(args->GetIsolate());
|
@@ -1231,7 +1292,9 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1231
1292
|
|
1232
1293
|
if (parent_object == Qnil) {
|
1233
1294
|
context->Global()->Set(
|
1234
|
-
|
1295
|
+
context,
|
1296
|
+
v8_str,
|
1297
|
+
FunctionTemplate::New(isolate, ruby_callback, external)
|
1235
1298
|
->GetFunction(context)
|
1236
1299
|
.ToLocalChecked());
|
1237
1300
|
|
@@ -1244,7 +1307,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1244
1307
|
|
1245
1308
|
MaybeLocal<Script> parsed_script = Script::Compile(context, eval);
|
1246
1309
|
if (parsed_script.IsEmpty()) {
|
1247
|
-
|
1310
|
+
parse_error = true;
|
1248
1311
|
} else {
|
1249
1312
|
MaybeLocal<Value> maybe_value =
|
1250
1313
|
parsed_script.ToLocalChecked()->Run(context);
|
@@ -1254,11 +1317,12 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1254
1317
|
Local<Value> value = maybe_value.ToLocalChecked();
|
1255
1318
|
if (value->IsObject()) {
|
1256
1319
|
value.As<Object>()->Set(
|
1257
|
-
|
1258
|
-
|
1320
|
+
context,
|
1321
|
+
v8_str,
|
1322
|
+
FunctionTemplate::New(isolate, ruby_callback, external)
|
1259
1323
|
->GetFunction(context)
|
1260
1324
|
.ToLocalChecked());
|
1261
|
-
|
1325
|
+
attach_error = false;
|
1262
1326
|
}
|
1263
1327
|
}
|
1264
1328
|
}
|
@@ -1288,35 +1352,39 @@ static VALUE rb_context_isolate_mutex(VALUE self) {
|
|
1288
1352
|
return context_info->isolate_info->mutex;
|
1289
1353
|
}
|
1290
1354
|
|
1291
|
-
|
1292
|
-
|
1293
|
-
|
1294
|
-
|
1295
|
-
|
1296
|
-
|
1297
|
-
if (isolate_info->isolate) {
|
1298
|
-
if (isolate_info->interrupted) {
|
1299
|
-
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");
|
1355
|
+
IsolateInfo::~IsolateInfo() {
|
1356
|
+
if (isolate) {
|
1357
|
+
if (this->interrupted) {
|
1358
|
+
fprintf(stderr, "WARNING: V8 isolate was interrupted by Ruby, "
|
1359
|
+
"it can not be disposed and memory will not be "
|
1360
|
+
"reclaimed till the Ruby process exits.\n");
|
1300
1361
|
} else {
|
1301
|
-
|
1302
|
-
|
1303
|
-
|
1362
|
+
if (this->pid != getpid() && !single_threaded) {
|
1363
|
+
fprintf(stderr, "WARNING: V8 isolate was forked, "
|
1364
|
+
"it can not be disposed and "
|
1365
|
+
"memory will not be reclaimed "
|
1366
|
+
"till the Ruby process exits.\n"
|
1367
|
+
"It is VERY likely your process will hang.\n"
|
1368
|
+
"If you wish to use v8 in forked environment "
|
1369
|
+
"please ensure the platform is initialized with:\n"
|
1370
|
+
"MiniRacer::Platform.set_flags! :single_threaded\n"
|
1371
|
+
);
|
1304
1372
|
} else {
|
1305
|
-
|
1373
|
+
isolate->Dispose();
|
1306
1374
|
}
|
1307
1375
|
}
|
1308
|
-
|
1376
|
+
isolate = nullptr;
|
1309
1377
|
}
|
1310
1378
|
|
1311
|
-
if (
|
1312
|
-
delete[]
|
1313
|
-
delete
|
1379
|
+
if (startup_data) {
|
1380
|
+
delete[] startup_data->data;
|
1381
|
+
delete startup_data;
|
1314
1382
|
}
|
1315
1383
|
|
1316
|
-
delete
|
1384
|
+
delete allocator;
|
1317
1385
|
}
|
1318
1386
|
|
1319
|
-
static void
|
1387
|
+
static void free_context_raw(void *arg) {
|
1320
1388
|
ContextInfo* context_info = (ContextInfo*)arg;
|
1321
1389
|
IsolateInfo* isolate_info = context_info->isolate_info;
|
1322
1390
|
Persistent<Context>* context = context_info->context;
|
@@ -1333,6 +1401,20 @@ static void *free_context_raw(void* arg) {
|
|
1333
1401
|
}
|
1334
1402
|
|
1335
1403
|
xfree(context_info);
|
1404
|
+
}
|
1405
|
+
|
1406
|
+
static void *free_context_thr(void* arg) {
|
1407
|
+
if (pthread_rwlock_tryrdlock(&exit_lock) != 0) {
|
1408
|
+
return NULL;
|
1409
|
+
}
|
1410
|
+
if (ruby_exiting) {
|
1411
|
+
return NULL;
|
1412
|
+
}
|
1413
|
+
|
1414
|
+
free_context_raw(arg);
|
1415
|
+
|
1416
|
+
pthread_rwlock_unlock(&exit_lock);
|
1417
|
+
|
1336
1418
|
return NULL;
|
1337
1419
|
}
|
1338
1420
|
|
@@ -1347,22 +1429,17 @@ static void free_context(ContextInfo* context_info) {
|
|
1347
1429
|
|
1348
1430
|
if (isolate_info && isolate_info->refs() > 1) {
|
1349
1431
|
pthread_t free_context_thread;
|
1350
|
-
|
1432
|
+
if (pthread_create(&free_context_thread, thread_attr_p,
|
1433
|
+
free_context_thr, (void*)context_info_copy)) {
|
1351
1434
|
fprintf(stderr, "WARNING failed to release memory in MiniRacer, thread to release could not be created, process will leak memory\n");
|
1352
1435
|
}
|
1353
|
-
|
1354
1436
|
} else {
|
1355
1437
|
free_context_raw(context_info_copy);
|
1356
1438
|
}
|
1357
1439
|
|
1358
|
-
if (context_info->context && isolate_info && isolate_info->isolate) {
|
1359
1440
|
context_info->context = NULL;
|
1360
|
-
}
|
1361
|
-
|
1362
|
-
if (isolate_info) {
|
1363
1441
|
context_info->isolate_info = NULL;
|
1364
1442
|
}
|
1365
|
-
}
|
1366
1443
|
|
1367
1444
|
static void deallocate_isolate(void* data) {
|
1368
1445
|
|
@@ -1376,7 +1453,7 @@ static void mark_isolate(void* data) {
|
|
1376
1453
|
isolate_info->mark();
|
1377
1454
|
}
|
1378
1455
|
|
1379
|
-
void deallocate(void* data) {
|
1456
|
+
static void deallocate(void* data) {
|
1380
1457
|
ContextInfo* context_info = (ContextInfo*)data;
|
1381
1458
|
|
1382
1459
|
free_context(context_info);
|
@@ -1391,22 +1468,22 @@ static void mark_context(void* data) {
|
|
1391
1468
|
}
|
1392
1469
|
}
|
1393
1470
|
|
1394
|
-
void deallocate_external_function(void * data) {
|
1471
|
+
static void deallocate_external_function(void * data) {
|
1395
1472
|
xfree(data);
|
1396
1473
|
}
|
1397
1474
|
|
1398
|
-
void deallocate_snapshot(void * data) {
|
1475
|
+
static void deallocate_snapshot(void * data) {
|
1399
1476
|
SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
|
1400
1477
|
delete[] snapshot_info->data;
|
1401
1478
|
xfree(snapshot_info);
|
1402
1479
|
}
|
1403
1480
|
|
1404
|
-
VALUE allocate_external_function(VALUE klass) {
|
1481
|
+
static VALUE allocate_external_function(VALUE klass) {
|
1405
1482
|
VALUE* self = ALLOC(VALUE);
|
1406
1483
|
return Data_Wrap_Struct(klass, NULL, deallocate_external_function, (void*)self);
|
1407
1484
|
}
|
1408
1485
|
|
1409
|
-
VALUE allocate(VALUE klass) {
|
1486
|
+
static VALUE allocate(VALUE klass) {
|
1410
1487
|
ContextInfo* context_info = ALLOC(ContextInfo);
|
1411
1488
|
context_info->isolate_info = NULL;
|
1412
1489
|
context_info->context = NULL;
|
@@ -1414,7 +1491,7 @@ VALUE allocate(VALUE klass) {
|
|
1414
1491
|
return Data_Wrap_Struct(klass, mark_context, deallocate, (void*)context_info);
|
1415
1492
|
}
|
1416
1493
|
|
1417
|
-
VALUE allocate_snapshot(VALUE klass) {
|
1494
|
+
static VALUE allocate_snapshot(VALUE klass) {
|
1418
1495
|
SnapshotInfo* snapshot_info = ALLOC(SnapshotInfo);
|
1419
1496
|
snapshot_info->data = NULL;
|
1420
1497
|
snapshot_info->raw_size = 0;
|
@@ -1422,7 +1499,7 @@ VALUE allocate_snapshot(VALUE klass) {
|
|
1422
1499
|
return Data_Wrap_Struct(klass, NULL, deallocate_snapshot, (void*)snapshot_info);
|
1423
1500
|
}
|
1424
1501
|
|
1425
|
-
VALUE allocate_isolate(VALUE klass) {
|
1502
|
+
static VALUE allocate_isolate(VALUE klass) {
|
1426
1503
|
IsolateInfo* isolate_info = new IsolateInfo();
|
1427
1504
|
|
1428
1505
|
return Data_Wrap_Struct(klass, mark_isolate, deallocate_isolate, (void*)isolate_info);
|
@@ -1519,6 +1596,8 @@ rb_heap_snapshot(VALUE self, VALUE file) {
|
|
1519
1596
|
FileOutputStream stream(fp);
|
1520
1597
|
snap->Serialize(&stream, HeapSnapshot::kJSON);
|
1521
1598
|
|
1599
|
+
fflush(fp);
|
1600
|
+
|
1522
1601
|
const_cast<HeapSnapshot*>(snap)->Delete();
|
1523
1602
|
|
1524
1603
|
return Qtrue;
|
@@ -1576,13 +1655,23 @@ nogvl_context_call(void *args) {
|
|
1576
1655
|
if (!call) {
|
1577
1656
|
return 0;
|
1578
1657
|
}
|
1579
|
-
|
1658
|
+
IsolateInfo *isolate_info = call->context_info->isolate_info;
|
1659
|
+
Isolate* isolate = isolate_info->isolate;
|
1580
1660
|
|
1581
1661
|
// in gvl flag
|
1582
1662
|
isolate->SetData(IN_GVL, (void*)false);
|
1583
1663
|
// terminate ASAP
|
1584
1664
|
isolate->SetData(DO_TERMINATE, (void*)false);
|
1585
1665
|
|
1666
|
+
if (call->max_memory > 0) {
|
1667
|
+
isolate->SetData(MEM_SOFTLIMIT_VALUE, &call->max_memory);
|
1668
|
+
isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)false);
|
1669
|
+
if (!isolate_info->added_gc_cb) {
|
1670
|
+
isolate->AddGCEpilogueCallback(gc_callback);
|
1671
|
+
isolate_info->added_gc_cb = true;
|
1672
|
+
}
|
1673
|
+
}
|
1674
|
+
|
1586
1675
|
Isolate::Scope isolate_scope(isolate);
|
1587
1676
|
EscapableHandleScope handle_scope(isolate);
|
1588
1677
|
TryCatch trycatch(isolate);
|
@@ -1640,6 +1729,13 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1640
1729
|
call_argv = argv + 1;
|
1641
1730
|
}
|
1642
1731
|
|
1732
|
+
call.max_memory = 0;
|
1733
|
+
VALUE mem_softlimit = rb_iv_get(self, "@max_memory");
|
1734
|
+
if (mem_softlimit != Qnil) {
|
1735
|
+
unsigned long sl_int = NUM2ULONG(mem_softlimit);
|
1736
|
+
call.max_memory = (size_t)sl_int;
|
1737
|
+
}
|
1738
|
+
|
1643
1739
|
bool missingFunction = false;
|
1644
1740
|
{
|
1645
1741
|
Locker lock(isolate);
|
@@ -1651,8 +1747,11 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1651
1747
|
|
1652
1748
|
// examples of such usage can be found in
|
1653
1749
|
// https://github.com/v8/v8/blob/36b32aa28db5e993312f4588d60aad5c8330c8a5/test/cctest/test-api.cc#L15711
|
1654
|
-
|
1655
|
-
MaybeLocal<v8::Value> val
|
1750
|
+
MaybeLocal<String> fname = String::NewFromUtf8(isolate, call.function_name);
|
1751
|
+
MaybeLocal<v8::Value> val;
|
1752
|
+
if (!fname.IsEmpty()) {
|
1753
|
+
val = context->Global()->Get(context, fname.ToLocalChecked());
|
1754
|
+
}
|
1656
1755
|
|
1657
1756
|
if (val.IsEmpty() || !val.ToLocalChecked()->IsFunction()) {
|
1658
1757
|
missingFunction = true;
|
@@ -1701,12 +1800,44 @@ static VALUE rb_context_create_isolate_value(VALUE self) {
|
|
1701
1800
|
return Data_Wrap_Struct(rb_cIsolate, NULL, &deallocate_isolate, isolate_info);
|
1702
1801
|
}
|
1703
1802
|
|
1803
|
+
static void set_ruby_exiting(VALUE value) {
|
1804
|
+
(void)value;
|
1805
|
+
|
1806
|
+
int res = pthread_rwlock_wrlock(&exit_lock);
|
1807
|
+
|
1808
|
+
ruby_exiting = true;
|
1809
|
+
if (res == 0) {
|
1810
|
+
pthread_rwlock_unlock(&exit_lock);
|
1811
|
+
}
|
1812
|
+
}
|
1813
|
+
|
1814
|
+
static VALUE rb_monotime(VALUE self) {
|
1815
|
+
struct timespec ts;
|
1816
|
+
if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) {
|
1817
|
+
return INT2FIX(-1);
|
1818
|
+
}
|
1819
|
+
|
1820
|
+
return DBL2NUM(
|
1821
|
+
(double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0);
|
1822
|
+
}
|
1823
|
+
|
1704
1824
|
extern "C" {
|
1705
1825
|
|
1706
|
-
void Init_sq_mini_racer_extension ( void )
|
1826
|
+
__attribute__((visibility("default"))) void Init_sq_mini_racer_extension ( void )
|
1707
1827
|
{
|
1708
|
-
|
1828
|
+
ID sqreen_id = rb_intern("Sqreen");
|
1829
|
+
VALUE rb_mSqreen;
|
1830
|
+
if (rb_const_defined(rb_cObject, sqreen_id)) {
|
1831
|
+
rb_mSqreen = rb_const_get(rb_cObject, sqreen_id);
|
1832
|
+
if (TYPE(rb_mSqreen) != T_MODULE) {
|
1833
|
+
rb_raise(rb_eTypeError, "Sqreen is not a module");
|
1834
|
+
return;
|
1835
|
+
}
|
1836
|
+
} else {
|
1837
|
+
rb_mSqreen = rb_define_module("Sqreen");
|
1838
|
+
}
|
1709
1839
|
VALUE rb_mMiniRacer = rb_define_module_under(rb_mSqreen, "MiniRacer");
|
1840
|
+
rb_define_module_function(rb_mMiniRacer, "monotime", (VALUE(*)(...))&rb_monotime, 0);
|
1710
1841
|
rb_cContext = rb_define_class_under(rb_mMiniRacer, "Context", rb_cObject);
|
1711
1842
|
rb_cSnapshot = rb_define_class_under(rb_mMiniRacer, "Snapshot", rb_cObject);
|
1712
1843
|
rb_cIsolate = rb_define_class_under(rb_mMiniRacer, "Isolate", rb_cObject);
|
@@ -1753,8 +1884,19 @@ extern "C" {
|
|
1753
1884
|
rb_define_private_method(rb_cSnapshot, "load", (VALUE(*)(...))&rb_snapshot_load, 1);
|
1754
1885
|
|
1755
1886
|
rb_define_method(rb_cIsolate, "idle_notification", (VALUE(*)(...))&rb_isolate_idle_notification, 1);
|
1887
|
+
rb_define_method(rb_cIsolate, "low_memory_notification", (VALUE(*)(...))&rb_isolate_low_memory_notification, 0);
|
1888
|
+
rb_define_method(rb_cIsolate, "pump_message_loop", (VALUE(*)(...))&rb_isolate_pump_message_loop, 0);
|
1756
1889
|
rb_define_private_method(rb_cIsolate, "init_with_snapshot",(VALUE(*)(...))&rb_isolate_init_with_snapshot, 1);
|
1757
1890
|
|
1758
1891
|
rb_define_singleton_method(rb_cPlatform, "set_flag_as_str!", (VALUE(*)(...))&rb_platform_set_flag_as_str, 1);
|
1892
|
+
|
1893
|
+
rb_set_end_proc(set_ruby_exiting, Qnil);
|
1894
|
+
|
1895
|
+
static pthread_attr_t attr;
|
1896
|
+
if (pthread_attr_init(&attr) == 0) {
|
1897
|
+
if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == 0) {
|
1898
|
+
thread_attr_p = &attr;
|
1899
|
+
}
|
1900
|
+
}
|
1759
1901
|
}
|
1760
1902
|
}
|