mini_racer 0.2.13 → 0.4.0.beta1
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 +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: []
|