mini_racer 0.2.8 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.dockerignore +12 -0
- data/.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
|
-
[![Build Status](https://travis-ci.org/
|
3
|
+
[![Build Status](https://travis-ci.org/rubyjs/mini_racer.svg?branch=master)](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
|
}
|