sq_mini_racer 0.2.5.0.2 → 0.3.1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
}
|