mini_racer 0.2.13 → 0.4.0.beta1
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 +15 -8
- data/CHANGELOG +33 -0
- data/Dockerfile +21 -0
- data/README.md +12 -2
- data/Rakefile +1 -0
- data/ext/mini_racer_extension/extconf.rb +10 -2
- data/ext/mini_racer_extension/mini_racer_extension.cc +131 -88
- 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 +40 -16
- data/lib/mini_racer/version.rb +2 -1
- data/mini_racer.gemspec +3 -2
- metadata +36 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c805c80e637874adae6cd80514e54a7e56bccb744ba3f3951fb1b8a575dd306f
|
4
|
+
data.tar.gz: 25721453b4d1fb59056bec90ca12363ce5f05c78fc984b5ee0c3007311d96b8c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ad2122baf3beb6e14861a5c1d5a8d50884ea803e6a59a6c62e72e6041b010da075d260c76e18f913cdcf30974d47ed013321f2660de204a06f8140d8cb313d87
|
7
|
+
data.tar.gz: 1462cae8c8d61310db4e6a1484c2d7526c2b69005b6fd42254dde1682c75f8e3d1d8eda1bac59811dbce8aae89ba195f0d4f808c2315906110b99e031d73369b
|
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
4
|
- 2.4
|
4
5
|
- 2.5
|
5
6
|
- 2.6
|
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,6 +1,39 @@
|
|
1
|
+
- 08-04-2021
|
2
|
+
|
3
|
+
- 0.4.0.beta1
|
4
|
+
|
5
|
+
- FIX: on downgrade mkmf was picking the wrong version of libv8, this fix will correct future issues
|
6
|
+
- FEATURE: upgraded libv8 to use node libv8 build which supports M1 and various ARM builds v8 moved to (8.6.395.17)
|
7
|
+
|
8
|
+
- 23-07-2020
|
9
|
+
|
10
|
+
- 0.3.1
|
11
|
+
|
12
|
+
- FIX: specify that libv8 must be larger than 8.4.255 but smaller than 8.5, this avoids issues going forward
|
13
|
+
|
14
|
+
- 22-07-2020
|
15
|
+
|
16
|
+
- 0.3.0
|
17
|
+
|
18
|
+
- FEATURE: upgraded to libv8 version 8.4.255.0
|
19
|
+
|
20
|
+
- 29-06-2020
|
21
|
+
|
22
|
+
- 0.2.15
|
23
|
+
|
24
|
+
- FEATURE: basic wasm support via pump_message_loop
|
25
|
+
|
26
|
+
- 15-05-2020
|
27
|
+
|
28
|
+
- 0.2.14
|
29
|
+
|
30
|
+
- FIX: ensure_gc_after_idle should take in milliseconds like the rest of the APIs not seconds
|
31
|
+
- FEATURE: strict params on MiniRacer::Context.new
|
32
|
+
|
1
33
|
- 15-05-2020
|
2
34
|
|
3
35
|
- 0.2.13
|
36
|
+
|
4
37
|
- FIX: edge case around ensure_gc_after_idle possibly firing when context is not idle
|
5
38
|
|
6
39
|
- 15-05-2020
|
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
@@ -239,7 +239,7 @@ This can come in handy to force V8 GC runs for example in between requests if yo
|
|
239
239
|
|
240
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.`
|
241
241
|
|
242
|
-
Additionally you may automate this process on a context by defining it with `MiniRacer::Content.new(ensure_gc_after_idle:
|
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
243
|
|
244
244
|
### V8 Runtime flags
|
245
245
|
|
@@ -249,7 +249,7 @@ It is possible to set V8 Runtime flags:
|
|
249
249
|
MiniRacer::Platform.set_flags! :noconcurrent_recompilation, max_inlining_levels: 10
|
250
250
|
```
|
251
251
|
|
252
|
-
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:
|
253
253
|
```ruby
|
254
254
|
MiniRacer::Platform.set_flags! :noconcurrent_recompilation, :noconcurrent_sweeping
|
255
255
|
```
|
@@ -315,6 +315,16 @@ context.eval("a = 2")
|
|
315
315
|
# nothing works on the context from now on, its a shell waiting to be disposed
|
316
316
|
```
|
317
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
|
+
|
318
328
|
### Function call
|
319
329
|
|
320
330
|
This calls the function passed as first argument:
|
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
|
|
@@ -158,7 +156,8 @@ static std::mutex platform_lock;
|
|
158
156
|
|
159
157
|
static pthread_attr_t *thread_attr_p;
|
160
158
|
static pthread_rwlock_t exit_lock = PTHREAD_RWLOCK_INITIALIZER;
|
161
|
-
static bool ruby_exiting; // guarded by exit_lock
|
159
|
+
static bool ruby_exiting = false; // guarded by exit_lock
|
160
|
+
static bool single_threaded = false;
|
162
161
|
|
163
162
|
static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
164
163
|
bool platform_already_initialized = false;
|
@@ -171,6 +170,9 @@ static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
|
171
170
|
platform_lock.lock();
|
172
171
|
|
173
172
|
if (current_platform == NULL) {
|
173
|
+
if (!strcmp(RSTRING_PTR(flag_as_str), "--single_threaded")) {
|
174
|
+
single_threaded = true;
|
175
|
+
}
|
174
176
|
V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), (int)RSTRING_LEN(flag_as_str));
|
175
177
|
} else {
|
176
178
|
platform_already_initialized = true;
|
@@ -237,11 +239,13 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
237
239
|
Local<Value> local_value = v8res.ToLocalChecked();
|
238
240
|
if ((local_value->IsObject() || local_value->IsArray()) &&
|
239
241
|
!local_value->IsDate() && !local_value->IsFunction()) {
|
240
|
-
Local<Object> JSON = context->Global()->Get(
|
241
|
-
|
242
|
+
Local<Object> JSON = context->Global()->Get(
|
243
|
+
context, String::NewFromUtf8Literal(isolate, "JSON"))
|
244
|
+
.ToLocalChecked().As<Object>();
|
242
245
|
|
243
|
-
Local<Function> stringify = JSON->Get(
|
244
|
-
|
246
|
+
Local<Function> stringify = JSON->Get(
|
247
|
+
context, v8::String::NewFromUtf8Literal(isolate, "stringify"))
|
248
|
+
.ToLocalChecked().As<Function>();
|
245
249
|
|
246
250
|
Local<Object> object = local_value->ToObject(context).ToLocalChecked();
|
247
251
|
const unsigned argc = 1;
|
@@ -295,7 +299,7 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
295
299
|
} else if(trycatch.HasTerminated()) {
|
296
300
|
evalRes.terminated = true;
|
297
301
|
evalRes.message = new Persistent<Value>();
|
298
|
-
Local<String> tmp = String::
|
302
|
+
Local<String> tmp = String::NewFromUtf8Literal(isolate, "JavaScript was terminated (either by timeout or explicitly)");
|
299
303
|
evalRes.message->Reset(isolate, tmp);
|
300
304
|
}
|
301
305
|
if (!trycatch.StackTrace(context).IsEmpty()) {
|
@@ -312,7 +316,8 @@ nogvl_context_eval(void* arg) {
|
|
312
316
|
|
313
317
|
EvalParams* eval_params = (EvalParams*)arg;
|
314
318
|
EvalResult* result = eval_params->result;
|
315
|
-
|
319
|
+
IsolateInfo* isolate_info = eval_params->context_info->isolate_info;
|
320
|
+
Isolate* isolate = isolate_info->isolate;
|
316
321
|
|
317
322
|
Isolate::Scope isolate_scope(isolate);
|
318
323
|
HandleScope handle_scope(isolate);
|
@@ -356,7 +361,10 @@ nogvl_context_eval(void* arg) {
|
|
356
361
|
// parsing successful
|
357
362
|
if (eval_params->max_memory > 0) {
|
358
363
|
isolate->SetData(MEM_SOFTLIMIT_VALUE, &eval_params->max_memory);
|
364
|
+
if (!isolate_info->added_gc_cb) {
|
359
365
|
isolate->AddGCEpilogueCallback(gc_callback);
|
366
|
+
isolate_info->added_gc_cb = true;
|
367
|
+
}
|
360
368
|
}
|
361
369
|
|
362
370
|
maybe_value = parsed_script.ToLocalChecked()->Run(context);
|
@@ -369,6 +377,12 @@ nogvl_context_eval(void* arg) {
|
|
369
377
|
return NULL;
|
370
378
|
}
|
371
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
|
+
|
372
386
|
// assumes isolate locking is in place
|
373
387
|
static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
374
388
|
Local<Value> value) {
|
@@ -400,8 +414,11 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
400
414
|
VALUE rb_array = rb_ary_new();
|
401
415
|
Local<Array> arr = Local<Array>::Cast(value);
|
402
416
|
for(uint32_t i=0; i < arr->Length(); i++) {
|
403
|
-
|
404
|
-
|
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());
|
405
422
|
if (rb_funcall(rb_elem, rb_intern("class"), 0) == rb_cFailedV8Conversion) {
|
406
423
|
return rb_elem;
|
407
424
|
}
|
@@ -431,18 +448,20 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
431
448
|
if (!maybe_props.IsEmpty()) {
|
432
449
|
Local<Array> props = maybe_props.ToLocalChecked();
|
433
450
|
for(uint32_t i=0; i < props->Length(); i++) {
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
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());
|
438
456
|
|
439
|
-
|
440
|
-
//
|
441
|
-
|
442
|
-
|
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();
|
443
461
|
}
|
444
462
|
|
445
|
-
VALUE rb_value = convert_v8_to_ruby(
|
463
|
+
VALUE rb_value = convert_v8_to_ruby(
|
464
|
+
isolate, context, prop_value.ToLocalChecked());
|
446
465
|
rb_hash_aset(rb_hash, rb_key, rb_value);
|
447
466
|
}
|
448
467
|
}
|
@@ -524,7 +543,7 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
524
543
|
length = RARRAY_LEN(value);
|
525
544
|
array = Array::New(isolate, (int)length);
|
526
545
|
for(i=0; i<length; i++) {
|
527
|
-
|
546
|
+
array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
|
528
547
|
}
|
529
548
|
return scope.Escape(array);
|
530
549
|
case T_HASH:
|
@@ -533,7 +552,7 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
533
552
|
length = RARRAY_LEN(hash_as_array);
|
534
553
|
for(i=0; i<length; i++) {
|
535
554
|
pair = rb_ary_entry(hash_as_array, i);
|
536
|
-
|
555
|
+
object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
|
537
556
|
convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 1)));
|
538
557
|
}
|
539
558
|
return scope.Escape(object);
|
@@ -563,9 +582,9 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
563
582
|
case T_UNDEF:
|
564
583
|
case T_NODE:
|
565
584
|
default:
|
566
|
-
return scope.Escape(String::
|
585
|
+
return scope.Escape(String::NewFromUtf8Literal(isolate, "Undefined Conversion"));
|
586
|
+
}
|
567
587
|
}
|
568
|
-
}
|
569
588
|
|
570
589
|
static void unblock_eval(void *ptr) {
|
571
590
|
EvalParams* eval = (EvalParams*)ptr;
|
@@ -576,53 +595,43 @@ static void unblock_eval(void *ptr) {
|
|
576
595
|
* The implementations of the run_extra_code(), create_snapshot_data_blob() and
|
577
596
|
* warm_up_snapshot_data_blob() functions have been derived from V8's test suite.
|
578
597
|
*/
|
579
|
-
bool run_extra_code(Isolate *isolate, Local<v8::Context> context,
|
598
|
+
static bool run_extra_code(Isolate *isolate, Local<v8::Context> context,
|
580
599
|
const char *utf8_source, const char *name) {
|
581
600
|
Context::Scope context_scope(context);
|
582
601
|
TryCatch try_catch(isolate);
|
583
602
|
Local<String> source_string;
|
584
|
-
if (!String::NewFromUtf8(isolate, utf8_source
|
585
|
-
NewStringType::kNormal)
|
586
|
-
.ToLocal(&source_string)) {
|
603
|
+
if (!String::NewFromUtf8(isolate, utf8_source).ToLocal(&source_string)) {
|
587
604
|
return false;
|
588
605
|
}
|
589
|
-
Local<
|
590
|
-
|
591
|
-
.ToLocalChecked();
|
606
|
+
Local<String> resource_name =
|
607
|
+
String::NewFromUtf8(isolate, name).ToLocalChecked();
|
592
608
|
ScriptOrigin origin(resource_name);
|
593
609
|
ScriptCompiler::Source source(source_string, origin);
|
594
610
|
Local<Script> script;
|
595
611
|
if (!ScriptCompiler::Compile(context, &source).ToLocal(&script))
|
596
612
|
return false;
|
597
|
-
if (script->Run(context).IsEmpty())
|
598
|
-
return false;
|
599
|
-
// CHECK(!try_catch.HasCaught());
|
613
|
+
if (script->Run(context).IsEmpty()) return false;
|
600
614
|
return true;
|
601
615
|
}
|
602
616
|
|
603
|
-
StartupData
|
617
|
+
static StartupData
|
604
618
|
create_snapshot_data_blob(const char *embedded_source = nullptr) {
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
SnapshotCreator snapshot_creator;
|
610
|
-
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);
|
611
623
|
{
|
612
624
|
HandleScope scope(isolate);
|
613
|
-
|
625
|
+
Local<v8::Context> context = v8::Context::New(isolate);
|
614
626
|
if (embedded_source != nullptr &&
|
615
|
-
!run_extra_code(isolate, context, embedded_source,
|
616
|
-
|
617
|
-
return result;
|
627
|
+
!run_extra_code(isolate, context, embedded_source, "<embedded>")) {
|
628
|
+
return {};
|
618
629
|
}
|
619
630
|
snapshot_creator.SetDefaultContext(context);
|
620
631
|
}
|
621
|
-
|
632
|
+
return snapshot_creator.CreateBlob(
|
622
633
|
SnapshotCreator::FunctionCodeHandling::kClear);
|
623
634
|
}
|
624
|
-
return result;
|
625
|
-
}
|
626
635
|
|
627
636
|
StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
|
628
637
|
const char *warmup_source) {
|
@@ -779,6 +788,19 @@ static VALUE rb_isolate_low_memory_notification(VALUE self) {
|
|
779
788
|
return Qnil;
|
780
789
|
}
|
781
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
|
+
|
782
804
|
static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
|
783
805
|
ContextInfo* context_info;
|
784
806
|
Data_Get_Struct(self, ContextInfo, context_info);
|
@@ -1049,7 +1071,9 @@ gvl_ruby_callback(void* data) {
|
|
1049
1071
|
callback_data.failed = false;
|
1050
1072
|
|
1051
1073
|
if ((bool)args->GetIsolate()->GetData(DO_TERMINATE) == true) {
|
1052
|
-
args->GetIsolate()->ThrowException(
|
1074
|
+
args->GetIsolate()->ThrowException(
|
1075
|
+
String::NewFromUtf8Literal(args->GetIsolate(),
|
1076
|
+
"Terminated execution during transition from Ruby to JS"));
|
1053
1077
|
args->GetIsolate()->TerminateExecution();
|
1054
1078
|
if (length > 0) {
|
1055
1079
|
rb_ary_clear(ruby_args);
|
@@ -1063,7 +1087,7 @@ gvl_ruby_callback(void* data) {
|
|
1063
1087
|
|
1064
1088
|
if(callback_data.failed) {
|
1065
1089
|
rb_iv_set(parent, "@current_exception", result);
|
1066
|
-
args->GetIsolate()->ThrowException(String::
|
1090
|
+
args->GetIsolate()->ThrowException(String::NewFromUtf8Literal(args->GetIsolate(), "Ruby exception"));
|
1067
1091
|
}
|
1068
1092
|
else {
|
1069
1093
|
HandleScope scope(args->GetIsolate());
|
@@ -1134,7 +1158,9 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1134
1158
|
|
1135
1159
|
if (parent_object == Qnil) {
|
1136
1160
|
context->Global()->Set(
|
1137
|
-
|
1161
|
+
context,
|
1162
|
+
v8_str,
|
1163
|
+
FunctionTemplate::New(isolate, ruby_callback, external)
|
1138
1164
|
->GetFunction(context)
|
1139
1165
|
.ToLocalChecked());
|
1140
1166
|
|
@@ -1147,7 +1173,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1147
1173
|
|
1148
1174
|
MaybeLocal<Script> parsed_script = Script::Compile(context, eval);
|
1149
1175
|
if (parsed_script.IsEmpty()) {
|
1150
|
-
|
1176
|
+
parse_error = true;
|
1151
1177
|
} else {
|
1152
1178
|
MaybeLocal<Value> maybe_value =
|
1153
1179
|
parsed_script.ToLocalChecked()->Run(context);
|
@@ -1157,11 +1183,12 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1157
1183
|
Local<Value> value = maybe_value.ToLocalChecked();
|
1158
1184
|
if (value->IsObject()) {
|
1159
1185
|
value.As<Object>()->Set(
|
1160
|
-
|
1161
|
-
|
1186
|
+
context,
|
1187
|
+
v8_str,
|
1188
|
+
FunctionTemplate::New(isolate, ruby_callback, external)
|
1162
1189
|
->GetFunction(context)
|
1163
1190
|
.ToLocalChecked());
|
1164
|
-
|
1191
|
+
attach_error = false;
|
1165
1192
|
}
|
1166
1193
|
}
|
1167
1194
|
}
|
@@ -1191,32 +1218,36 @@ static VALUE rb_context_isolate_mutex(VALUE self) {
|
|
1191
1218
|
return context_info->isolate_info->mutex;
|
1192
1219
|
}
|
1193
1220
|
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1197
|
-
|
1198
|
-
|
1199
|
-
|
1200
|
-
if (isolate_info->isolate) {
|
1201
|
-
if (isolate_info->interrupted) {
|
1202
|
-
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");
|
1203
1227
|
} else {
|
1204
|
-
|
1205
|
-
|
1206
|
-
|
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
|
+
);
|
1207
1238
|
} else {
|
1208
|
-
|
1239
|
+
isolate->Dispose();
|
1209
1240
|
}
|
1210
1241
|
}
|
1211
|
-
|
1242
|
+
isolate = nullptr;
|
1212
1243
|
}
|
1213
1244
|
|
1214
|
-
if (
|
1215
|
-
delete[]
|
1216
|
-
delete
|
1245
|
+
if (startup_data) {
|
1246
|
+
delete[] startup_data->data;
|
1247
|
+
delete startup_data;
|
1217
1248
|
}
|
1218
1249
|
|
1219
|
-
delete
|
1250
|
+
delete allocator;
|
1220
1251
|
}
|
1221
1252
|
|
1222
1253
|
static void free_context_raw(void *arg) {
|
@@ -1288,7 +1319,7 @@ static void mark_isolate(void* data) {
|
|
1288
1319
|
isolate_info->mark();
|
1289
1320
|
}
|
1290
1321
|
|
1291
|
-
void deallocate(void* data) {
|
1322
|
+
static void deallocate(void* data) {
|
1292
1323
|
ContextInfo* context_info = (ContextInfo*)data;
|
1293
1324
|
|
1294
1325
|
free_context(context_info);
|
@@ -1303,22 +1334,22 @@ static void mark_context(void* data) {
|
|
1303
1334
|
}
|
1304
1335
|
}
|
1305
1336
|
|
1306
|
-
void deallocate_external_function(void * data) {
|
1337
|
+
static void deallocate_external_function(void * data) {
|
1307
1338
|
xfree(data);
|
1308
1339
|
}
|
1309
1340
|
|
1310
|
-
void deallocate_snapshot(void * data) {
|
1341
|
+
static void deallocate_snapshot(void * data) {
|
1311
1342
|
SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
|
1312
1343
|
delete[] snapshot_info->data;
|
1313
1344
|
xfree(snapshot_info);
|
1314
1345
|
}
|
1315
1346
|
|
1316
|
-
VALUE allocate_external_function(VALUE klass) {
|
1347
|
+
static VALUE allocate_external_function(VALUE klass) {
|
1317
1348
|
VALUE* self = ALLOC(VALUE);
|
1318
1349
|
return Data_Wrap_Struct(klass, NULL, deallocate_external_function, (void*)self);
|
1319
1350
|
}
|
1320
1351
|
|
1321
|
-
VALUE allocate(VALUE klass) {
|
1352
|
+
static VALUE allocate(VALUE klass) {
|
1322
1353
|
ContextInfo* context_info = ALLOC(ContextInfo);
|
1323
1354
|
context_info->isolate_info = NULL;
|
1324
1355
|
context_info->context = NULL;
|
@@ -1326,7 +1357,7 @@ VALUE allocate(VALUE klass) {
|
|
1326
1357
|
return Data_Wrap_Struct(klass, mark_context, deallocate, (void*)context_info);
|
1327
1358
|
}
|
1328
1359
|
|
1329
|
-
VALUE allocate_snapshot(VALUE klass) {
|
1360
|
+
static VALUE allocate_snapshot(VALUE klass) {
|
1330
1361
|
SnapshotInfo* snapshot_info = ALLOC(SnapshotInfo);
|
1331
1362
|
snapshot_info->data = NULL;
|
1332
1363
|
snapshot_info->raw_size = 0;
|
@@ -1334,7 +1365,7 @@ VALUE allocate_snapshot(VALUE klass) {
|
|
1334
1365
|
return Data_Wrap_Struct(klass, NULL, deallocate_snapshot, (void*)snapshot_info);
|
1335
1366
|
}
|
1336
1367
|
|
1337
|
-
VALUE allocate_isolate(VALUE klass) {
|
1368
|
+
static VALUE allocate_isolate(VALUE klass) {
|
1338
1369
|
IsolateInfo* isolate_info = new IsolateInfo();
|
1339
1370
|
|
1340
1371
|
return Data_Wrap_Struct(klass, mark_isolate, deallocate_isolate, (void*)isolate_info);
|
@@ -1473,7 +1504,8 @@ nogvl_context_call(void *args) {
|
|
1473
1504
|
if (!call) {
|
1474
1505
|
return NULL;
|
1475
1506
|
}
|
1476
|
-
|
1507
|
+
IsolateInfo *isolate_info = call->context_info->isolate_info;
|
1508
|
+
Isolate* isolate = isolate_info->isolate;
|
1477
1509
|
|
1478
1510
|
// in gvl flag
|
1479
1511
|
isolate->SetData(IN_GVL, (void*)false);
|
@@ -1483,7 +1515,10 @@ nogvl_context_call(void *args) {
|
|
1483
1515
|
if (call->max_memory > 0) {
|
1484
1516
|
isolate->SetData(MEM_SOFTLIMIT_VALUE, &call->max_memory);
|
1485
1517
|
isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)false);
|
1518
|
+
if (!isolate_info->added_gc_cb) {
|
1486
1519
|
isolate->AddGCEpilogueCallback(gc_callback);
|
1520
|
+
isolate_info->added_gc_cb = true;
|
1521
|
+
}
|
1487
1522
|
}
|
1488
1523
|
|
1489
1524
|
Isolate::Scope isolate_scope(isolate);
|
@@ -1561,8 +1596,11 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1561
1596
|
|
1562
1597
|
// examples of such usage can be found in
|
1563
1598
|
// https://github.com/v8/v8/blob/36b32aa28db5e993312f4588d60aad5c8330c8a5/test/cctest/test-api.cc#L15711
|
1564
|
-
|
1565
|
-
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
|
+
}
|
1566
1604
|
|
1567
1605
|
if (val.IsEmpty() || !val.ToLocalChecked()->IsFunction()) {
|
1568
1606
|
missingFunction = true;
|
@@ -1611,6 +1649,7 @@ static void set_ruby_exiting(VALUE value) {
|
|
1611
1649
|
(void)value;
|
1612
1650
|
|
1613
1651
|
int res = pthread_rwlock_wrlock(&exit_lock);
|
1652
|
+
|
1614
1653
|
ruby_exiting = true;
|
1615
1654
|
if (res == 0) {
|
1616
1655
|
pthread_rwlock_unlock(&exit_lock);
|
@@ -1619,7 +1658,7 @@ static void set_ruby_exiting(VALUE value) {
|
|
1619
1658
|
|
1620
1659
|
extern "C" {
|
1621
1660
|
|
1622
|
-
void Init_mini_racer_extension ( void )
|
1661
|
+
__attribute__((visibility("default"))) void Init_mini_racer_extension ( void )
|
1623
1662
|
{
|
1624
1663
|
VALUE rb_mMiniRacer = rb_define_module("MiniRacer");
|
1625
1664
|
rb_cContext = rb_define_class_under(rb_mMiniRacer, "Context", rb_cObject);
|
@@ -1668,7 +1707,7 @@ extern "C" {
|
|
1668
1707
|
|
1669
1708
|
rb_define_method(rb_cIsolate, "idle_notification", (VALUE(*)(...))&rb_isolate_idle_notification, 1);
|
1670
1709
|
rb_define_method(rb_cIsolate, "low_memory_notification", (VALUE(*)(...))&rb_isolate_low_memory_notification, 0);
|
1671
|
-
|
1710
|
+
rb_define_method(rb_cIsolate, "pump_message_loop", (VALUE(*)(...))&rb_isolate_pump_message_loop, 0);
|
1672
1711
|
rb_define_private_method(rb_cIsolate, "init_with_snapshot",(VALUE(*)(...))&rb_isolate_init_with_snapshot, 1);
|
1673
1712
|
|
1674
1713
|
rb_define_singleton_method(rb_cPlatform, "set_flag_as_str!", (VALUE(*)(...))&rb_platform_set_flag_as_str, 1);
|
@@ -1681,5 +1720,9 @@ extern "C" {
|
|
1681
1720
|
thread_attr_p = &attr;
|
1682
1721
|
}
|
1683
1722
|
}
|
1723
|
+
auto on_fork_for_child = []() {
|
1724
|
+
exit_lock = PTHREAD_RWLOCK_INITIALIZER;
|
1725
|
+
};
|
1726
|
+
pthread_atfork(nullptr, nullptr, on_fork_for_child);
|
1684
1727
|
}
|
1685
1728
|
}
|
@@ -0,0 +1,123 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <dlfcn.h>
|
3
|
+
#include <string.h>
|
4
|
+
#include <stdint.h>
|
5
|
+
#include <stdlib.h>
|
6
|
+
|
7
|
+
// Load a Ruby extension like Ruby does, only with flags that:
|
8
|
+
// a) hide symbols from other extensions (RTLD_LOCAL)
|
9
|
+
// b) bind symbols tightly (RTLD_DEEPBIND, when available)
|
10
|
+
|
11
|
+
void Init_mini_racer_loader(void);
|
12
|
+
|
13
|
+
static void *_dln_load(const char *file);
|
14
|
+
|
15
|
+
static VALUE _load_shared_lib(VALUE self, volatile VALUE fname)
|
16
|
+
{
|
17
|
+
(void) self;
|
18
|
+
|
19
|
+
// check that path is not tainted
|
20
|
+
SafeStringValue(fname);
|
21
|
+
|
22
|
+
FilePathValue(fname);
|
23
|
+
VALUE path = rb_str_encode_ospath(fname);
|
24
|
+
|
25
|
+
char *loc = StringValueCStr(path);
|
26
|
+
void *handle = _dln_load(loc);
|
27
|
+
|
28
|
+
return handle ? Qtrue : Qfalse;
|
29
|
+
}
|
30
|
+
|
31
|
+
// adapted from Ruby's dln.c
|
32
|
+
#define INIT_FUNC_PREFIX ((char[]) {'I', 'n', 'i', 't', '_'})
|
33
|
+
#define INIT_FUNCNAME(buf, file) do { \
|
34
|
+
const char *base = (file); \
|
35
|
+
const size_t flen = _init_funcname(&base); \
|
36
|
+
const size_t plen = sizeof(INIT_FUNC_PREFIX); \
|
37
|
+
char *const tmp = ALLOCA_N(char, plen + flen + 1); \
|
38
|
+
memcpy(tmp, INIT_FUNC_PREFIX, plen); \
|
39
|
+
memcpy(tmp+plen, base, flen); \
|
40
|
+
tmp[plen+flen] = '\0'; \
|
41
|
+
*(buf) = tmp; \
|
42
|
+
} while(0)
|
43
|
+
|
44
|
+
// adapted from Ruby's dln.c
|
45
|
+
static size_t _init_funcname(const char **file)
|
46
|
+
{
|
47
|
+
const char *p = *file,
|
48
|
+
*base,
|
49
|
+
*dot = NULL;
|
50
|
+
|
51
|
+
for (base = p; *p; p++) { /* Find position of last '/' */
|
52
|
+
if (*p == '.' && !dot) {
|
53
|
+
dot = p;
|
54
|
+
}
|
55
|
+
if (*p == '/') {
|
56
|
+
base = p + 1;
|
57
|
+
dot = NULL;
|
58
|
+
}
|
59
|
+
}
|
60
|
+
*file = base;
|
61
|
+
return (uintptr_t) ((dot ? dot : p) - base);
|
62
|
+
}
|
63
|
+
|
64
|
+
// adapted from Ruby's dln.c
|
65
|
+
static void *_dln_load(const char *file)
|
66
|
+
{
|
67
|
+
char *buf;
|
68
|
+
const char *error;
|
69
|
+
#define DLN_ERROR() (error = dlerror(), strcpy(ALLOCA_N(char, strlen(error) + 1), error))
|
70
|
+
|
71
|
+
void *handle;
|
72
|
+
void (*init_fct)(void);
|
73
|
+
|
74
|
+
INIT_FUNCNAME(&buf, file);
|
75
|
+
|
76
|
+
#ifndef RTLD_DEEPBIND
|
77
|
+
# define RTLD_DEEPBIND 0
|
78
|
+
#endif
|
79
|
+
/* Load file */
|
80
|
+
if ((handle = dlopen(file, RTLD_LAZY|RTLD_LOCAL|RTLD_DEEPBIND)) == NULL) {
|
81
|
+
DLN_ERROR();
|
82
|
+
goto failed;
|
83
|
+
}
|
84
|
+
#if defined(RUBY_EXPORT)
|
85
|
+
{
|
86
|
+
static const char incompatible[] = "incompatible library version";
|
87
|
+
void *ex = dlsym(handle, "ruby_xmalloc");
|
88
|
+
if (ex && ex != (void *) &ruby_xmalloc) {
|
89
|
+
|
90
|
+
# if defined __APPLE__
|
91
|
+
/* dlclose() segfaults */
|
92
|
+
rb_fatal("%s - %s", incompatible, file);
|
93
|
+
# else
|
94
|
+
dlclose(handle);
|
95
|
+
error = incompatible;
|
96
|
+
goto failed;
|
97
|
+
#endif
|
98
|
+
}
|
99
|
+
}
|
100
|
+
# endif
|
101
|
+
|
102
|
+
init_fct = (void (*)(void)) dlsym(handle, buf);
|
103
|
+
if (init_fct == NULL) {
|
104
|
+
error = DLN_ERROR();
|
105
|
+
dlclose(handle);
|
106
|
+
goto failed;
|
107
|
+
}
|
108
|
+
|
109
|
+
/* Call the init code */
|
110
|
+
(*init_fct)();
|
111
|
+
|
112
|
+
return handle;
|
113
|
+
|
114
|
+
failed:
|
115
|
+
rb_raise(rb_eLoadError, "%s", error);
|
116
|
+
}
|
117
|
+
|
118
|
+
__attribute__((visibility("default"))) void Init_mini_racer_loader()
|
119
|
+
{
|
120
|
+
VALUE mMiniRacer = rb_define_module("MiniRacer");
|
121
|
+
VALUE mLoader = rb_define_module_under(mMiniRacer, "Loader");
|
122
|
+
rb_define_singleton_method(mLoader, "load", _load_shared_lib, 1);
|
123
|
+
}
|
data/lib/mini_racer.rb
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
require "mini_racer/version"
|
2
|
-
require "
|
2
|
+
require "mini_racer_loader"
|
3
|
+
require "pathname"
|
4
|
+
|
5
|
+
ext_filename = "mini_racer_extension.#{RbConfig::CONFIG['DLEXT']}"
|
6
|
+
ext_path = Gem.loaded_specs['mini_racer'].require_paths
|
7
|
+
.map { |p| (p = Pathname.new(p)).absolute? ? p : Pathname.new(__dir__).parent + p }
|
8
|
+
ext_found = ext_path.map { |p| p + ext_filename }.find { |p| p.file? }
|
9
|
+
|
10
|
+
raise LoadError, "Could not find #{ext_filename} in #{ext_path.map(&:to_s)}" unless ext_found
|
11
|
+
MiniRacer::Loader.load(ext_found.to_s)
|
12
|
+
|
3
13
|
require "thread"
|
4
14
|
require "json"
|
5
15
|
|
@@ -130,23 +140,22 @@ module MiniRacer
|
|
130
140
|
end
|
131
141
|
end
|
132
142
|
|
133
|
-
def initialize(
|
143
|
+
def initialize(max_memory: nil, timeout: nil, isolate: nil, ensure_gc_after_idle: nil, snapshot: nil)
|
134
144
|
options ||= {}
|
135
145
|
|
136
|
-
check_init_options!(
|
146
|
+
check_init_options!(isolate: isolate, snapshot: snapshot, max_memory: max_memory, ensure_gc_after_idle: ensure_gc_after_idle, timeout: timeout)
|
137
147
|
|
138
148
|
@functions = {}
|
139
149
|
@timeout = nil
|
140
150
|
@max_memory = nil
|
141
151
|
@current_exception = nil
|
142
|
-
@timeout =
|
143
|
-
|
144
|
-
|
145
|
-
end
|
152
|
+
@timeout = timeout
|
153
|
+
@max_memory = max_memory
|
154
|
+
|
146
155
|
# false signals it should be fetched if requested
|
147
|
-
@isolate =
|
156
|
+
@isolate = isolate || false
|
148
157
|
|
149
|
-
@ensure_gc_after_idle =
|
158
|
+
@ensure_gc_after_idle = ensure_gc_after_idle
|
150
159
|
|
151
160
|
if @ensure_gc_after_idle
|
152
161
|
@last_eval = nil
|
@@ -162,7 +171,7 @@ module MiniRacer
|
|
162
171
|
@eval_thread = nil
|
163
172
|
|
164
173
|
# defined in the C class
|
165
|
-
init_unsafe(
|
174
|
+
init_unsafe(isolate, snapshot)
|
166
175
|
end
|
167
176
|
|
168
177
|
def isolate
|
@@ -290,6 +299,7 @@ module MiniRacer
|
|
290
299
|
@ensure_gc_mutex.synchronize do
|
291
300
|
@ensure_gc_thread = nil if !@ensure_gc_thread&.alive?
|
292
301
|
@ensure_gc_thread ||= Thread.new do
|
302
|
+
ensure_gc_after_idle_seconds = @ensure_gc_after_idle / 1000.0
|
293
303
|
done = false
|
294
304
|
while !done
|
295
305
|
now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
@@ -299,7 +309,7 @@ module MiniRacer
|
|
299
309
|
break
|
300
310
|
end
|
301
311
|
|
302
|
-
if !@eval_thread &&
|
312
|
+
if !@eval_thread && ensure_gc_after_idle_seconds < now - @last_eval
|
303
313
|
@ensure_gc_mutex.synchronize do
|
304
314
|
isolate_mutex.synchronize do
|
305
315
|
if !@eval_thread
|
@@ -310,7 +320,7 @@ module MiniRacer
|
|
310
320
|
end
|
311
321
|
end
|
312
322
|
end
|
313
|
-
sleep
|
323
|
+
sleep ensure_gc_after_idle_seconds if !done
|
314
324
|
end
|
315
325
|
end
|
316
326
|
end
|
@@ -369,15 +379,29 @@ module MiniRacer
|
|
369
379
|
rp.close if rp
|
370
380
|
end
|
371
381
|
|
372
|
-
def check_init_options!(
|
373
|
-
assert_option_is_nil_or_a('isolate',
|
374
|
-
assert_option_is_nil_or_a('snapshot',
|
382
|
+
def check_init_options!(isolate:, snapshot:, max_memory:, ensure_gc_after_idle:, timeout:)
|
383
|
+
assert_option_is_nil_or_a('isolate', isolate, Isolate)
|
384
|
+
assert_option_is_nil_or_a('snapshot', snapshot, Snapshot)
|
385
|
+
|
386
|
+
assert_numeric_or_nil('max_memory', max_memory, min_value: 10_000)
|
387
|
+
assert_numeric_or_nil('ensure_gc_after_idle', ensure_gc_after_idle, min_value: 1)
|
388
|
+
assert_numeric_or_nil('timeout', timeout, min_value: 1)
|
375
389
|
|
376
|
-
if
|
390
|
+
if isolate && snapshot
|
377
391
|
raise ArgumentError, 'can only pass one of isolate and snapshot options'
|
378
392
|
end
|
379
393
|
end
|
380
394
|
|
395
|
+
def assert_numeric_or_nil(option_name, object, min_value:)
|
396
|
+
if object.is_a?(Numeric) && object < min_value
|
397
|
+
raise ArgumentError, "#{option_name} must be larger than #{min_value}"
|
398
|
+
end
|
399
|
+
|
400
|
+
if !object.nil? && !object.is_a?(Numeric)
|
401
|
+
raise ArgumentError, "#{option_name} must be a number, passed a #{object.inspect}"
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
381
405
|
def assert_option_is_nil_or_a(option_name, object, klass)
|
382
406
|
unless object.nil? || object.is_a?(klass)
|
383
407
|
raise ArgumentError, "#{option_name} must be a #{klass} object, passed a #{object.inspect}"
|
data/lib/mini_racer/version.rb
CHANGED
data/mini_racer.gemspec
CHANGED
@@ -30,11 +30,12 @@ Gem::Specification.new do |spec|
|
|
30
30
|
spec.add_development_dependency "rake", ">= 12.3.3"
|
31
31
|
spec.add_development_dependency "minitest", "~> 5.0"
|
32
32
|
spec.add_development_dependency "rake-compiler"
|
33
|
+
spec.add_development_dependency "m"
|
33
34
|
|
34
|
-
spec.add_dependency 'libv8',
|
35
|
+
spec.add_dependency 'libv8-node', MiniRacer::LIBV8_NODE_VERSION
|
35
36
|
spec.require_paths = ["lib", "ext"]
|
36
37
|
|
37
|
-
spec.extensions = ["ext/mini_racer_extension/extconf.rb"]
|
38
|
+
spec.extensions = ["ext/mini_racer_loader/extconf.rb", "ext/mini_racer_extension/extconf.rb"]
|
38
39
|
|
39
40
|
spec.required_ruby_version = '>= 2.3'
|
40
41
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mini_racer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0.beta1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Saffron
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-04-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -67,31 +67,49 @@ dependencies:
|
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: m
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- - "
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
74
81
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: libv8-node
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 15.12.0.0.beta1
|
76
90
|
type: :runtime
|
77
91
|
prerelease: false
|
78
92
|
version_requirements: !ruby/object:Gem::Requirement
|
79
93
|
requirements:
|
80
|
-
- - "
|
94
|
+
- - "~>"
|
81
95
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
96
|
+
version: 15.12.0.0.beta1
|
83
97
|
description: Minimal embedded v8 engine for Ruby
|
84
98
|
email:
|
85
99
|
- sam.saffron@gmail.com
|
86
100
|
executables: []
|
87
101
|
extensions:
|
102
|
+
- ext/mini_racer_loader/extconf.rb
|
88
103
|
- ext/mini_racer_extension/extconf.rb
|
89
104
|
extra_rdoc_files: []
|
90
105
|
files:
|
106
|
+
- ".dockerignore"
|
107
|
+
- ".github/workflows/ci.yml"
|
91
108
|
- ".gitignore"
|
92
109
|
- ".travis.yml"
|
93
110
|
- CHANGELOG
|
94
111
|
- CODE_OF_CONDUCT.md
|
112
|
+
- Dockerfile
|
95
113
|
- Gemfile
|
96
114
|
- LICENSE.txt
|
97
115
|
- README.md
|
@@ -100,6 +118,8 @@ files:
|
|
100
118
|
- bin/setup
|
101
119
|
- ext/mini_racer_extension/extconf.rb
|
102
120
|
- ext/mini_racer_extension/mini_racer_extension.cc
|
121
|
+
- ext/mini_racer_loader/extconf.rb
|
122
|
+
- ext/mini_racer_loader/mini_racer_loader.c
|
103
123
|
- lib/mini_racer.rb
|
104
124
|
- lib/mini_racer/version.rb
|
105
125
|
- mini_racer.gemspec
|
@@ -108,10 +128,10 @@ licenses:
|
|
108
128
|
- MIT
|
109
129
|
metadata:
|
110
130
|
bug_tracker_uri: https://github.com/discourse/mini_racer/issues
|
111
|
-
changelog_uri: https://github.com/discourse/mini_racer/blob/v0.
|
112
|
-
documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.
|
113
|
-
source_code_uri: https://github.com/discourse/mini_racer/tree/v0.
|
114
|
-
post_install_message:
|
131
|
+
changelog_uri: https://github.com/discourse/mini_racer/blob/v0.4.0.beta1/CHANGELOG
|
132
|
+
documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.4.0.beta1
|
133
|
+
source_code_uri: https://github.com/discourse/mini_racer/tree/v0.4.0.beta1
|
134
|
+
post_install_message:
|
115
135
|
rdoc_options: []
|
116
136
|
require_paths:
|
117
137
|
- lib
|
@@ -123,12 +143,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
123
143
|
version: '2.3'
|
124
144
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
125
145
|
requirements:
|
126
|
-
- - "
|
146
|
+
- - ">"
|
127
147
|
- !ruby/object:Gem::Version
|
128
|
-
version:
|
148
|
+
version: 1.3.1
|
129
149
|
requirements: []
|
130
|
-
rubygems_version: 3.
|
131
|
-
signing_key:
|
150
|
+
rubygems_version: 3.2.2
|
151
|
+
signing_key:
|
132
152
|
specification_version: 4
|
133
153
|
summary: Minimal embedded v8 for Ruby
|
134
154
|
test_files: []
|