mini_racer 0.2.8 → 0.4.0
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/.dockerignore +12 -0
- data/.github/workflows/ci.yml +80 -0
- data/.gitignore +1 -0
- data/.travis.yml +16 -9
- data/CHANGELOG +71 -0
- data/Dockerfile +21 -0
- data/README.md +19 -4
- data/Rakefile +1 -0
- data/ext/mini_racer_extension/extconf.rb +10 -2
- data/ext/mini_racer_extension/mini_racer_extension.cc +223 -101
- data/ext/mini_racer_loader/extconf.rb +8 -0
- data/ext/mini_racer_loader/mini_racer_loader.c +123 -0
- data/lib/mini_racer.rb +82 -15
- data/lib/mini_racer/version.rb +4 -1
- data/mini_racer.gemspec +11 -4
- metadata +43 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e73ac3193df3f33b51463ba1125ac4eee312caecd47833c545e098ba91d11ece
|
4
|
+
data.tar.gz: d6d754829b36953d239ba85e84d2fc88c0af3ccbadd3f8d2e4cd9759bd24fab2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 52a0799d84be10bb03774789b3d12d4f0fe2d4159d310b9c6a1ae3dcf878a99cd6b7c786fae813f29754a894ecf5fcc6b7e07834c0aba994a16da5c54d75595b
|
7
|
+
data.tar.gz: bc478bdb582d7b786d28754200063d7ef185396e0da23840f006679bc1af6707f54c7a719918360afa9b24c2b4ab083ca081e2b2d6853095ecd5949a9482ecbd
|
data/.dockerignore
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
name: Test
|
2
|
+
on:
|
3
|
+
- push
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
test-darwin:
|
7
|
+
strategy:
|
8
|
+
fail-fast: false
|
9
|
+
matrix:
|
10
|
+
os:
|
11
|
+
- '10.15'
|
12
|
+
- '11.0'
|
13
|
+
platform:
|
14
|
+
- x86_64
|
15
|
+
# arm64
|
16
|
+
name: Test (darwin)
|
17
|
+
runs-on: macos-${{ matrix.os }}
|
18
|
+
steps:
|
19
|
+
- name: Checkout
|
20
|
+
uses: actions/checkout@v2
|
21
|
+
- name: Bundle
|
22
|
+
run: bundle install
|
23
|
+
- name: Compile
|
24
|
+
run: bundle exec rake compile
|
25
|
+
- name: Test
|
26
|
+
run: bundle exec rake test
|
27
|
+
test-linux:
|
28
|
+
strategy:
|
29
|
+
fail-fast: false
|
30
|
+
matrix:
|
31
|
+
ruby:
|
32
|
+
- '2.4'
|
33
|
+
- '2.5'
|
34
|
+
- '2.6'
|
35
|
+
- '2.7'
|
36
|
+
- '3.0'
|
37
|
+
platform:
|
38
|
+
- amd64
|
39
|
+
- arm64
|
40
|
+
# arm
|
41
|
+
# ppc64le
|
42
|
+
# s390x
|
43
|
+
libc:
|
44
|
+
- gnu
|
45
|
+
- musl
|
46
|
+
name: Test (linux)
|
47
|
+
runs-on: ubuntu-20.04
|
48
|
+
steps:
|
49
|
+
- name: Enable ${{ matrix.platform }} platform
|
50
|
+
id: qemu
|
51
|
+
if: ${{ matrix.platform != 'amd64' }}
|
52
|
+
run: |
|
53
|
+
docker run --privileged --rm tonistiigi/binfmt:latest --install ${{ matrix.platform }} | tee platforms.json
|
54
|
+
echo "::set-output name=platforms::$(cat platforms.json)"
|
55
|
+
- name: Start container
|
56
|
+
id: container
|
57
|
+
run: |
|
58
|
+
case ${{ matrix.libc }} in
|
59
|
+
gnu)
|
60
|
+
echo 'ruby:${{ matrix.ruby }}'
|
61
|
+
;;
|
62
|
+
musl)
|
63
|
+
echo 'ruby:${{ matrix.ruby }}-alpine'
|
64
|
+
;;
|
65
|
+
esac > container_image
|
66
|
+
echo "::set-output name=image::$(cat container_image)"
|
67
|
+
docker run --rm -d -v "${PWD}":"${PWD}" -w "${PWD}" --platform linux/${{ matrix.platform }} $(cat container_image) /bin/sleep 64d | tee container_id
|
68
|
+
docker exec -w "${PWD}" $(cat container_id) uname -a
|
69
|
+
echo "::set-output name=id::$(cat container_id)"
|
70
|
+
- name: Install Alpine system dependencies
|
71
|
+
if: ${{ matrix.libc == 'musl' }}
|
72
|
+
run: docker exec -w "${PWD}" ${{ steps.container.outputs.id }} apk add --no-cache build-base linux-headers bash python2 python3 git curl tar clang binutils-gold
|
73
|
+
- name: Checkout
|
74
|
+
uses: actions/checkout@v2
|
75
|
+
- name: Bundle
|
76
|
+
run: docker exec -w "${PWD}" ${{ steps.container.outputs.id }} bundle install
|
77
|
+
- name: Compile
|
78
|
+
run: docker exec -w "${PWD}" ${{ steps.container.outputs.id }} bundle exec rake compile
|
79
|
+
- name: Test
|
80
|
+
run: docker exec -w "${PWD}" ${{ steps.container.outputs.id }} bundle exec rake test
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,23 +1,30 @@
|
|
1
1
|
language: ruby
|
2
|
+
os: linux
|
2
3
|
rvm:
|
3
|
-
- 2.3
|
4
4
|
- 2.4
|
5
5
|
- 2.5
|
6
6
|
- 2.6
|
7
|
+
- 2.7
|
8
|
+
- 3.0
|
7
9
|
- ruby-head
|
8
|
-
|
10
|
+
arch:
|
11
|
+
- amd64
|
12
|
+
- arm64
|
13
|
+
jobs:
|
9
14
|
include:
|
10
|
-
- rvm: 2.5
|
15
|
+
- rvm: 2.5
|
11
16
|
os: osx
|
12
17
|
osx_image: xcode9.4
|
13
|
-
- rvm: 2.
|
18
|
+
- rvm: 2.6
|
14
19
|
os: osx
|
15
|
-
osx_image:
|
16
|
-
- rvm: 2.
|
20
|
+
osx_image: xcode11.3
|
21
|
+
- rvm: 2.6
|
17
22
|
os: osx
|
18
|
-
osx_image:
|
19
|
-
|
20
|
-
|
23
|
+
osx_image: xcode12.2
|
24
|
+
- rvm: 2.7
|
25
|
+
os: osx
|
26
|
+
osx_image: xcode12.2
|
27
|
+
dist: xenial
|
21
28
|
before_install:
|
22
29
|
- gem update --system
|
23
30
|
- gem install bundler -v 1.16.2
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,74 @@
|
|
1
|
+
- 11-04-2021
|
2
|
+
|
3
|
+
- 0.4.0
|
4
|
+
|
5
|
+
- FEATURE: upgrade to libv8 node 15.14.0 (v8 8.6.395.17)
|
6
|
+
- Promote 0.4.0.beta1 to release, using libv8-node release path
|
7
|
+
|
8
|
+
- 08-04-2021
|
9
|
+
|
10
|
+
- 0.4.0.beta1
|
11
|
+
|
12
|
+
- FIX: on downgrade mkmf was picking the wrong version of libv8, this fix will correct future issues
|
13
|
+
- FEATURE: upgraded libv8 to use node libv8 build which supports M1 and various ARM builds v8 moved to (8.6.395.17)
|
14
|
+
|
15
|
+
- 23-07-2020
|
16
|
+
|
17
|
+
- 0.3.1
|
18
|
+
|
19
|
+
- FIX: specify that libv8 must be larger than 8.4.255 but smaller than 8.5, this avoids issues going forward
|
20
|
+
|
21
|
+
- 22-07-2020
|
22
|
+
|
23
|
+
- 0.3.0
|
24
|
+
|
25
|
+
- FEATURE: upgraded to libv8 version 8.4.255.0
|
26
|
+
|
27
|
+
- 29-06-2020
|
28
|
+
|
29
|
+
- 0.2.15
|
30
|
+
|
31
|
+
- FEATURE: basic wasm support via pump_message_loop
|
32
|
+
|
33
|
+
- 15-05-2020
|
34
|
+
|
35
|
+
- 0.2.14
|
36
|
+
|
37
|
+
- FIX: ensure_gc_after_idle should take in milliseconds like the rest of the APIs not seconds
|
38
|
+
- FEATURE: strict params on MiniRacer::Context.new
|
39
|
+
|
40
|
+
- 15-05-2020
|
41
|
+
|
42
|
+
- 0.2.13
|
43
|
+
|
44
|
+
- FIX: edge case around ensure_gc_after_idle possibly firing when context is not idle
|
45
|
+
|
46
|
+
- 15-05-2020
|
47
|
+
|
48
|
+
- 0.2.12
|
49
|
+
|
50
|
+
- FEATURE: isolate.low_memory_notification which can force a full GC
|
51
|
+
- 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
|
52
|
+
|
53
|
+
- 14-05-2020
|
54
|
+
|
55
|
+
- 0.2.11
|
56
|
+
|
57
|
+
- FIX: dumping heap snapshots was not flushing the file leading to corrupt snapshots
|
58
|
+
- FIX: a use-after-free shutdown crash
|
59
|
+
|
60
|
+
- 0.2.10
|
61
|
+
|
62
|
+
- 22-04-2020
|
63
|
+
|
64
|
+
- FEATURE: memory softlimit support for nogvl_context_call
|
65
|
+
|
66
|
+
- 0.2.9
|
67
|
+
|
68
|
+
- 09-01-2020
|
69
|
+
|
70
|
+
- FIX: correct segfault when JS returns a Symbol and properly cast to ruby symbol
|
71
|
+
|
1
72
|
- 0.2.8
|
2
73
|
|
3
74
|
- 11-11-2019
|
data/Dockerfile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
ARG RUBY_VERSION=2.7
|
2
|
+
FROM ruby:${RUBY_VERSION}
|
3
|
+
|
4
|
+
RUN test ! -f /etc/alpine-release || apk add --no-cache build-base git
|
5
|
+
|
6
|
+
# without this `COPY .git`, we get the following error:
|
7
|
+
# fatal: not a git repository (or any of the parent directories): .git
|
8
|
+
# but with it we need the full gem just to compile the extension because
|
9
|
+
# of gemspec's `git --ls-files`
|
10
|
+
# COPY .git /code/.git
|
11
|
+
COPY Gemfile mini_racer.gemspec /code/
|
12
|
+
COPY lib/mini_racer/version.rb /code/lib/mini_racer/version.rb
|
13
|
+
WORKDIR /code
|
14
|
+
RUN bundle install
|
15
|
+
|
16
|
+
COPY Rakefile /code/
|
17
|
+
COPY ext /code/ext/
|
18
|
+
RUN bundle exec rake compile
|
19
|
+
|
20
|
+
COPY . /code/
|
21
|
+
CMD bundle exec irb -rmini_racer
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# MiniRacer
|
2
2
|
|
3
|
-
[](https://travis-ci.org/rubyjs/mini_racer)
|
4
4
|
|
5
5
|
Minimal, modern embedded V8 for Ruby.
|
6
6
|
|
@@ -230,12 +230,17 @@ context = MiniRacer::Context.new(isolate: isolate)
|
|
230
230
|
# give up to 100ms for V8 garbage collection
|
231
231
|
isolate.idle_notification(100)
|
232
232
|
|
233
|
+
# force V8 to perform a full GC
|
234
|
+
isolate.low_memory_notification
|
235
|
+
|
233
236
|
```
|
234
237
|
|
235
238
|
This can come in handy to force V8 GC runs for example in between requests if you use MiniRacer on a web application.
|
236
239
|
|
237
240
|
Note that this method maps directly to [`v8::Isolate::IdleNotification`](http://bespin.cz/~ondras/html/classv8_1_1Isolate.html#aea16cbb2e351de9a3ae7be2b7cb48297), and that in particular its return value is the same (true if there is no further garbage to collect, false otherwise) and the same caveats apply, in particular that `there is no guarantee that the [call will return] within the time limit.`
|
238
241
|
|
242
|
+
Additionally you may automate this process on a context by defining it with `MiniRacer::Content.new(ensure_gc_after_idle: 1000)`. Using this will ensure V8 will run a full GC using `context.isolate.low_memory_notification` 1 second after the last eval on the context. Low memory notification is both slower and more aggressive than an idle_notification and will ensure long living isolates use minimal amounts of memory.
|
243
|
+
|
239
244
|
### V8 Runtime flags
|
240
245
|
|
241
246
|
It is possible to set V8 Runtime flags:
|
@@ -244,7 +249,7 @@ It is possible to set V8 Runtime flags:
|
|
244
249
|
MiniRacer::Platform.set_flags! :noconcurrent_recompilation, max_inlining_levels: 10
|
245
250
|
```
|
246
251
|
|
247
|
-
This can come in handy if you want to use MiniRacer with Unicorn, which doesn't seem to
|
252
|
+
This can come in handy if you want to use MiniRacer with Unicorn, which doesn't seem to always appreciate V8's liberal use of threading:
|
248
253
|
```ruby
|
249
254
|
MiniRacer::Platform.set_flags! :noconcurrent_recompilation, :noconcurrent_sweeping
|
250
255
|
```
|
@@ -273,7 +278,7 @@ context.eval js
|
|
273
278
|
The same code without the harmony runtime flag results in a `MiniRacer::RuntimeError: RangeError: Maximum call stack size exceeded` exception.
|
274
279
|
Please refer to http://node.green/ as a reference on other harmony features.
|
275
280
|
|
276
|
-
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).
|
281
|
+
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).
|
277
282
|
|
278
283
|
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.
|
279
284
|
|
@@ -310,6 +315,16 @@ context.eval("a = 2")
|
|
310
315
|
# nothing works on the context from now on, its a shell waiting to be disposed
|
311
316
|
```
|
312
317
|
|
318
|
+
A MiniRacer context can also be dumped in a heapsnapshot file using `#write_heap_snapshot(file_or_io)`
|
319
|
+
|
320
|
+
```ruby
|
321
|
+
context = MiniRacer::Context.new(timeout: 5)
|
322
|
+
context.eval("let a='testing';")
|
323
|
+
context.write_heap_snapshot("test.heapsnapshot")
|
324
|
+
```
|
325
|
+
|
326
|
+
This file can then be loaded in the memory tab of the chrome dev console.
|
327
|
+
|
313
328
|
### Function call
|
314
329
|
|
315
330
|
This calls the function passed as first argument:
|
@@ -447,7 +462,7 @@ Add this to your .travis.yml file:
|
|
447
462
|
|
448
463
|
## Contributing
|
449
464
|
|
450
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
465
|
+
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.
|
451
466
|
|
452
467
|
|
453
468
|
## License
|
data/Rakefile
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'mkmf'
|
2
|
-
|
2
|
+
require_relative '../../lib/mini_racer/version'
|
3
|
+
gem 'libv8-node', MiniRacer::LIBV8_NODE_VERSION
|
4
|
+
require 'libv8-node'
|
3
5
|
|
4
6
|
IS_DARWIN = RUBY_PLATFORM =~ /darwin/
|
5
7
|
|
@@ -11,11 +13,17 @@ $CPPFLAGS += " -rdynamic" unless $CPPFLAGS.split.include? "-rdynamic"
|
|
11
13
|
$CPPFLAGS += " -fPIC" unless $CPPFLAGS.split.include? "-rdynamic" or IS_DARWIN
|
12
14
|
$CPPFLAGS += " -std=c++0x"
|
13
15
|
$CPPFLAGS += " -fpermissive"
|
16
|
+
$CPPFLAGS += " -DV8_COMPRESS_POINTERS"
|
17
|
+
$CPPFLAGS += " -fvisibility=hidden "
|
14
18
|
|
15
19
|
$CPPFLAGS += " -Wno-reserved-user-defined-literal" if IS_DARWIN
|
16
20
|
|
17
21
|
$LDFLAGS.insert(0, " -stdlib=libc++ ") if IS_DARWIN
|
18
22
|
|
23
|
+
# check for missing symbols at link time
|
24
|
+
# $LDFLAGS += " -Wl,--no-undefined " unless IS_DARWIN
|
25
|
+
# $LDFLAGS += " -Wl,-undefined,error " if IS_DARWIN
|
26
|
+
|
19
27
|
if ENV['CXX']
|
20
28
|
puts "SETTING CXX"
|
21
29
|
CONFIG['CXX'] = ENV['CXX']
|
@@ -53,7 +61,7 @@ if enable_config('debug') || enable_config('asan')
|
|
53
61
|
CONFIG['debugflags'] << ' -ggdb3 -O0'
|
54
62
|
end
|
55
63
|
|
56
|
-
Libv8.configure_makefile
|
64
|
+
Libv8::Node.configure_makefile
|
57
65
|
|
58
66
|
if enable_config('asan')
|
59
67
|
$CPPFLAGS.insert(0, " -fsanitize=address ")
|
@@ -25,6 +25,7 @@ public:
|
|
25
25
|
ArrayBuffer::Allocator* allocator;
|
26
26
|
StartupData* startup_data;
|
27
27
|
bool interrupted;
|
28
|
+
bool added_gc_cb;
|
28
29
|
pid_t pid;
|
29
30
|
VALUE mutex;
|
30
31
|
|
@@ -42,15 +43,12 @@ public:
|
|
42
43
|
|
43
44
|
|
44
45
|
IsolateInfo() : isolate(nullptr), allocator(nullptr), startup_data(nullptr),
|
45
|
-
interrupted(false), pid(getpid()), refs_count(0) {
|
46
|
+
interrupted(false), added_gc_cb(false), pid(getpid()), refs_count(0) {
|
46
47
|
VALUE cMutex = rb_const_get(rb_cThread, rb_intern("Mutex"));
|
47
48
|
mutex = rb_class_new_instance(0, nullptr, cMutex);
|
48
49
|
}
|
49
50
|
|
50
|
-
~IsolateInfo()
|
51
|
-
void free_isolate(IsolateInfo*);
|
52
|
-
free_isolate(this);
|
53
|
-
}
|
51
|
+
~IsolateInfo();
|
54
52
|
|
55
53
|
void init(SnapshotInfo* snapshot_info = nullptr);
|
56
54
|
|
@@ -127,6 +125,7 @@ typedef struct {
|
|
127
125
|
Local<Function> fun;
|
128
126
|
Local<Value> *argv;
|
129
127
|
EvalResult result;
|
128
|
+
size_t max_memory;
|
130
129
|
} FunctionCall;
|
131
130
|
|
132
131
|
enum IsolateFlags {
|
@@ -155,6 +154,11 @@ static VALUE rb_cDateTime = Qnil;
|
|
155
154
|
static std::unique_ptr<Platform> current_platform = NULL;
|
156
155
|
static std::mutex platform_lock;
|
157
156
|
|
157
|
+
static pthread_attr_t *thread_attr_p;
|
158
|
+
static pthread_rwlock_t exit_lock = PTHREAD_RWLOCK_INITIALIZER;
|
159
|
+
static bool ruby_exiting = false; // guarded by exit_lock
|
160
|
+
static bool single_threaded = false;
|
161
|
+
|
158
162
|
static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
159
163
|
bool platform_already_initialized = false;
|
160
164
|
|
@@ -166,6 +170,9 @@ static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
|
166
170
|
platform_lock.lock();
|
167
171
|
|
168
172
|
if (current_platform == NULL) {
|
173
|
+
if (!strcmp(RSTRING_PTR(flag_as_str), "--single_threaded")) {
|
174
|
+
single_threaded = true;
|
175
|
+
}
|
169
176
|
V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), (int)RSTRING_LEN(flag_as_str));
|
170
177
|
} else {
|
171
178
|
platform_already_initialized = true;
|
@@ -232,11 +239,13 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
232
239
|
Local<Value> local_value = v8res.ToLocalChecked();
|
233
240
|
if ((local_value->IsObject() || local_value->IsArray()) &&
|
234
241
|
!local_value->IsDate() && !local_value->IsFunction()) {
|
235
|
-
Local<Object> JSON = context->Global()->Get(
|
236
|
-
|
242
|
+
Local<Object> JSON = context->Global()->Get(
|
243
|
+
context, String::NewFromUtf8Literal(isolate, "JSON"))
|
244
|
+
.ToLocalChecked().As<Object>();
|
237
245
|
|
238
|
-
Local<Function> stringify = JSON->Get(
|
239
|
-
|
246
|
+
Local<Function> stringify = JSON->Get(
|
247
|
+
context, v8::String::NewFromUtf8Literal(isolate, "stringify"))
|
248
|
+
.ToLocalChecked().As<Function>();
|
240
249
|
|
241
250
|
Local<Object> object = local_value->ToObject(context).ToLocalChecked();
|
242
251
|
const unsigned argc = 1;
|
@@ -290,7 +299,7 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
290
299
|
} else if(trycatch.HasTerminated()) {
|
291
300
|
evalRes.terminated = true;
|
292
301
|
evalRes.message = new Persistent<Value>();
|
293
|
-
Local<String> tmp = String::
|
302
|
+
Local<String> tmp = String::NewFromUtf8Literal(isolate, "JavaScript was terminated (either by timeout or explicitly)");
|
294
303
|
evalRes.message->Reset(isolate, tmp);
|
295
304
|
}
|
296
305
|
if (!trycatch.StackTrace(context).IsEmpty()) {
|
@@ -307,7 +316,8 @@ nogvl_context_eval(void* arg) {
|
|
307
316
|
|
308
317
|
EvalParams* eval_params = (EvalParams*)arg;
|
309
318
|
EvalResult* result = eval_params->result;
|
310
|
-
|
319
|
+
IsolateInfo* isolate_info = eval_params->context_info->isolate_info;
|
320
|
+
Isolate* isolate = isolate_info->isolate;
|
311
321
|
|
312
322
|
Isolate::Scope isolate_scope(isolate);
|
313
323
|
HandleScope handle_scope(isolate);
|
@@ -351,7 +361,10 @@ nogvl_context_eval(void* arg) {
|
|
351
361
|
// parsing successful
|
352
362
|
if (eval_params->max_memory > 0) {
|
353
363
|
isolate->SetData(MEM_SOFTLIMIT_VALUE, &eval_params->max_memory);
|
364
|
+
if (!isolate_info->added_gc_cb) {
|
354
365
|
isolate->AddGCEpilogueCallback(gc_callback);
|
366
|
+
isolate_info->added_gc_cb = true;
|
367
|
+
}
|
355
368
|
}
|
356
369
|
|
357
370
|
maybe_value = parsed_script.ToLocalChecked()->Run(context);
|
@@ -364,6 +377,12 @@ nogvl_context_eval(void* arg) {
|
|
364
377
|
return NULL;
|
365
378
|
}
|
366
379
|
|
380
|
+
static VALUE new_empty_failed_conv_obj() {
|
381
|
+
// TODO isolate code that translates execption to ruby
|
382
|
+
// exception so we can properly return it
|
383
|
+
return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
|
384
|
+
}
|
385
|
+
|
367
386
|
// assumes isolate locking is in place
|
368
387
|
static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
369
388
|
Local<Value> value) {
|
@@ -395,8 +414,11 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
395
414
|
VALUE rb_array = rb_ary_new();
|
396
415
|
Local<Array> arr = Local<Array>::Cast(value);
|
397
416
|
for(uint32_t i=0; i < arr->Length(); i++) {
|
398
|
-
|
399
|
-
|
417
|
+
MaybeLocal<Value> element = arr->Get(context, i);
|
418
|
+
if (element.IsEmpty()) {
|
419
|
+
continue;
|
420
|
+
}
|
421
|
+
VALUE rb_elem = convert_v8_to_ruby(isolate, context, element.ToLocalChecked());
|
400
422
|
if (rb_funcall(rb_elem, rb_intern("class"), 0) == rb_cFailedV8Conversion) {
|
401
423
|
return rb_elem;
|
402
424
|
}
|
@@ -426,26 +448,47 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
426
448
|
if (!maybe_props.IsEmpty()) {
|
427
449
|
Local<Array> props = maybe_props.ToLocalChecked();
|
428
450
|
for(uint32_t i=0; i < props->Length(); i++) {
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
451
|
+
MaybeLocal<Value> key = props->Get(context, i);
|
452
|
+
if (key.IsEmpty()) {
|
453
|
+
return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
|
454
|
+
}
|
455
|
+
VALUE rb_key = convert_v8_to_ruby(isolate, context, key.ToLocalChecked());
|
433
456
|
|
434
|
-
|
435
|
-
//
|
436
|
-
|
437
|
-
|
457
|
+
MaybeLocal<Value> prop_value = object->Get(context, key.ToLocalChecked());
|
458
|
+
// this may have failed due to Get raising
|
459
|
+
if (prop_value.IsEmpty() || trycatch.HasCaught()) {
|
460
|
+
return new_empty_failed_conv_obj();
|
438
461
|
}
|
439
462
|
|
440
|
-
VALUE rb_value = convert_v8_to_ruby(
|
463
|
+
VALUE rb_value = convert_v8_to_ruby(
|
464
|
+
isolate, context, prop_value.ToLocalChecked());
|
441
465
|
rb_hash_aset(rb_hash, rb_key, rb_value);
|
442
466
|
}
|
443
467
|
}
|
444
468
|
return rb_hash;
|
445
469
|
}
|
446
470
|
|
447
|
-
|
448
|
-
|
471
|
+
if (value->IsSymbol()) {
|
472
|
+
v8::String::Utf8Value symbol_name(isolate,
|
473
|
+
Local<Symbol>::Cast(value)->Name());
|
474
|
+
|
475
|
+
VALUE str_symbol = rb_enc_str_new(
|
476
|
+
*symbol_name,
|
477
|
+
symbol_name.length(),
|
478
|
+
rb_enc_find("utf-8")
|
479
|
+
);
|
480
|
+
|
481
|
+
return ID2SYM(rb_intern_str(str_symbol));
|
482
|
+
}
|
483
|
+
|
484
|
+
MaybeLocal<String> rstr_maybe = value->ToString(context);
|
485
|
+
|
486
|
+
if (rstr_maybe.IsEmpty()) {
|
487
|
+
return Qnil;
|
488
|
+
} else {
|
489
|
+
Local<String> rstr = rstr_maybe.ToLocalChecked();
|
490
|
+
return rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
|
491
|
+
}
|
449
492
|
}
|
450
493
|
|
451
494
|
static VALUE convert_v8_to_ruby(Isolate* isolate,
|
@@ -500,7 +543,7 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
500
543
|
length = RARRAY_LEN(value);
|
501
544
|
array = Array::New(isolate, (int)length);
|
502
545
|
for(i=0; i<length; i++) {
|
503
|
-
|
546
|
+
array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
|
504
547
|
}
|
505
548
|
return scope.Escape(array);
|
506
549
|
case T_HASH:
|
@@ -509,7 +552,7 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
509
552
|
length = RARRAY_LEN(hash_as_array);
|
510
553
|
for(i=0; i<length; i++) {
|
511
554
|
pair = rb_ary_entry(hash_as_array, i);
|
512
|
-
|
555
|
+
object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
|
513
556
|
convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 1)));
|
514
557
|
}
|
515
558
|
return scope.Escape(object);
|
@@ -539,9 +582,9 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
539
582
|
case T_UNDEF:
|
540
583
|
case T_NODE:
|
541
584
|
default:
|
542
|
-
return scope.Escape(String::
|
585
|
+
return scope.Escape(String::NewFromUtf8Literal(isolate, "Undefined Conversion"));
|
586
|
+
}
|
543
587
|
}
|
544
|
-
}
|
545
588
|
|
546
589
|
static void unblock_eval(void *ptr) {
|
547
590
|
EvalParams* eval = (EvalParams*)ptr;
|
@@ -552,53 +595,43 @@ static void unblock_eval(void *ptr) {
|
|
552
595
|
* The implementations of the run_extra_code(), create_snapshot_data_blob() and
|
553
596
|
* warm_up_snapshot_data_blob() functions have been derived from V8's test suite.
|
554
597
|
*/
|
555
|
-
bool run_extra_code(Isolate *isolate, Local<v8::Context> context,
|
598
|
+
static bool run_extra_code(Isolate *isolate, Local<v8::Context> context,
|
556
599
|
const char *utf8_source, const char *name) {
|
557
600
|
Context::Scope context_scope(context);
|
558
601
|
TryCatch try_catch(isolate);
|
559
602
|
Local<String> source_string;
|
560
|
-
if (!String::NewFromUtf8(isolate, utf8_source
|
561
|
-
NewStringType::kNormal)
|
562
|
-
.ToLocal(&source_string)) {
|
603
|
+
if (!String::NewFromUtf8(isolate, utf8_source).ToLocal(&source_string)) {
|
563
604
|
return false;
|
564
605
|
}
|
565
|
-
Local<
|
566
|
-
|
567
|
-
.ToLocalChecked();
|
606
|
+
Local<String> resource_name =
|
607
|
+
String::NewFromUtf8(isolate, name).ToLocalChecked();
|
568
608
|
ScriptOrigin origin(resource_name);
|
569
609
|
ScriptCompiler::Source source(source_string, origin);
|
570
610
|
Local<Script> script;
|
571
611
|
if (!ScriptCompiler::Compile(context, &source).ToLocal(&script))
|
572
612
|
return false;
|
573
|
-
if (script->Run(context).IsEmpty())
|
574
|
-
return false;
|
575
|
-
// CHECK(!try_catch.HasCaught());
|
613
|
+
if (script->Run(context).IsEmpty()) return false;
|
576
614
|
return true;
|
577
615
|
}
|
578
616
|
|
579
|
-
StartupData
|
617
|
+
static StartupData
|
580
618
|
create_snapshot_data_blob(const char *embedded_source = nullptr) {
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
SnapshotCreator snapshot_creator;
|
586
|
-
Isolate *isolate = snapshot_creator.GetIsolate();
|
619
|
+
Isolate *isolate = Isolate::Allocate();
|
620
|
+
|
621
|
+
// Optionally run a script to embed, and serialize to create a snapshot blob.
|
622
|
+
SnapshotCreator snapshot_creator(isolate);
|
587
623
|
{
|
588
624
|
HandleScope scope(isolate);
|
589
|
-
|
625
|
+
Local<v8::Context> context = v8::Context::New(isolate);
|
590
626
|
if (embedded_source != nullptr &&
|
591
|
-
!run_extra_code(isolate, context, embedded_source,
|
592
|
-
|
593
|
-
return result;
|
627
|
+
!run_extra_code(isolate, context, embedded_source, "<embedded>")) {
|
628
|
+
return {};
|
594
629
|
}
|
595
630
|
snapshot_creator.SetDefaultContext(context);
|
596
631
|
}
|
597
|
-
|
632
|
+
return snapshot_creator.CreateBlob(
|
598
633
|
SnapshotCreator::FunctionCodeHandling::kClear);
|
599
634
|
}
|
600
|
-
return result;
|
601
|
-
}
|
602
635
|
|
603
636
|
StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
|
604
637
|
const char *warmup_source) {
|
@@ -745,6 +778,29 @@ static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
|
|
745
778
|
return isolate_info->isolate->IdleNotificationDeadline(now + duration) ? Qtrue : Qfalse;
|
746
779
|
}
|
747
780
|
|
781
|
+
static VALUE rb_isolate_low_memory_notification(VALUE self) {
|
782
|
+
IsolateInfo* isolate_info;
|
783
|
+
Data_Get_Struct(self, IsolateInfo, isolate_info);
|
784
|
+
|
785
|
+
if (current_platform == NULL) return Qfalse;
|
786
|
+
|
787
|
+
isolate_info->isolate->LowMemoryNotification();
|
788
|
+
return Qnil;
|
789
|
+
}
|
790
|
+
|
791
|
+
static VALUE rb_isolate_pump_message_loop(VALUE self) {
|
792
|
+
IsolateInfo* isolate_info;
|
793
|
+
Data_Get_Struct(self, IsolateInfo, isolate_info);
|
794
|
+
|
795
|
+
if (current_platform == NULL) return Qfalse;
|
796
|
+
|
797
|
+
if (platform::PumpMessageLoop(current_platform.get(), isolate_info->isolate)){
|
798
|
+
return Qtrue;
|
799
|
+
} else {
|
800
|
+
return Qfalse;
|
801
|
+
}
|
802
|
+
}
|
803
|
+
|
748
804
|
static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
|
749
805
|
ContextInfo* context_info;
|
750
806
|
Data_Get_Struct(self, ContextInfo, context_info);
|
@@ -1015,7 +1071,9 @@ gvl_ruby_callback(void* data) {
|
|
1015
1071
|
callback_data.failed = false;
|
1016
1072
|
|
1017
1073
|
if ((bool)args->GetIsolate()->GetData(DO_TERMINATE) == true) {
|
1018
|
-
args->GetIsolate()->ThrowException(
|
1074
|
+
args->GetIsolate()->ThrowException(
|
1075
|
+
String::NewFromUtf8Literal(args->GetIsolate(),
|
1076
|
+
"Terminated execution during transition from Ruby to JS"));
|
1019
1077
|
args->GetIsolate()->TerminateExecution();
|
1020
1078
|
if (length > 0) {
|
1021
1079
|
rb_ary_clear(ruby_args);
|
@@ -1029,7 +1087,7 @@ gvl_ruby_callback(void* data) {
|
|
1029
1087
|
|
1030
1088
|
if(callback_data.failed) {
|
1031
1089
|
rb_iv_set(parent, "@current_exception", result);
|
1032
|
-
args->GetIsolate()->ThrowException(String::
|
1090
|
+
args->GetIsolate()->ThrowException(String::NewFromUtf8Literal(args->GetIsolate(), "Ruby exception"));
|
1033
1091
|
}
|
1034
1092
|
else {
|
1035
1093
|
HandleScope scope(args->GetIsolate());
|
@@ -1100,7 +1158,9 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1100
1158
|
|
1101
1159
|
if (parent_object == Qnil) {
|
1102
1160
|
context->Global()->Set(
|
1103
|
-
|
1161
|
+
context,
|
1162
|
+
v8_str,
|
1163
|
+
FunctionTemplate::New(isolate, ruby_callback, external)
|
1104
1164
|
->GetFunction(context)
|
1105
1165
|
.ToLocalChecked());
|
1106
1166
|
|
@@ -1113,7 +1173,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1113
1173
|
|
1114
1174
|
MaybeLocal<Script> parsed_script = Script::Compile(context, eval);
|
1115
1175
|
if (parsed_script.IsEmpty()) {
|
1116
|
-
|
1176
|
+
parse_error = true;
|
1117
1177
|
} else {
|
1118
1178
|
MaybeLocal<Value> maybe_value =
|
1119
1179
|
parsed_script.ToLocalChecked()->Run(context);
|
@@ -1123,11 +1183,12 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1123
1183
|
Local<Value> value = maybe_value.ToLocalChecked();
|
1124
1184
|
if (value->IsObject()) {
|
1125
1185
|
value.As<Object>()->Set(
|
1126
|
-
|
1127
|
-
|
1186
|
+
context,
|
1187
|
+
v8_str,
|
1188
|
+
FunctionTemplate::New(isolate, ruby_callback, external)
|
1128
1189
|
->GetFunction(context)
|
1129
1190
|
.ToLocalChecked());
|
1130
|
-
|
1191
|
+
attach_error = false;
|
1131
1192
|
}
|
1132
1193
|
}
|
1133
1194
|
}
|
@@ -1157,35 +1218,39 @@ static VALUE rb_context_isolate_mutex(VALUE self) {
|
|
1157
1218
|
return context_info->isolate_info->mutex;
|
1158
1219
|
}
|
1159
1220
|
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
if (isolate_info->isolate) {
|
1167
|
-
if (isolate_info->interrupted) {
|
1168
|
-
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");
|
1221
|
+
IsolateInfo::~IsolateInfo() {
|
1222
|
+
if (isolate) {
|
1223
|
+
if (this->interrupted) {
|
1224
|
+
fprintf(stderr, "WARNING: V8 isolate was interrupted by Ruby, "
|
1225
|
+
"it can not be disposed and memory will not be "
|
1226
|
+
"reclaimed till the Ruby process exits.\n");
|
1169
1227
|
} else {
|
1170
|
-
|
1171
|
-
|
1172
|
-
|
1228
|
+
if (this->pid != getpid() && !single_threaded) {
|
1229
|
+
fprintf(stderr, "WARNING: V8 isolate was forked, "
|
1230
|
+
"it can not be disposed and "
|
1231
|
+
"memory will not be reclaimed "
|
1232
|
+
"till the Ruby process exits.\n"
|
1233
|
+
"It is VERY likely your process will hang.\n"
|
1234
|
+
"If you wish to use v8 in forked environment "
|
1235
|
+
"please ensure the platform is initialized with:\n"
|
1236
|
+
"MiniRacer::Platform.set_flags! :single_threaded\n"
|
1237
|
+
);
|
1173
1238
|
} else {
|
1174
|
-
|
1239
|
+
isolate->Dispose();
|
1175
1240
|
}
|
1176
1241
|
}
|
1177
|
-
|
1242
|
+
isolate = nullptr;
|
1178
1243
|
}
|
1179
1244
|
|
1180
|
-
if (
|
1181
|
-
delete[]
|
1182
|
-
delete
|
1245
|
+
if (startup_data) {
|
1246
|
+
delete[] startup_data->data;
|
1247
|
+
delete startup_data;
|
1183
1248
|
}
|
1184
1249
|
|
1185
|
-
delete
|
1250
|
+
delete allocator;
|
1186
1251
|
}
|
1187
1252
|
|
1188
|
-
static void
|
1253
|
+
static void free_context_raw(void *arg) {
|
1189
1254
|
ContextInfo* context_info = (ContextInfo*)arg;
|
1190
1255
|
IsolateInfo* isolate_info = context_info->isolate_info;
|
1191
1256
|
Persistent<Context>* context = context_info->context;
|
@@ -1202,6 +1267,20 @@ static void *free_context_raw(void* arg) {
|
|
1202
1267
|
}
|
1203
1268
|
|
1204
1269
|
xfree(context_info);
|
1270
|
+
}
|
1271
|
+
|
1272
|
+
static void *free_context_thr(void* arg) {
|
1273
|
+
if (pthread_rwlock_tryrdlock(&exit_lock) != 0) {
|
1274
|
+
return NULL;
|
1275
|
+
}
|
1276
|
+
if (ruby_exiting) {
|
1277
|
+
return NULL;
|
1278
|
+
}
|
1279
|
+
|
1280
|
+
free_context_raw(arg);
|
1281
|
+
|
1282
|
+
pthread_rwlock_unlock(&exit_lock);
|
1283
|
+
|
1205
1284
|
return NULL;
|
1206
1285
|
}
|
1207
1286
|
|
@@ -1215,22 +1294,17 @@ static void free_context(ContextInfo* context_info) {
|
|
1215
1294
|
context_info_copy->context = context_info->context;
|
1216
1295
|
|
1217
1296
|
if (isolate_info && isolate_info->refs() > 1) {
|
1218
|
-
|
1219
|
-
|
1220
|
-
|
1221
|
-
|
1222
|
-
|
1297
|
+
pthread_t free_context_thread;
|
1298
|
+
if (pthread_create(&free_context_thread, thread_attr_p,
|
1299
|
+
free_context_thr, (void*)context_info_copy)) {
|
1300
|
+
fprintf(stderr, "WARNING failed to release memory in MiniRacer, thread to release could not be created, process will leak memory\n");
|
1301
|
+
}
|
1223
1302
|
} else {
|
1224
1303
|
free_context_raw(context_info_copy);
|
1225
1304
|
}
|
1226
1305
|
|
1227
|
-
|
1228
|
-
|
1229
|
-
}
|
1230
|
-
|
1231
|
-
if (isolate_info) {
|
1232
|
-
context_info->isolate_info = NULL;
|
1233
|
-
}
|
1306
|
+
context_info->context = NULL;
|
1307
|
+
context_info->isolate_info = NULL;
|
1234
1308
|
}
|
1235
1309
|
|
1236
1310
|
static void deallocate_isolate(void* data) {
|
@@ -1245,7 +1319,7 @@ static void mark_isolate(void* data) {
|
|
1245
1319
|
isolate_info->mark();
|
1246
1320
|
}
|
1247
1321
|
|
1248
|
-
void deallocate(void* data) {
|
1322
|
+
static void deallocate(void* data) {
|
1249
1323
|
ContextInfo* context_info = (ContextInfo*)data;
|
1250
1324
|
|
1251
1325
|
free_context(context_info);
|
@@ -1260,22 +1334,22 @@ static void mark_context(void* data) {
|
|
1260
1334
|
}
|
1261
1335
|
}
|
1262
1336
|
|
1263
|
-
void deallocate_external_function(void * data) {
|
1337
|
+
static void deallocate_external_function(void * data) {
|
1264
1338
|
xfree(data);
|
1265
1339
|
}
|
1266
1340
|
|
1267
|
-
void deallocate_snapshot(void * data) {
|
1341
|
+
static void deallocate_snapshot(void * data) {
|
1268
1342
|
SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
|
1269
1343
|
delete[] snapshot_info->data;
|
1270
1344
|
xfree(snapshot_info);
|
1271
1345
|
}
|
1272
1346
|
|
1273
|
-
VALUE allocate_external_function(VALUE klass) {
|
1347
|
+
static VALUE allocate_external_function(VALUE klass) {
|
1274
1348
|
VALUE* self = ALLOC(VALUE);
|
1275
1349
|
return Data_Wrap_Struct(klass, NULL, deallocate_external_function, (void*)self);
|
1276
1350
|
}
|
1277
1351
|
|
1278
|
-
VALUE allocate(VALUE klass) {
|
1352
|
+
static VALUE allocate(VALUE klass) {
|
1279
1353
|
ContextInfo* context_info = ALLOC(ContextInfo);
|
1280
1354
|
context_info->isolate_info = NULL;
|
1281
1355
|
context_info->context = NULL;
|
@@ -1283,7 +1357,7 @@ VALUE allocate(VALUE klass) {
|
|
1283
1357
|
return Data_Wrap_Struct(klass, mark_context, deallocate, (void*)context_info);
|
1284
1358
|
}
|
1285
1359
|
|
1286
|
-
VALUE allocate_snapshot(VALUE klass) {
|
1360
|
+
static VALUE allocate_snapshot(VALUE klass) {
|
1287
1361
|
SnapshotInfo* snapshot_info = ALLOC(SnapshotInfo);
|
1288
1362
|
snapshot_info->data = NULL;
|
1289
1363
|
snapshot_info->raw_size = 0;
|
@@ -1291,7 +1365,7 @@ VALUE allocate_snapshot(VALUE klass) {
|
|
1291
1365
|
return Data_Wrap_Struct(klass, NULL, deallocate_snapshot, (void*)snapshot_info);
|
1292
1366
|
}
|
1293
1367
|
|
1294
|
-
VALUE allocate_isolate(VALUE klass) {
|
1368
|
+
static VALUE allocate_isolate(VALUE klass) {
|
1295
1369
|
IsolateInfo* isolate_info = new IsolateInfo();
|
1296
1370
|
|
1297
1371
|
return Data_Wrap_Struct(klass, mark_isolate, deallocate_isolate, (void*)isolate_info);
|
@@ -1388,6 +1462,8 @@ rb_heap_snapshot(VALUE self, VALUE file) {
|
|
1388
1462
|
FileOutputStream stream(fp);
|
1389
1463
|
snap->Serialize(&stream, HeapSnapshot::kJSON);
|
1390
1464
|
|
1465
|
+
fflush(fp);
|
1466
|
+
|
1391
1467
|
const_cast<HeapSnapshot*>(snap)->Delete();
|
1392
1468
|
|
1393
1469
|
return Qtrue;
|
@@ -1428,13 +1504,23 @@ nogvl_context_call(void *args) {
|
|
1428
1504
|
if (!call) {
|
1429
1505
|
return NULL;
|
1430
1506
|
}
|
1431
|
-
|
1507
|
+
IsolateInfo *isolate_info = call->context_info->isolate_info;
|
1508
|
+
Isolate* isolate = isolate_info->isolate;
|
1432
1509
|
|
1433
1510
|
// in gvl flag
|
1434
1511
|
isolate->SetData(IN_GVL, (void*)false);
|
1435
1512
|
// terminate ASAP
|
1436
1513
|
isolate->SetData(DO_TERMINATE, (void*)false);
|
1437
1514
|
|
1515
|
+
if (call->max_memory > 0) {
|
1516
|
+
isolate->SetData(MEM_SOFTLIMIT_VALUE, &call->max_memory);
|
1517
|
+
isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)false);
|
1518
|
+
if (!isolate_info->added_gc_cb) {
|
1519
|
+
isolate->AddGCEpilogueCallback(gc_callback);
|
1520
|
+
isolate_info->added_gc_cb = true;
|
1521
|
+
}
|
1522
|
+
}
|
1523
|
+
|
1438
1524
|
Isolate::Scope isolate_scope(isolate);
|
1439
1525
|
EscapableHandleScope handle_scope(isolate);
|
1440
1526
|
TryCatch trycatch(isolate);
|
@@ -1492,6 +1578,13 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1492
1578
|
call_argv = argv + 1;
|
1493
1579
|
}
|
1494
1580
|
|
1581
|
+
call.max_memory = 0;
|
1582
|
+
VALUE mem_softlimit = rb_iv_get(self, "@max_memory");
|
1583
|
+
if (mem_softlimit != Qnil) {
|
1584
|
+
unsigned long sl_int = NUM2ULONG(mem_softlimit);
|
1585
|
+
call.max_memory = (size_t)sl_int;
|
1586
|
+
}
|
1587
|
+
|
1495
1588
|
bool missingFunction = false;
|
1496
1589
|
{
|
1497
1590
|
Locker lock(isolate);
|
@@ -1503,8 +1596,11 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1503
1596
|
|
1504
1597
|
// examples of such usage can be found in
|
1505
1598
|
// https://github.com/v8/v8/blob/36b32aa28db5e993312f4588d60aad5c8330c8a5/test/cctest/test-api.cc#L15711
|
1506
|
-
|
1507
|
-
MaybeLocal<v8::Value> val
|
1599
|
+
MaybeLocal<String> fname = String::NewFromUtf8(isolate, call.function_name);
|
1600
|
+
MaybeLocal<v8::Value> val;
|
1601
|
+
if (!fname.IsEmpty()) {
|
1602
|
+
val = context->Global()->Get(context, fname.ToLocalChecked());
|
1603
|
+
}
|
1508
1604
|
|
1509
1605
|
if (val.IsEmpty() || !val.ToLocalChecked()->IsFunction()) {
|
1510
1606
|
missingFunction = true;
|
@@ -1549,9 +1645,20 @@ static VALUE rb_context_create_isolate_value(VALUE self) {
|
|
1549
1645
|
return Data_Wrap_Struct(rb_cIsolate, NULL, &deallocate_isolate, isolate_info);
|
1550
1646
|
}
|
1551
1647
|
|
1648
|
+
static void set_ruby_exiting(VALUE value) {
|
1649
|
+
(void)value;
|
1650
|
+
|
1651
|
+
int res = pthread_rwlock_wrlock(&exit_lock);
|
1652
|
+
|
1653
|
+
ruby_exiting = true;
|
1654
|
+
if (res == 0) {
|
1655
|
+
pthread_rwlock_unlock(&exit_lock);
|
1656
|
+
}
|
1657
|
+
}
|
1658
|
+
|
1552
1659
|
extern "C" {
|
1553
1660
|
|
1554
|
-
void Init_mini_racer_extension ( void )
|
1661
|
+
__attribute__((visibility("default"))) void Init_mini_racer_extension ( void )
|
1555
1662
|
{
|
1556
1663
|
VALUE rb_mMiniRacer = rb_define_module("MiniRacer");
|
1557
1664
|
rb_cContext = rb_define_class_under(rb_mMiniRacer, "Context", rb_cObject);
|
@@ -1599,8 +1706,23 @@ extern "C" {
|
|
1599
1706
|
rb_define_private_method(rb_cSnapshot, "load", (VALUE(*)(...))&rb_snapshot_load, 1);
|
1600
1707
|
|
1601
1708
|
rb_define_method(rb_cIsolate, "idle_notification", (VALUE(*)(...))&rb_isolate_idle_notification, 1);
|
1709
|
+
rb_define_method(rb_cIsolate, "low_memory_notification", (VALUE(*)(...))&rb_isolate_low_memory_notification, 0);
|
1710
|
+
rb_define_method(rb_cIsolate, "pump_message_loop", (VALUE(*)(...))&rb_isolate_pump_message_loop, 0);
|
1602
1711
|
rb_define_private_method(rb_cIsolate, "init_with_snapshot",(VALUE(*)(...))&rb_isolate_init_with_snapshot, 1);
|
1603
1712
|
|
1604
1713
|
rb_define_singleton_method(rb_cPlatform, "set_flag_as_str!", (VALUE(*)(...))&rb_platform_set_flag_as_str, 1);
|
1714
|
+
|
1715
|
+
rb_set_end_proc(set_ruby_exiting, Qnil);
|
1716
|
+
|
1717
|
+
static pthread_attr_t attr;
|
1718
|
+
if (pthread_attr_init(&attr) == 0) {
|
1719
|
+
if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == 0) {
|
1720
|
+
thread_attr_p = &attr;
|
1721
|
+
}
|
1722
|
+
}
|
1723
|
+
auto on_fork_for_child = []() {
|
1724
|
+
exit_lock = PTHREAD_RWLOCK_INITIALIZER;
|
1725
|
+
};
|
1726
|
+
pthread_atfork(nullptr, nullptr, on_fork_for_child);
|
1605
1727
|
}
|
1606
1728
|
}
|