sq_mini_racer 0.2.5.0.2 → 0.3.1.0.4
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/.gitignore +9 -0
- data/CHANGELOG +73 -0
- data/Dockerfile +0 -1
- data/README.md +17 -2
- data/Rakefile +44 -62
- data/azure-template.yml +10 -1
- data/ext/mini_racer_extension/extconf.rb +9 -6
- data/ext/mini_racer_extension/mini_racer_extension.cc +265 -99
- 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 -5
- data/valgrind.supp +74 -0
- metadata +65 -13
- data/.travis.yml +0 -73
- data/ext/prv_ext_loader/extconf.rb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d8cbb749f96b48a2a73449a6d1cd185b52666e06cafde1403b272546febe73f3
|
4
|
+
data.tar.gz: 8f043ac401c8352821c390f8bb77a6f049bef9fd1554bdb999bb176159dedfce
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 12b21a0d7d26dd3a4ff555322126f2a93d6e3ecfd6a510aff13af9b097334b43709ad2b8fdd8453736f157364eda2ad6f7bfc22de4c97294eefa519bc9cffd74
|
7
|
+
data.tar.gz: e9f45f0ff41d1939ce62acaeeeae6ecf2aa4d1744c87b6067043bac7480bddb94410a99a27a92526cdfdc006a597cd6d0e55bde992a52bfcfb3c49491d7dc7ce
|
data/.gitignore
CHANGED
@@ -11,3 +11,12 @@ lib/*.so
|
|
11
11
|
/lib/sq_mini_racer/
|
12
12
|
/ext/mini_racer_extension/mini_racer.creator.*
|
13
13
|
*.bundle
|
14
|
+
/mini_racer.config
|
15
|
+
/mini_racer.creator
|
16
|
+
/mini_racer.creator.user
|
17
|
+
/mini_racer.files
|
18
|
+
/mini_racer.includes
|
19
|
+
/compile_commands.json
|
20
|
+
/.vscode/
|
21
|
+
/.clangd/
|
22
|
+
/.idea/
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,76 @@
|
|
1
|
+
- Unreleased
|
2
|
+
|
3
|
+
- FIX: on downgrade mkmf was picking the wrong version of libv8, this fix will correct future issues
|
4
|
+
|
5
|
+
- 23-07-2020
|
6
|
+
|
7
|
+
- 0.3.1
|
8
|
+
|
9
|
+
- FIX: specify that libv8 must be larger than 8.4.255 but smaller than 8.5, this avoids issues going forward
|
10
|
+
|
11
|
+
- 22-07-2020
|
12
|
+
|
13
|
+
- 0.3.0
|
14
|
+
|
15
|
+
- FEATURE: upgraded to libv8 version 8.4.255.0
|
16
|
+
|
17
|
+
- 29-06-2020
|
18
|
+
|
19
|
+
- 0.2.15
|
20
|
+
|
21
|
+
- FEATURE: basic wasm support via pump_message_loop
|
22
|
+
|
23
|
+
- 15-05-2020
|
24
|
+
|
25
|
+
- 0.2.14
|
26
|
+
|
27
|
+
- FIX: ensure_gc_after_idle should take in milliseconds like the rest of the APIs not seconds
|
28
|
+
- FEATURE: strict params on MiniRacer::Context.new
|
29
|
+
|
30
|
+
- 15-05-2020
|
31
|
+
|
32
|
+
- 0.2.13
|
33
|
+
|
34
|
+
- FIX: edge case around ensure_gc_after_idle possibly firing when context is not idle
|
35
|
+
|
36
|
+
- 15-05-2020
|
37
|
+
|
38
|
+
- 0.2.12
|
39
|
+
|
40
|
+
- FEATURE: isolate.low_memory_notification which can force a full GC
|
41
|
+
- FEATURE: MiniRacer::Context.new(ensure_gc_after_idle: 2) - to force full GC 2 seconds after context is idle, this allows you to conserve memory on isolates
|
42
|
+
|
43
|
+
- 14-05-2020
|
44
|
+
|
45
|
+
- 0.2.11
|
46
|
+
|
47
|
+
- FIX: dumping heap snapshots was not flushing the file leading to corrupt snapshots
|
48
|
+
- FIX: a use-after-free shutdown crash
|
49
|
+
|
50
|
+
- 0.2.10
|
51
|
+
|
52
|
+
- 22-04-2020
|
53
|
+
|
54
|
+
- FEATURE: memory softlimit support for nogvl_context_call
|
55
|
+
|
56
|
+
- 0.2.9
|
57
|
+
|
58
|
+
- 09-01-2020
|
59
|
+
|
60
|
+
- FIX: correct segfault when JS returns a Symbol and properly cast to ruby symbol
|
61
|
+
|
62
|
+
- 0.2.8
|
63
|
+
|
64
|
+
- 11-11-2019
|
65
|
+
|
66
|
+
- FIX: ensure thread live cycle is properly accounter for following file descriptor fix
|
67
|
+
|
68
|
+
- 0.2.7
|
69
|
+
|
70
|
+
- 11-11-2019
|
71
|
+
|
72
|
+
- FIX: release the file descriptor for timeout pipe earlier (this avoids holding too many files open in Ruby 2.7)
|
73
|
+
|
1
74
|
- 14-05-2019
|
2
75
|
|
3
76
|
- 0.2.6
|
data/Dockerfile
CHANGED
data/README.md
CHANGED
@@ -234,12 +234,17 @@ context = MiniRacer::Context.new(isolate: isolate)
|
|
234
234
|
# give up to 100ms for V8 garbage collection
|
235
235
|
isolate.idle_notification(100)
|
236
236
|
|
237
|
+
# force V8 to perform a full GC
|
238
|
+
isolate.low_memory_notification
|
239
|
+
|
237
240
|
```
|
238
241
|
|
239
242
|
This can come in handy to force V8 GC runs for example in between requests if you use MiniRacer on a web application.
|
240
243
|
|
241
244
|
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.`
|
242
245
|
|
246
|
+
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.
|
247
|
+
|
243
248
|
### V8 Runtime flags
|
244
249
|
|
245
250
|
It is possible to set V8 Runtime flags:
|
@@ -277,7 +282,7 @@ context.eval js
|
|
277
282
|
The same code without the harmony runtime flag results in a `MiniRacer::RuntimeError: RangeError: Maximum call stack size exceeded` exception.
|
278
283
|
Please refer to http://node.green/ as a reference on other harmony features.
|
279
284
|
|
280
|
-
A list of all V8 runtime flags can be found using `node --v8-options`, or else by perusing [the V8 source code for flags (make sure to use the right version of V8)](https://github.com/v8/v8/blob/master/src/flag-definitions.h).
|
285
|
+
A list of all V8 runtime flags can be found using `node --v8-options`, or else by perusing [the V8 source code for flags (make sure to use the right version of V8)](https://github.com/v8/v8/blob/master/src/flags/flag-definitions.h).
|
281
286
|
|
282
287
|
Note that runtime flags must be set before any other operation (e.g. creating a context, a snapshot or an isolate), otherwise an exception will be thrown.
|
283
288
|
|
@@ -314,6 +319,16 @@ context.eval("a = 2")
|
|
314
319
|
# nothing works on the context from now on, its a shell waiting to be disposed
|
315
320
|
```
|
316
321
|
|
322
|
+
A MiniRacer context can also be dumped in a heapsnapshot file using `#write_heap_snapshot(file_or_io)`
|
323
|
+
|
324
|
+
```ruby
|
325
|
+
context = MiniRacer::Context.new(timeout: 5)
|
326
|
+
context.eval("let a='testing';")
|
327
|
+
context.write_heap_snapshot("test.heapsnapshot")
|
328
|
+
```
|
329
|
+
|
330
|
+
This file can then be loaded in the memory tab of the chrome dev console.
|
331
|
+
|
317
332
|
### Function call
|
318
333
|
|
319
334
|
This calls the function passed as first argument:
|
@@ -451,7 +466,7 @@ Add this to your .travis.yml file:
|
|
451
466
|
|
452
467
|
## Contributing
|
453
468
|
|
454
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
469
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/rubyjs/mini_racer. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
455
470
|
|
456
471
|
|
457
472
|
## License
|
data/Rakefile
CHANGED
@@ -1,75 +1,57 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
2
|
require "rake/testtask"
|
3
3
|
require "rake/extensiontask"
|
4
|
+
require "shellwords"
|
5
|
+
|
6
|
+
class ValgrindTestTask < Rake::TestTask
|
7
|
+
VALGRIND_EXEC = 'valgrind'
|
8
|
+
DEFAULT_VALGRIND_OPTS = %w{
|
9
|
+
--trace-children=yes
|
10
|
+
--partial-loads-ok=yes
|
11
|
+
--error-limit=no
|
12
|
+
--error-exitcode=33
|
13
|
+
--num-callers=100
|
14
|
+
--suppressions=valgrind.supp
|
15
|
+
--gen-suppressions=all
|
16
|
+
}
|
17
|
+
|
18
|
+
attr_accessor :valgrind_args
|
19
|
+
|
20
|
+
def initialize(name=:valgrind_test)
|
21
|
+
@valgrind_args = DEFAULT_VALGRIND_OPTS
|
22
|
+
super
|
23
|
+
end
|
4
24
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
Rake::ExtensionTask.new( 'mini_racer_extension', gem ) do |ext|
|
15
|
-
ext.name = 'sq_mini_racer_extension'
|
16
|
-
end
|
17
|
-
Rake::ExtensionTask.new('prv_ext_loader', gem)
|
18
|
-
|
19
|
-
|
20
|
-
# via http://blog.flavorjon.es/2009/06/easily-valgrind-gdb-your-ruby-c.html
|
21
|
-
namespace :test do
|
22
|
-
desc "run test suite with Address Sanitizer"
|
23
|
-
task :asan do
|
24
|
-
ENV["CONFIGURE_ARGS"] = [ENV["CONFIGURE_ARGS"], '--enable-asan'].compact.join(' ')
|
25
|
-
Rake::Task['compile'].invoke
|
26
|
-
|
27
|
-
asan_path = `ldconfig -N -p |grep libasan | grep -v 32 | sed 's/.* => \\(.*\\)$/\\1/'`.chomp.split("\n")[-1]
|
28
|
-
|
29
|
-
|
30
|
-
cmdline = "env LD_PRELOAD=\"#{asan_path}\" ruby test/test_leak.rb"
|
31
|
-
puts cmdline
|
32
|
-
system cmdline
|
33
|
-
|
34
|
-
cmdline = "env LD_PRELOAD=\"#{asan_path}\" rake test"
|
35
|
-
puts cmdline
|
36
|
-
system cmdline
|
25
|
+
# see original def in fileutils.rb
|
26
|
+
def ruby(*args, &block)
|
27
|
+
options = (Hash === args.last) ? args.pop : {}
|
28
|
+
if args.length > 1
|
29
|
+
sh(*([VALGRIND_EXEC] + valgrind_args + [RUBY] + args + [options]), &block)
|
30
|
+
else
|
31
|
+
# if the size is 1 it's assumed the arguments are already escaped
|
32
|
+
non_escaped_args = [VALGRIND_EXEC] + valgrind_args + [RUBY]
|
33
|
+
sh("#{non_escaped_args.map { |s| Shellwords.escape(s) }.join(' ')} #{args.first}", options, &block)
|
37
34
|
end
|
38
|
-
|
39
|
-
|
40
|
-
# interpreter
|
41
|
-
VALGRIND_BASIC_OPTS = "--num-callers=50 --error-limit=no \
|
42
|
-
--partial-loads-ok=yes --undef-value-errors=no"
|
35
|
+
end
|
36
|
+
end
|
43
37
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
system cmdline
|
38
|
+
test_task_cfg = Proc.new do |t|
|
39
|
+
t.libs << 'test'
|
40
|
+
t.libs << 'lib'
|
41
|
+
t.test_files = FileList['test/**/*_test.rb']
|
49
42
|
end
|
50
43
|
|
51
|
-
|
52
|
-
|
53
|
-
cmdline = "valgrind #{VALGRIND_BASIC_OPTS} --leak-check=full ruby test/test_leak.rb"
|
54
|
-
puts cmdline
|
55
|
-
require 'open3'
|
56
|
-
_, stderr = Open3.capture3(cmdline)
|
44
|
+
Rake::TestTask.new(:test, &test_task_cfg)
|
45
|
+
ValgrindTestTask.new(:'test:valgrind', &test_task_cfg)
|
57
46
|
|
58
|
-
|
59
|
-
stderr.split("\n").each do |line|
|
47
|
+
task :default => [:compile, :test]
|
60
48
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
section = ""
|
68
|
-
else
|
69
|
-
section << line << "\n"
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
49
|
+
gem = Gem::Specification.load( File.dirname(__FILE__) + '/mini_racer.gemspec' )
|
50
|
+
Rake::ExtensionTask.new( 'mini_racer_loader', gem ) do |ext|
|
51
|
+
ext.name = 'sq_mini_racer_loader'
|
52
|
+
end
|
53
|
+
Rake::ExtensionTask.new( 'mini_racer_extension', gem ) do |ext|
|
54
|
+
ext.name = 'sq_mini_racer_extension'
|
73
55
|
end
|
74
56
|
|
75
57
|
desc 'run clang-tidy linter on mini_racer_extension.cc'
|
data/azure-template.yml
CHANGED
@@ -66,7 +66,7 @@ jobs:
|
|
66
66
|
displayName: Print logs
|
67
67
|
condition: failed()
|
68
68
|
|
69
|
-
- script: $(bundle) exec rake test
|
69
|
+
- script: TESTOPTS=--junit $(bundle) exec rake test
|
70
70
|
displayName: Run tests
|
71
71
|
env:
|
72
72
|
${{ if eq(parameters.with_mini_racer, true) }}:
|
@@ -74,6 +74,15 @@ jobs:
|
|
74
74
|
${{ if eq(parameters.with_therubyracer, true) }}:
|
75
75
|
LOAD_THERUBYRACER: 1
|
76
76
|
|
77
|
+
- task: PublishTestResults@2
|
78
|
+
displayName: Publish test results
|
79
|
+
condition: succeededOrFailed()
|
80
|
+
inputs:
|
81
|
+
testResultsFormat: 'JUnit'
|
82
|
+
testResultsFiles: '**/report.xml'
|
83
|
+
searchFolder: '$(System.DefaultWorkingDirectory)'
|
84
|
+
mergeTestResults: true
|
85
|
+
|
77
86
|
- ${{ if and(eq(parameters.imageName, 'ubuntu-18.04'), eq(parameters.with_mini_racer, false), eq(parameters.with_therubyracer, false)) }}:
|
78
87
|
- script: $(bundle) exec rake test:valgrind
|
79
88
|
displayName: Run tests w/ valgrind
|
@@ -26,14 +26,11 @@ def cppflags_add_cpu_extension!
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def libv8_gem_name
|
29
|
-
#return "libv8-solaris" if IS_SOLARIS
|
30
|
-
#return "libv8-alpine" if IS_LINUX_MUSL
|
31
|
-
|
32
29
|
'libv8-node'
|
33
30
|
end
|
34
31
|
|
35
32
|
def libv8_requirement
|
36
|
-
'~>
|
33
|
+
'~> 15.5.1.0.beta1'
|
37
34
|
end
|
38
35
|
|
39
36
|
def libv8_basename(version)
|
@@ -207,19 +204,25 @@ $CPPFLAGS += " -rdynamic" unless $CPPFLAGS.split.include? "-rdynamic"
|
|
207
204
|
$CPPFLAGS += " -fPIC" unless $CPPFLAGS.split.include? "-rdynamic" or IS_DARWIN
|
208
205
|
$CPPFLAGS += " -std=c++0x"
|
209
206
|
$CPPFLAGS += " -fpermissive"
|
207
|
+
$CPPFLAGS += " -DV8_COMPRESS_POINTERS"
|
208
|
+
$CPPFLAGS += " -fvisibility=hidden "
|
210
209
|
cppflags_add_frame_pointer!
|
211
210
|
cppflags_add_cpu_extension!
|
212
211
|
|
213
212
|
$CPPFLAGS += " -Wno-reserved-user-defined-literal" if IS_DARWIN
|
214
213
|
|
215
214
|
$LDFLAGS.insert(0, " -stdlib=libc++ ") if IS_DARWIN
|
216
|
-
|
217
|
-
$LDFLAGS += " -Wl
|
215
|
+
# causes problems on some systems
|
216
|
+
# $LDFLAGS += " -Wl,--no-undefined " unless IS_DARWIN
|
217
|
+
# $LDFLAGS += " -Wl,-undefined,error " if IS_DARWIN
|
218
218
|
|
219
219
|
if ENV['CXX']
|
220
220
|
puts "SETTING CXX"
|
221
221
|
CONFIG['CXX'] = ENV['CXX']
|
222
222
|
end
|
223
|
+
# 1.9 has no $CXXFLAGS
|
224
|
+
$CPPFLAGS += " #{ENV['CPPFLAGS']}" if ENV['CPPFLAGS']
|
225
|
+
$LDFLAGS += " #{ENV['LDFLAGS']}" if ENV['LDFLAGS']
|
223
226
|
|
224
227
|
CXX11_TEST = <<EOS
|
225
228
|
#if __cplusplus <= 199711L
|
@@ -34,7 +34,11 @@
|
|
34
34
|
#include <atomic>
|
35
35
|
#include <math.h>
|
36
36
|
#include "compat.hpp"
|
37
|
+
#ifdef __x86_64__
|
37
38
|
#include "simdutf8check.h"
|
39
|
+
#endif
|
40
|
+
|
41
|
+
#include <time.h>
|
38
42
|
|
39
43
|
using namespace v8;
|
40
44
|
|
@@ -49,6 +53,7 @@ public:
|
|
49
53
|
ArrayBuffer::Allocator* allocator;
|
50
54
|
StartupData* startup_data;
|
51
55
|
bool interrupted;
|
56
|
+
bool added_gc_cb;
|
52
57
|
pid_t pid;
|
53
58
|
VALUE mutex;
|
54
59
|
|
@@ -66,15 +71,12 @@ public:
|
|
66
71
|
|
67
72
|
|
68
73
|
IsolateInfo() : isolate(nullptr), allocator(nullptr), startup_data(nullptr),
|
69
|
-
interrupted(false), pid(getpid()), refs_count(0) {
|
74
|
+
interrupted(false), added_gc_cb(false), pid(getpid()), refs_count(0) {
|
70
75
|
VALUE cMutex = rb_const_get(rb_cThread, rb_intern("Mutex"));
|
71
76
|
mutex = rb_class_new_instance(0, nullptr, cMutex);
|
72
77
|
}
|
73
78
|
|
74
|
-
~IsolateInfo()
|
75
|
-
void free_isolate(IsolateInfo*);
|
76
|
-
free_isolate(this);
|
77
|
-
}
|
79
|
+
~IsolateInfo();
|
78
80
|
|
79
81
|
void init(SnapshotInfo* snapshot_info = nullptr);
|
80
82
|
|
@@ -151,6 +153,7 @@ typedef struct {
|
|
151
153
|
Local<Function> fun;
|
152
154
|
Local<Value> *argv;
|
153
155
|
EvalResult result;
|
156
|
+
size_t max_memory;
|
154
157
|
} FunctionCall;
|
155
158
|
|
156
159
|
enum IsolateFlags {
|
@@ -179,6 +182,11 @@ static VALUE rb_cDateTime = Qnil;
|
|
179
182
|
static std::unique_ptr<Platform> current_platform = NULL;
|
180
183
|
static std::mutex platform_lock;
|
181
184
|
|
185
|
+
static pthread_attr_t *thread_attr_p;
|
186
|
+
static pthread_rwlock_t exit_lock = PTHREAD_RWLOCK_INITIALIZER;
|
187
|
+
static bool ruby_exiting = false; // guarded by exit_lock
|
188
|
+
static bool single_threaded = false;
|
189
|
+
|
182
190
|
static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
183
191
|
bool platform_already_initialized = false;
|
184
192
|
|
@@ -190,6 +198,9 @@ static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
|
190
198
|
platform_lock.lock();
|
191
199
|
|
192
200
|
if (current_platform == NULL) {
|
201
|
+
if (!strcmp(RSTRING_PTR(flag_as_str), "--single_threaded")) {
|
202
|
+
single_threaded = true;
|
203
|
+
}
|
193
204
|
V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), (int)RSTRING_LEN(flag_as_str));
|
194
205
|
} else {
|
195
206
|
platform_already_initialized = true;
|
@@ -256,11 +267,13 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
256
267
|
Local<Value> local_value = v8res.ToLocalChecked();
|
257
268
|
if ((local_value->IsObject() || local_value->IsArray()) &&
|
258
269
|
!local_value->IsDate() && !local_value->IsFunction()) {
|
259
|
-
Local<Object> JSON = context->Global()->Get(
|
260
|
-
|
270
|
+
Local<Object> JSON = context->Global()->Get(
|
271
|
+
context, String::NewFromUtf8Literal(isolate, "JSON"))
|
272
|
+
.ToLocalChecked().As<Object>();
|
261
273
|
|
262
|
-
Local<Function> stringify = JSON->Get(
|
263
|
-
|
274
|
+
Local<Function> stringify = JSON->Get(
|
275
|
+
context, v8::String::NewFromUtf8Literal(isolate, "stringify"))
|
276
|
+
.ToLocalChecked().As<Function>();
|
264
277
|
|
265
278
|
Local<Object> object = local_value->ToObject(context).ToLocalChecked();
|
266
279
|
const unsigned argc = 1;
|
@@ -314,7 +327,7 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
314
327
|
} else if(trycatch.HasTerminated()) {
|
315
328
|
evalRes.terminated = true;
|
316
329
|
evalRes.message = new Persistent<Value>();
|
317
|
-
Local<String> tmp = String::
|
330
|
+
Local<String> tmp = String::NewFromUtf8Literal(isolate, "JavaScript was terminated (either by timeout or explicitly)");
|
318
331
|
evalRes.message->Reset(isolate, tmp);
|
319
332
|
}
|
320
333
|
if (!trycatch.StackTrace(context).IsEmpty()) {
|
@@ -335,7 +348,8 @@ nogvl_context_eval(void* arg) {
|
|
335
348
|
|
336
349
|
EvalParams* eval_params = (EvalParams*)arg;
|
337
350
|
EvalResult* result = eval_params->result;
|
338
|
-
|
351
|
+
IsolateInfo* isolate_info = eval_params->context_info->isolate_info;
|
352
|
+
Isolate* isolate = isolate_info->isolate;
|
339
353
|
|
340
354
|
Isolate::Scope isolate_scope(isolate);
|
341
355
|
HandleScope handle_scope(isolate);
|
@@ -379,7 +393,10 @@ nogvl_context_eval(void* arg) {
|
|
379
393
|
// parsing successful
|
380
394
|
if (eval_params->max_memory > 0) {
|
381
395
|
isolate->SetData(MEM_SOFTLIMIT_VALUE, &eval_params->max_memory);
|
396
|
+
if (!isolate_info->added_gc_cb) {
|
382
397
|
isolate->AddGCEpilogueCallback(gc_callback);
|
398
|
+
isolate_info->added_gc_cb = true;
|
399
|
+
}
|
383
400
|
}
|
384
401
|
|
385
402
|
maybe_value = parsed_script.ToLocalChecked()->Run(context);
|
@@ -392,6 +409,12 @@ nogvl_context_eval(void* arg) {
|
|
392
409
|
return 0;
|
393
410
|
}
|
394
411
|
|
412
|
+
static VALUE new_empty_failed_conv_obj() {
|
413
|
+
// TODO isolate code that translates execption to ruby
|
414
|
+
// exception so we can properly return it
|
415
|
+
return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
|
416
|
+
}
|
417
|
+
|
395
418
|
// assumes isolate locking is in place
|
396
419
|
static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
397
420
|
Local<Value> value) {
|
@@ -423,8 +446,11 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
423
446
|
VALUE rb_array = rb_ary_new();
|
424
447
|
Local<Array> arr = Local<Array>::Cast(value);
|
425
448
|
for(uint32_t i=0; i < arr->Length(); i++) {
|
426
|
-
|
427
|
-
|
449
|
+
MaybeLocal<Value> element = arr->Get(context, i);
|
450
|
+
if (element.IsEmpty()) {
|
451
|
+
continue;
|
452
|
+
}
|
453
|
+
VALUE rb_elem = convert_v8_to_ruby(isolate, context, element.ToLocalChecked());
|
428
454
|
if (rb_funcall(rb_elem, rb_intern("class"), 0) == rb_cFailedV8Conversion) {
|
429
455
|
return rb_elem;
|
430
456
|
}
|
@@ -454,26 +480,47 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
454
480
|
if (!maybe_props.IsEmpty()) {
|
455
481
|
Local<Array> props = maybe_props.ToLocalChecked();
|
456
482
|
for(uint32_t i=0; i < props->Length(); i++) {
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
483
|
+
MaybeLocal<Value> key = props->Get(context, i);
|
484
|
+
if (key.IsEmpty()) {
|
485
|
+
return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
|
486
|
+
}
|
487
|
+
VALUE rb_key = convert_v8_to_ruby(isolate, context, key.ToLocalChecked());
|
461
488
|
|
462
|
-
|
463
|
-
//
|
464
|
-
|
465
|
-
|
489
|
+
MaybeLocal<Value> prop_value = object->Get(context, key.ToLocalChecked());
|
490
|
+
// this may have failed due to Get raising
|
491
|
+
if (prop_value.IsEmpty() || trycatch.HasCaught()) {
|
492
|
+
return new_empty_failed_conv_obj();
|
466
493
|
}
|
467
494
|
|
468
|
-
VALUE rb_value = convert_v8_to_ruby(
|
495
|
+
VALUE rb_value = convert_v8_to_ruby(
|
496
|
+
isolate, context, prop_value.ToLocalChecked());
|
469
497
|
rb_hash_aset(rb_hash, rb_key, rb_value);
|
470
498
|
}
|
471
499
|
}
|
472
500
|
return rb_hash;
|
473
501
|
}
|
474
502
|
|
475
|
-
|
476
|
-
|
503
|
+
if (value->IsSymbol()) {
|
504
|
+
v8::String::Utf8Value symbol_name(isolate,
|
505
|
+
Local<Symbol>::Cast(value)->Name());
|
506
|
+
|
507
|
+
VALUE str_symbol = rb_enc_str_new(
|
508
|
+
*symbol_name,
|
509
|
+
symbol_name.length(),
|
510
|
+
rb_enc_find("utf-8")
|
511
|
+
);
|
512
|
+
|
513
|
+
return ID2SYM(rb_intern_str(str_symbol));
|
514
|
+
}
|
515
|
+
|
516
|
+
MaybeLocal<String> rstr_maybe = value->ToString(context);
|
517
|
+
|
518
|
+
if (rstr_maybe.IsEmpty()) {
|
519
|
+
return Qnil;
|
520
|
+
} else {
|
521
|
+
Local<String> rstr = rstr_maybe.ToLocalChecked();
|
522
|
+
return rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
|
523
|
+
}
|
477
524
|
}
|
478
525
|
|
479
526
|
static VALUE convert_v8_to_ruby(Isolate* isolate,
|
@@ -519,11 +566,13 @@ static inline Local<Value> convert_ruby_str_to_v8(
|
|
519
566
|
static const rb_encoding *usascii_enc = rb_usascii_encoding();
|
520
567
|
static const rb_encoding *latin1_enc = rb_enc_find("ISO-8859-1");
|
521
568
|
assert(latin1_enc != nullptr);
|
569
|
+
#ifdef __x86_64__
|
522
570
|
#ifndef __AVX2__
|
523
571
|
# define validate_utf8 validate_utf8_fast
|
524
572
|
#else
|
525
573
|
static const (*validate_utf8)(const char *, size_t) =
|
526
574
|
best_utf8_validate_func();
|
575
|
+
#endif
|
527
576
|
#endif
|
528
577
|
|
529
578
|
rb_encoding *enc = rb_enc_get(value);
|
@@ -532,8 +581,12 @@ static inline Local<Value> convert_ruby_str_to_v8(
|
|
532
581
|
if (len < 0 || len > INT_MAX) {
|
533
582
|
return Null(isolate);
|
534
583
|
}
|
584
|
+
#ifdef __x86_64__
|
535
585
|
bool is_valid_utf8 = enc == utf8_enc &&
|
536
586
|
validate_utf8(str, static_cast<size_t>(len));
|
587
|
+
#else
|
588
|
+
bool is_valid_utf8 = false;
|
589
|
+
#endif
|
537
590
|
|
538
591
|
MaybeLocal<String> v8str;
|
539
592
|
int int_len = static_cast<int>(len);
|
@@ -596,7 +649,11 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
596
649
|
case T_FLOAT:
|
597
650
|
return scope.Escape(Number::New(isolate, NUM2DBL(value)));
|
598
651
|
case T_STRING:
|
599
|
-
|
652
|
+
//#ifndef __x86_64__
|
653
|
+
// return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, (int)RSTRING_LEN(value)).ToLocalChecked());
|
654
|
+
//#else
|
655
|
+
return scope.Escape(convert_ruby_str_to_v8(scope, isolate, value));
|
656
|
+
//#endif
|
600
657
|
case T_NIL:
|
601
658
|
return scope.Escape(Null(isolate));
|
602
659
|
case T_TRUE:
|
@@ -608,7 +665,7 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
608
665
|
length = RARRAY_LEN(value);
|
609
666
|
array = Array::New(isolate, (int)length);
|
610
667
|
for(i=0; i<length; i++) {
|
611
|
-
|
668
|
+
array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
|
612
669
|
}
|
613
670
|
return scope.Escape(array);
|
614
671
|
}
|
@@ -619,7 +676,7 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
619
676
|
length = RARRAY_LEN(hash_as_array);
|
620
677
|
for(i=0; i<length; i++) {
|
621
678
|
pair = rb_ary_entry(hash_as_array, i);
|
622
|
-
|
679
|
+
object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
|
623
680
|
convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 1)));
|
624
681
|
}
|
625
682
|
return scope.Escape(object);
|
@@ -627,7 +684,11 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
627
684
|
case T_SYMBOL:
|
628
685
|
{
|
629
686
|
value = rb_funcall(value, rb_intern("to_s"), 0);
|
630
|
-
|
687
|
+
//#ifndef __x86_64__
|
688
|
+
// return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, (int)RSTRING_LEN(value)).ToLocalChecked());
|
689
|
+
//#else
|
690
|
+
return scope.Escape(convert_ruby_str_to_v8(scope, isolate, value));
|
691
|
+
//#endif
|
631
692
|
}
|
632
693
|
case T_DATA:
|
633
694
|
{
|
@@ -659,10 +720,15 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
659
720
|
if (rb_respond_to(value, rb_intern("to_s"))) {
|
660
721
|
// TODO: if this throws we're screwed
|
661
722
|
value = rb_funcall(value, rb_intern("to_s"), 0);
|
723
|
+
//#ifndef __x86_64__
|
724
|
+
// return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, (int)RSTRING_LEN(value)).ToLocalChecked());
|
725
|
+
//#else
|
662
726
|
return scope.Escape(convert_ruby_str_to_v8(scope, isolate, value));
|
727
|
+
//#endif
|
663
728
|
}
|
664
|
-
|
665
|
-
|
729
|
+
return scope.Escape(
|
730
|
+
String::NewFromUtf8Literal(isolate, "Undefined Conversion"));
|
731
|
+
}
|
666
732
|
}
|
667
733
|
}
|
668
734
|
|
@@ -675,53 +741,43 @@ static void unblock_eval(void *ptr) {
|
|
675
741
|
* The implementations of the run_extra_code(), create_snapshot_data_blob() and
|
676
742
|
* warm_up_snapshot_data_blob() functions have been derived from V8's test suite.
|
677
743
|
*/
|
678
|
-
bool run_extra_code(Isolate *isolate, Local<v8::Context> context,
|
744
|
+
static bool run_extra_code(Isolate *isolate, Local<v8::Context> context,
|
679
745
|
const char *utf8_source, const char *name) {
|
680
746
|
Context::Scope context_scope(context);
|
681
747
|
TryCatch try_catch(isolate);
|
682
748
|
Local<String> source_string;
|
683
|
-
if (!String::NewFromUtf8(isolate, utf8_source
|
684
|
-
NewStringType::kNormal)
|
685
|
-
.ToLocal(&source_string)) {
|
749
|
+
if (!String::NewFromUtf8(isolate, utf8_source).ToLocal(&source_string)) {
|
686
750
|
return false;
|
687
751
|
}
|
688
|
-
Local<
|
689
|
-
|
690
|
-
.ToLocalChecked();
|
752
|
+
Local<String> resource_name =
|
753
|
+
String::NewFromUtf8(isolate, name).ToLocalChecked();
|
691
754
|
ScriptOrigin origin(resource_name);
|
692
755
|
ScriptCompiler::Source source(source_string, origin);
|
693
756
|
Local<Script> script;
|
694
757
|
if (!ScriptCompiler::Compile(context, &source).ToLocal(&script))
|
695
758
|
return false;
|
696
|
-
if (script->Run(context).IsEmpty())
|
697
|
-
return false;
|
698
|
-
// CHECK(!try_catch.HasCaught());
|
759
|
+
if (script->Run(context).IsEmpty()) return false;
|
699
760
|
return true;
|
700
761
|
}
|
701
762
|
|
702
|
-
StartupData
|
763
|
+
static StartupData
|
703
764
|
create_snapshot_data_blob(const char *embedded_source = nullptr) {
|
704
|
-
|
705
|
-
|
706
|
-
|
765
|
+
Isolate *isolate = Isolate::Allocate();
|
766
|
+
|
767
|
+
// Optionally run a script to embed, and serialize to create a snapshot blob.
|
768
|
+
SnapshotCreator snapshot_creator(isolate);
|
707
769
|
{
|
708
|
-
SnapshotCreator snapshot_creator;
|
709
|
-
Isolate *isolate = snapshot_creator.GetIsolate();
|
710
|
-
{
|
711
770
|
HandleScope scope(isolate);
|
712
|
-
|
771
|
+
Local<v8::Context> context = v8::Context::New(isolate);
|
713
772
|
if (embedded_source != nullptr &&
|
714
|
-
!run_extra_code(isolate, context, embedded_source,
|
715
|
-
|
716
|
-
return result;
|
773
|
+
!run_extra_code(isolate, context, embedded_source, "<embedded>")) {
|
774
|
+
return {};
|
717
775
|
}
|
718
776
|
snapshot_creator.SetDefaultContext(context);
|
719
777
|
}
|
720
|
-
|
778
|
+
return snapshot_creator.CreateBlob(
|
721
779
|
SnapshotCreator::FunctionCodeHandling::kClear);
|
722
780
|
}
|
723
|
-
return result;
|
724
|
-
}
|
725
781
|
|
726
782
|
StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
|
727
783
|
const char *warmup_source) {
|
@@ -868,6 +924,29 @@ static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
|
|
868
924
|
return isolate_info->isolate->IdleNotificationDeadline(now + duration) ? Qtrue : Qfalse;
|
869
925
|
}
|
870
926
|
|
927
|
+
static VALUE rb_isolate_low_memory_notification(VALUE self) {
|
928
|
+
IsolateInfo* isolate_info;
|
929
|
+
Data_Get_Struct(self, IsolateInfo, isolate_info);
|
930
|
+
|
931
|
+
if (current_platform == NULL) return Qfalse;
|
932
|
+
|
933
|
+
isolate_info->isolate->LowMemoryNotification();
|
934
|
+
return Qnil;
|
935
|
+
}
|
936
|
+
|
937
|
+
static VALUE rb_isolate_pump_message_loop(VALUE self) {
|
938
|
+
IsolateInfo* isolate_info;
|
939
|
+
Data_Get_Struct(self, IsolateInfo, isolate_info);
|
940
|
+
|
941
|
+
if (current_platform == NULL) return Qfalse;
|
942
|
+
|
943
|
+
if (platform::PumpMessageLoop(current_platform.get(), isolate_info->isolate)){
|
944
|
+
return Qtrue;
|
945
|
+
} else {
|
946
|
+
return Qfalse;
|
947
|
+
}
|
948
|
+
}
|
949
|
+
|
871
950
|
static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
|
872
951
|
ContextInfo* context_info;
|
873
952
|
Data_Get_Struct(self, ContextInfo, context_info);
|
@@ -1142,7 +1221,9 @@ gvl_ruby_callback(void* data) {
|
|
1142
1221
|
callback_data.failed = false;
|
1143
1222
|
|
1144
1223
|
if ((bool)args->GetIsolate()->GetData(DO_TERMINATE) == true) {
|
1145
|
-
args->GetIsolate()->ThrowException(
|
1224
|
+
args->GetIsolate()->ThrowException(
|
1225
|
+
String::NewFromUtf8Literal(args->GetIsolate(),
|
1226
|
+
"Terminated execution during transition from Ruby to JS"));
|
1146
1227
|
args->GetIsolate()->TerminateExecution();
|
1147
1228
|
if (length > 0) {
|
1148
1229
|
rb_ary_clear(ruby_args);
|
@@ -1156,7 +1237,7 @@ gvl_ruby_callback(void* data) {
|
|
1156
1237
|
|
1157
1238
|
if(callback_data.failed) {
|
1158
1239
|
rb_iv_set(parent, "@current_exception", result);
|
1159
|
-
args->GetIsolate()->ThrowException(String::
|
1240
|
+
args->GetIsolate()->ThrowException(String::NewFromUtf8Literal(args->GetIsolate(), "Ruby exception"));
|
1160
1241
|
}
|
1161
1242
|
else {
|
1162
1243
|
HandleScope scope(args->GetIsolate());
|
@@ -1231,7 +1312,9 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1231
1312
|
|
1232
1313
|
if (parent_object == Qnil) {
|
1233
1314
|
context->Global()->Set(
|
1234
|
-
|
1315
|
+
context,
|
1316
|
+
v8_str,
|
1317
|
+
FunctionTemplate::New(isolate, ruby_callback, external)
|
1235
1318
|
->GetFunction(context)
|
1236
1319
|
.ToLocalChecked());
|
1237
1320
|
|
@@ -1244,7 +1327,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1244
1327
|
|
1245
1328
|
MaybeLocal<Script> parsed_script = Script::Compile(context, eval);
|
1246
1329
|
if (parsed_script.IsEmpty()) {
|
1247
|
-
|
1330
|
+
parse_error = true;
|
1248
1331
|
} else {
|
1249
1332
|
MaybeLocal<Value> maybe_value =
|
1250
1333
|
parsed_script.ToLocalChecked()->Run(context);
|
@@ -1254,11 +1337,12 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1254
1337
|
Local<Value> value = maybe_value.ToLocalChecked();
|
1255
1338
|
if (value->IsObject()) {
|
1256
1339
|
value.As<Object>()->Set(
|
1257
|
-
|
1258
|
-
|
1340
|
+
context,
|
1341
|
+
v8_str,
|
1342
|
+
FunctionTemplate::New(isolate, ruby_callback, external)
|
1259
1343
|
->GetFunction(context)
|
1260
1344
|
.ToLocalChecked());
|
1261
|
-
|
1345
|
+
attach_error = false;
|
1262
1346
|
}
|
1263
1347
|
}
|
1264
1348
|
}
|
@@ -1288,35 +1372,39 @@ static VALUE rb_context_isolate_mutex(VALUE self) {
|
|
1288
1372
|
return context_info->isolate_info->mutex;
|
1289
1373
|
}
|
1290
1374
|
|
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");
|
1375
|
+
IsolateInfo::~IsolateInfo() {
|
1376
|
+
if (isolate) {
|
1377
|
+
if (this->interrupted) {
|
1378
|
+
fprintf(stderr, "WARNING: V8 isolate was interrupted by Ruby, "
|
1379
|
+
"it can not be disposed and memory will not be "
|
1380
|
+
"reclaimed till the Ruby process exits.\n");
|
1300
1381
|
} else {
|
1301
|
-
|
1302
|
-
|
1303
|
-
|
1382
|
+
if (this->pid != getpid() && !single_threaded) {
|
1383
|
+
fprintf(stderr, "WARNING: V8 isolate was forked, "
|
1384
|
+
"it can not be disposed and "
|
1385
|
+
"memory will not be reclaimed "
|
1386
|
+
"till the Ruby process exits.\n"
|
1387
|
+
"It is VERY likely your process will hang.\n"
|
1388
|
+
"If you wish to use v8 in forked environment "
|
1389
|
+
"please ensure the platform is initialized with:\n"
|
1390
|
+
"MiniRacer::Platform.set_flags! :single_threaded\n"
|
1391
|
+
);
|
1304
1392
|
} else {
|
1305
|
-
|
1393
|
+
isolate->Dispose();
|
1306
1394
|
}
|
1307
1395
|
}
|
1308
|
-
|
1396
|
+
isolate = nullptr;
|
1309
1397
|
}
|
1310
1398
|
|
1311
|
-
if (
|
1312
|
-
delete[]
|
1313
|
-
delete
|
1399
|
+
if (startup_data) {
|
1400
|
+
delete[] startup_data->data;
|
1401
|
+
delete startup_data;
|
1314
1402
|
}
|
1315
1403
|
|
1316
|
-
delete
|
1404
|
+
delete allocator;
|
1317
1405
|
}
|
1318
1406
|
|
1319
|
-
static void
|
1407
|
+
static void free_context_raw(void *arg) {
|
1320
1408
|
ContextInfo* context_info = (ContextInfo*)arg;
|
1321
1409
|
IsolateInfo* isolate_info = context_info->isolate_info;
|
1322
1410
|
Persistent<Context>* context = context_info->context;
|
@@ -1333,6 +1421,20 @@ static void *free_context_raw(void* arg) {
|
|
1333
1421
|
}
|
1334
1422
|
|
1335
1423
|
xfree(context_info);
|
1424
|
+
}
|
1425
|
+
|
1426
|
+
static void *free_context_thr(void* arg) {
|
1427
|
+
if (pthread_rwlock_tryrdlock(&exit_lock) != 0) {
|
1428
|
+
return NULL;
|
1429
|
+
}
|
1430
|
+
if (ruby_exiting) {
|
1431
|
+
return NULL;
|
1432
|
+
}
|
1433
|
+
|
1434
|
+
free_context_raw(arg);
|
1435
|
+
|
1436
|
+
pthread_rwlock_unlock(&exit_lock);
|
1437
|
+
|
1336
1438
|
return NULL;
|
1337
1439
|
}
|
1338
1440
|
|
@@ -1347,22 +1449,17 @@ static void free_context(ContextInfo* context_info) {
|
|
1347
1449
|
|
1348
1450
|
if (isolate_info && isolate_info->refs() > 1) {
|
1349
1451
|
pthread_t free_context_thread;
|
1350
|
-
|
1452
|
+
if (pthread_create(&free_context_thread, thread_attr_p,
|
1453
|
+
free_context_thr, (void*)context_info_copy)) {
|
1351
1454
|
fprintf(stderr, "WARNING failed to release memory in MiniRacer, thread to release could not be created, process will leak memory\n");
|
1352
1455
|
}
|
1353
|
-
|
1354
1456
|
} else {
|
1355
1457
|
free_context_raw(context_info_copy);
|
1356
1458
|
}
|
1357
1459
|
|
1358
|
-
if (context_info->context && isolate_info && isolate_info->isolate) {
|
1359
1460
|
context_info->context = NULL;
|
1360
|
-
}
|
1361
|
-
|
1362
|
-
if (isolate_info) {
|
1363
1461
|
context_info->isolate_info = NULL;
|
1364
1462
|
}
|
1365
|
-
}
|
1366
1463
|
|
1367
1464
|
static void deallocate_isolate(void* data) {
|
1368
1465
|
|
@@ -1376,7 +1473,7 @@ static void mark_isolate(void* data) {
|
|
1376
1473
|
isolate_info->mark();
|
1377
1474
|
}
|
1378
1475
|
|
1379
|
-
void deallocate(void* data) {
|
1476
|
+
static void deallocate(void* data) {
|
1380
1477
|
ContextInfo* context_info = (ContextInfo*)data;
|
1381
1478
|
|
1382
1479
|
free_context(context_info);
|
@@ -1391,22 +1488,22 @@ static void mark_context(void* data) {
|
|
1391
1488
|
}
|
1392
1489
|
}
|
1393
1490
|
|
1394
|
-
void deallocate_external_function(void * data) {
|
1491
|
+
static void deallocate_external_function(void * data) {
|
1395
1492
|
xfree(data);
|
1396
1493
|
}
|
1397
1494
|
|
1398
|
-
void deallocate_snapshot(void * data) {
|
1495
|
+
static void deallocate_snapshot(void * data) {
|
1399
1496
|
SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
|
1400
1497
|
delete[] snapshot_info->data;
|
1401
1498
|
xfree(snapshot_info);
|
1402
1499
|
}
|
1403
1500
|
|
1404
|
-
VALUE allocate_external_function(VALUE klass) {
|
1501
|
+
static VALUE allocate_external_function(VALUE klass) {
|
1405
1502
|
VALUE* self = ALLOC(VALUE);
|
1406
1503
|
return Data_Wrap_Struct(klass, NULL, deallocate_external_function, (void*)self);
|
1407
1504
|
}
|
1408
1505
|
|
1409
|
-
VALUE allocate(VALUE klass) {
|
1506
|
+
static VALUE allocate(VALUE klass) {
|
1410
1507
|
ContextInfo* context_info = ALLOC(ContextInfo);
|
1411
1508
|
context_info->isolate_info = NULL;
|
1412
1509
|
context_info->context = NULL;
|
@@ -1414,7 +1511,7 @@ VALUE allocate(VALUE klass) {
|
|
1414
1511
|
return Data_Wrap_Struct(klass, mark_context, deallocate, (void*)context_info);
|
1415
1512
|
}
|
1416
1513
|
|
1417
|
-
VALUE allocate_snapshot(VALUE klass) {
|
1514
|
+
static VALUE allocate_snapshot(VALUE klass) {
|
1418
1515
|
SnapshotInfo* snapshot_info = ALLOC(SnapshotInfo);
|
1419
1516
|
snapshot_info->data = NULL;
|
1420
1517
|
snapshot_info->raw_size = 0;
|
@@ -1422,7 +1519,7 @@ VALUE allocate_snapshot(VALUE klass) {
|
|
1422
1519
|
return Data_Wrap_Struct(klass, NULL, deallocate_snapshot, (void*)snapshot_info);
|
1423
1520
|
}
|
1424
1521
|
|
1425
|
-
VALUE allocate_isolate(VALUE klass) {
|
1522
|
+
static VALUE allocate_isolate(VALUE klass) {
|
1426
1523
|
IsolateInfo* isolate_info = new IsolateInfo();
|
1427
1524
|
|
1428
1525
|
return Data_Wrap_Struct(klass, mark_isolate, deallocate_isolate, (void*)isolate_info);
|
@@ -1519,6 +1616,8 @@ rb_heap_snapshot(VALUE self, VALUE file) {
|
|
1519
1616
|
FileOutputStream stream(fp);
|
1520
1617
|
snap->Serialize(&stream, HeapSnapshot::kJSON);
|
1521
1618
|
|
1619
|
+
fflush(fp);
|
1620
|
+
|
1522
1621
|
const_cast<HeapSnapshot*>(snap)->Delete();
|
1523
1622
|
|
1524
1623
|
return Qtrue;
|
@@ -1576,13 +1675,23 @@ nogvl_context_call(void *args) {
|
|
1576
1675
|
if (!call) {
|
1577
1676
|
return 0;
|
1578
1677
|
}
|
1579
|
-
|
1678
|
+
IsolateInfo *isolate_info = call->context_info->isolate_info;
|
1679
|
+
Isolate* isolate = isolate_info->isolate;
|
1580
1680
|
|
1581
1681
|
// in gvl flag
|
1582
1682
|
isolate->SetData(IN_GVL, (void*)false);
|
1583
1683
|
// terminate ASAP
|
1584
1684
|
isolate->SetData(DO_TERMINATE, (void*)false);
|
1585
1685
|
|
1686
|
+
if (call->max_memory > 0) {
|
1687
|
+
isolate->SetData(MEM_SOFTLIMIT_VALUE, &call->max_memory);
|
1688
|
+
isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)false);
|
1689
|
+
if (!isolate_info->added_gc_cb) {
|
1690
|
+
isolate->AddGCEpilogueCallback(gc_callback);
|
1691
|
+
isolate_info->added_gc_cb = true;
|
1692
|
+
}
|
1693
|
+
}
|
1694
|
+
|
1586
1695
|
Isolate::Scope isolate_scope(isolate);
|
1587
1696
|
EscapableHandleScope handle_scope(isolate);
|
1588
1697
|
TryCatch trycatch(isolate);
|
@@ -1640,6 +1749,13 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1640
1749
|
call_argv = argv + 1;
|
1641
1750
|
}
|
1642
1751
|
|
1752
|
+
call.max_memory = 0;
|
1753
|
+
VALUE mem_softlimit = rb_iv_get(self, "@max_memory");
|
1754
|
+
if (mem_softlimit != Qnil) {
|
1755
|
+
unsigned long sl_int = NUM2ULONG(mem_softlimit);
|
1756
|
+
call.max_memory = (size_t)sl_int;
|
1757
|
+
}
|
1758
|
+
|
1643
1759
|
bool missingFunction = false;
|
1644
1760
|
{
|
1645
1761
|
Locker lock(isolate);
|
@@ -1651,8 +1767,11 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1651
1767
|
|
1652
1768
|
// examples of such usage can be found in
|
1653
1769
|
// https://github.com/v8/v8/blob/36b32aa28db5e993312f4588d60aad5c8330c8a5/test/cctest/test-api.cc#L15711
|
1654
|
-
|
1655
|
-
MaybeLocal<v8::Value> val
|
1770
|
+
MaybeLocal<String> fname = String::NewFromUtf8(isolate, call.function_name);
|
1771
|
+
MaybeLocal<v8::Value> val;
|
1772
|
+
if (!fname.IsEmpty()) {
|
1773
|
+
val = context->Global()->Get(context, fname.ToLocalChecked());
|
1774
|
+
}
|
1656
1775
|
|
1657
1776
|
if (val.IsEmpty() || !val.ToLocalChecked()->IsFunction()) {
|
1658
1777
|
missingFunction = true;
|
@@ -1701,12 +1820,44 @@ static VALUE rb_context_create_isolate_value(VALUE self) {
|
|
1701
1820
|
return Data_Wrap_Struct(rb_cIsolate, NULL, &deallocate_isolate, isolate_info);
|
1702
1821
|
}
|
1703
1822
|
|
1823
|
+
static void set_ruby_exiting(VALUE value) {
|
1824
|
+
(void)value;
|
1825
|
+
|
1826
|
+
int res = pthread_rwlock_wrlock(&exit_lock);
|
1827
|
+
|
1828
|
+
ruby_exiting = true;
|
1829
|
+
if (res == 0) {
|
1830
|
+
pthread_rwlock_unlock(&exit_lock);
|
1831
|
+
}
|
1832
|
+
}
|
1833
|
+
|
1834
|
+
static VALUE rb_monotime(VALUE self) {
|
1835
|
+
struct timespec ts;
|
1836
|
+
if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) {
|
1837
|
+
return INT2FIX(-1);
|
1838
|
+
}
|
1839
|
+
|
1840
|
+
return DBL2NUM(
|
1841
|
+
(double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0);
|
1842
|
+
}
|
1843
|
+
|
1704
1844
|
extern "C" {
|
1705
1845
|
|
1706
|
-
void Init_sq_mini_racer_extension ( void )
|
1846
|
+
__attribute__((visibility("default"))) void Init_sq_mini_racer_extension ( void )
|
1707
1847
|
{
|
1708
|
-
|
1848
|
+
ID sqreen_id = rb_intern("Sqreen");
|
1849
|
+
VALUE rb_mSqreen;
|
1850
|
+
if (rb_const_defined(rb_cObject, sqreen_id)) {
|
1851
|
+
rb_mSqreen = rb_const_get(rb_cObject, sqreen_id);
|
1852
|
+
if (TYPE(rb_mSqreen) != T_MODULE) {
|
1853
|
+
rb_raise(rb_eTypeError, "Sqreen is not a module");
|
1854
|
+
return;
|
1855
|
+
}
|
1856
|
+
} else {
|
1857
|
+
rb_mSqreen = rb_define_module("Sqreen");
|
1858
|
+
}
|
1709
1859
|
VALUE rb_mMiniRacer = rb_define_module_under(rb_mSqreen, "MiniRacer");
|
1860
|
+
rb_define_module_function(rb_mMiniRacer, "monotime", (VALUE(*)(...))&rb_monotime, 0);
|
1710
1861
|
rb_cContext = rb_define_class_under(rb_mMiniRacer, "Context", rb_cObject);
|
1711
1862
|
rb_cSnapshot = rb_define_class_under(rb_mMiniRacer, "Snapshot", rb_cObject);
|
1712
1863
|
rb_cIsolate = rb_define_class_under(rb_mMiniRacer, "Isolate", rb_cObject);
|
@@ -1753,8 +1904,23 @@ extern "C" {
|
|
1753
1904
|
rb_define_private_method(rb_cSnapshot, "load", (VALUE(*)(...))&rb_snapshot_load, 1);
|
1754
1905
|
|
1755
1906
|
rb_define_method(rb_cIsolate, "idle_notification", (VALUE(*)(...))&rb_isolate_idle_notification, 1);
|
1907
|
+
rb_define_method(rb_cIsolate, "low_memory_notification", (VALUE(*)(...))&rb_isolate_low_memory_notification, 0);
|
1908
|
+
rb_define_method(rb_cIsolate, "pump_message_loop", (VALUE(*)(...))&rb_isolate_pump_message_loop, 0);
|
1756
1909
|
rb_define_private_method(rb_cIsolate, "init_with_snapshot",(VALUE(*)(...))&rb_isolate_init_with_snapshot, 1);
|
1757
1910
|
|
1758
1911
|
rb_define_singleton_method(rb_cPlatform, "set_flag_as_str!", (VALUE(*)(...))&rb_platform_set_flag_as_str, 1);
|
1912
|
+
|
1913
|
+
rb_set_end_proc(set_ruby_exiting, Qnil);
|
1914
|
+
|
1915
|
+
static pthread_attr_t attr;
|
1916
|
+
if (pthread_attr_init(&attr) == 0) {
|
1917
|
+
if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == 0) {
|
1918
|
+
thread_attr_p = &attr;
|
1919
|
+
}
|
1920
|
+
}
|
1921
|
+
auto on_fork_for_child = []() {
|
1922
|
+
exit_lock = PTHREAD_RWLOCK_INITIALIZER;
|
1923
|
+
};
|
1924
|
+
pthread_atfork(nullptr, nullptr, on_fork_for_child);
|
1759
1925
|
}
|
1760
1926
|
}
|