mini_racer 0.6.2 → 0.6.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +72 -26
- data/CHANGELOG +11 -1
- data/README.md +18 -16
- data/Rakefile +15 -3
- data/ext/mini_racer_extension/extconf.rb +9 -0
- data/ext/mini_racer_extension/mini_racer_extension.cc +25 -63
- data/ext/mini_racer_loader/extconf.rb +5 -0
- data/lib/mini_racer/truffleruby.rb +353 -0
- data/lib/mini_racer/version.rb +1 -1
- data/lib/mini_racer.rb +19 -17
- metadata +7 -7
- data/.travis.yml +0 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4290b52d3d19c196567892c0d3eda0b47f07047afe059e63d4ac976258cb52a0
|
4
|
+
data.tar.gz: 55e41b4e8ab93cdfe45446ff08a5b3c42323356e36b79d91f2e564c43a7715fe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c0e0cca11803ffa6ba38998810ccb82413257145947a637f3800252f4a0d47bd47fde425786c09f1eedf6c28b1bb584d34914b111effffef908ff182e79c5329
|
7
|
+
data.tar.gz: 05dbfe34c140fedf6fbd33f5eee62f6cc6880579d238039f2e351a0279fa9267575b46ae7890f9c7d19ebd43bf1020e9bb57d445ad4e6ac8f89440ac3e2744d3
|
data/.github/workflows/ci.yml
CHANGED
@@ -1,48 +1,92 @@
|
|
1
|
-
name:
|
1
|
+
name: Tests
|
2
|
+
|
2
3
|
on:
|
3
|
-
|
4
|
+
pull_request:
|
5
|
+
push:
|
6
|
+
branches:
|
7
|
+
- master
|
4
8
|
|
5
9
|
jobs:
|
10
|
+
test-truffleruby:
|
11
|
+
strategy:
|
12
|
+
fail-fast: false
|
13
|
+
matrix:
|
14
|
+
os:
|
15
|
+
- "macos-11"
|
16
|
+
- "macos-12"
|
17
|
+
- "ubuntu-20.04"
|
18
|
+
ruby:
|
19
|
+
- "truffleruby+graalvm-head"
|
20
|
+
|
21
|
+
name: ${{ matrix.os }} - ${{ matrix.ruby }}
|
22
|
+
runs-on: ${{ matrix.os }}
|
23
|
+
|
24
|
+
env:
|
25
|
+
TRUFFLERUBYOPT: "--jvm --polyglot"
|
26
|
+
|
27
|
+
steps:
|
28
|
+
- uses: actions/checkout@v3
|
29
|
+
- uses: ruby/setup-ruby@v1
|
30
|
+
with:
|
31
|
+
ruby-version: ${{ matrix.ruby }}
|
32
|
+
bundler-cache: true
|
33
|
+
- name: Install GraalVM JS component
|
34
|
+
run: gu install js
|
35
|
+
- name: Compile
|
36
|
+
run: bundle exec rake compile
|
37
|
+
- name: Test
|
38
|
+
run: bundle exec rake test
|
39
|
+
|
6
40
|
test-darwin:
|
7
41
|
strategy:
|
8
42
|
fail-fast: false
|
9
43
|
matrix:
|
10
44
|
os:
|
11
|
-
-
|
12
|
-
-
|
13
|
-
|
14
|
-
-
|
15
|
-
|
16
|
-
|
17
|
-
|
45
|
+
- "macos-11"
|
46
|
+
- "macos-12"
|
47
|
+
ruby:
|
48
|
+
- "ruby-2.6"
|
49
|
+
- "ruby-2.7"
|
50
|
+
- "ruby-3.0"
|
51
|
+
- "ruby-3.1"
|
52
|
+
|
53
|
+
name: ${{ matrix.os }} - ${{ matrix.ruby }}
|
54
|
+
runs-on: ${{ matrix.os }}
|
55
|
+
|
18
56
|
steps:
|
19
|
-
-
|
20
|
-
|
21
|
-
|
22
|
-
|
57
|
+
- uses: actions/checkout@v3
|
58
|
+
- uses: ruby/setup-ruby@v1
|
59
|
+
with:
|
60
|
+
ruby-version: ${{ matrix.ruby }}
|
61
|
+
bundler-cache: true
|
23
62
|
- name: Compile
|
24
63
|
run: bundle exec rake compile
|
25
64
|
- name: Test
|
26
65
|
run: bundle exec rake test
|
66
|
+
|
27
67
|
test-linux:
|
28
68
|
strategy:
|
29
69
|
fail-fast: false
|
30
70
|
matrix:
|
31
71
|
ruby:
|
32
|
-
-
|
33
|
-
-
|
34
|
-
-
|
72
|
+
- "2.6"
|
73
|
+
- "2.7"
|
74
|
+
- "3.0"
|
75
|
+
- "3.1"
|
35
76
|
platform:
|
36
|
-
- amd64
|
37
|
-
- arm64
|
38
|
-
# arm
|
39
|
-
# ppc64le
|
40
|
-
# s390x
|
77
|
+
- "amd64"
|
78
|
+
- "arm64"
|
41
79
|
libc:
|
42
|
-
- gnu
|
43
|
-
- musl
|
44
|
-
|
80
|
+
- "gnu"
|
81
|
+
- "musl"
|
82
|
+
exclude:
|
83
|
+
# there's no libv8-node (v16) for aarch64-linux-musl at the moment
|
84
|
+
- platform: "arm64"
|
85
|
+
libc: "musl"
|
86
|
+
|
87
|
+
name: linux-${{ matrix.platform }} - ruby-${{ matrix.ruby }} - ${{ matrix.libc }}
|
45
88
|
runs-on: ubuntu-20.04
|
89
|
+
|
46
90
|
steps:
|
47
91
|
- name: Enable ${{ matrix.platform }} platform
|
48
92
|
id: qemu
|
@@ -67,9 +111,11 @@ jobs:
|
|
67
111
|
echo "::set-output name=id::$(cat container_id)"
|
68
112
|
- name: Install Alpine system dependencies
|
69
113
|
if: ${{ matrix.libc == 'musl' }}
|
70
|
-
run: docker exec -w "${PWD}" ${{ steps.container.outputs.id }} apk add --no-cache build-base
|
114
|
+
run: docker exec -w "${PWD}" ${{ steps.container.outputs.id }} apk add --no-cache build-base bash git
|
71
115
|
- name: Checkout
|
72
|
-
uses: actions/checkout@
|
116
|
+
uses: actions/checkout@v3
|
117
|
+
- name: Update Rubygems
|
118
|
+
run: docker exec -w "${PWD}" ${{ steps.container.outputs.id }} gem update --system
|
73
119
|
- name: Bundle
|
74
120
|
run: docker exec -w "${PWD}" ${{ steps.container.outputs.id }} bundle install
|
75
121
|
- name: Compile
|
data/CHANGELOG
CHANGED
@@ -1,4 +1,14 @@
|
|
1
|
-
-
|
1
|
+
- 16-08-2022
|
2
|
+
|
3
|
+
- 0.6.3
|
4
|
+
|
5
|
+
- Truffle ruby support! Thanks to Brandon Fish and the truffle team
|
6
|
+
- Hide libv8 symbols on ELF targets
|
7
|
+
- Slightly shrunk binary size
|
8
|
+
- Simplified timeout implementation
|
9
|
+
- Some stability fixes
|
10
|
+
|
11
|
+
- 17-01-2022
|
2
12
|
|
3
13
|
- 0.6.2
|
4
14
|
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# MiniRacer
|
2
2
|
|
3
|
-
[![
|
3
|
+
[![Test](https://github.com/rubyjs/mini_racer/actions/workflows/ci.yml/badge.svg)](https://github.com/rubyjs/mini_racer/actions/workflows/ci.yml)
|
4
4
|
|
5
5
|
Minimal, modern embedded V8 for Ruby.
|
6
6
|
|
@@ -12,7 +12,7 @@ MiniRacer has an adapter for [execjs](https://github.com/rails/execjs) so it can
|
|
12
12
|
|
13
13
|
### A note about Ruby version Support
|
14
14
|
|
15
|
-
MiniRacer only supports non-EOL versions of Ruby. See [Ruby](https://www.ruby-lang.org/en/downloads)
|
15
|
+
MiniRacer only supports non-EOL versions of Ruby. See [Ruby Maintenance Branches](https://www.ruby-lang.org/en/downloads/branches/) for the list of non-EOL Rubies.
|
16
16
|
|
17
17
|
If you require support for older versions of Ruby install an older version of the gem.
|
18
18
|
|
@@ -112,16 +112,21 @@ context.eval('bar()', filename: 'a/bar.js')
|
|
112
112
|
|
113
113
|
### Fork safety
|
114
114
|
|
115
|
-
Some Ruby web servers employ forking (for example unicorn or puma in clustered mode). V8 is not fork safe.
|
116
|
-
|
115
|
+
Some Ruby web servers employ forking (for example unicorn or puma in clustered mode). V8 is not fork safe by default and sadly Ruby does not have support for fork notifications per [#5446](https://bugs.ruby-lang.org/issues/5446).
|
116
|
+
|
117
|
+
Since 0.6.1 mini_racer does support V8 single threaded platform mode which should remove most forking related issues. To enable run this before using `MiniRacer::Context`:
|
118
|
+
|
119
|
+
```ruby
|
120
|
+
MiniRacer::Platform.set_flags!(:single_threaded)
|
121
|
+
```
|
117
122
|
|
118
123
|
If you want to ensure your application does not leak memory after fork either:
|
119
124
|
|
120
|
-
1. Ensure no MiniRacer::Context objects are created in the master process
|
125
|
+
1. Ensure no `MiniRacer::Context` objects are created in the master process
|
121
126
|
|
122
127
|
Or
|
123
128
|
|
124
|
-
2. Dispose manually of all MiniRacer::Context objects prior to forking
|
129
|
+
2. Dispose manually of all `MiniRacer::Context` objects prior to forking
|
125
130
|
|
126
131
|
```ruby
|
127
132
|
# before fork
|
@@ -419,18 +424,15 @@ Or install it yourself as:
|
|
419
424
|
**Note** using v8.h and compiling MiniRacer requires a C++11 standard compiler, more specifically clang 3.5 (or later) or GCC 6.3 (or later).
|
420
425
|
|
421
426
|
|
422
|
-
|
427
|
+
### Troubleshooting
|
423
428
|
|
424
|
-
|
429
|
+
If you have a problem installing mini_racer, please consider the following steps:
|
425
430
|
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
- sudo: required
|
432
|
-
- dist: trusty
|
433
|
-
```
|
431
|
+
* make sure you try the latest released version of mini_racer
|
432
|
+
* make sure you have Rubygems >= 3.2.13 and bundler >= 2.2.13 installed via `gem update --system`
|
433
|
+
* if you are using bundler, make sure to have `PLATFORMS` set correctly in `Gemfile.lock` via `bundle lock --add-platform`
|
434
|
+
* make sure to recompile/reinstall `mini_racer` and `libv8-node` after system upgrades (for example via `gem uninstall --all mini_racer libv8-node`)
|
435
|
+
* make sure you are on the latest patch/teeny version of a supported Ruby branch
|
434
436
|
|
435
437
|
## Similar Projects
|
436
438
|
|
data/Rakefile
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
2
|
require "rake/testtask"
|
3
|
-
require "rake/extensiontask"
|
4
3
|
|
5
4
|
Rake::TestTask.new(:test) do |t|
|
6
5
|
t.libs << "test"
|
@@ -11,8 +10,21 @@ end
|
|
11
10
|
task :default => [:compile, :test]
|
12
11
|
|
13
12
|
gem = Gem::Specification.load( File.dirname(__FILE__) + '/mini_racer.gemspec' )
|
14
|
-
|
15
|
-
|
13
|
+
|
14
|
+
if RUBY_ENGINE == "truffleruby"
|
15
|
+
task :compile do
|
16
|
+
# noop
|
17
|
+
end
|
18
|
+
|
19
|
+
task :clean do
|
20
|
+
# noop
|
21
|
+
end
|
22
|
+
else
|
23
|
+
require 'rake/extensiontask'
|
24
|
+
Rake::ExtensionTask.new( 'mini_racer_loader', gem )
|
25
|
+
Rake::ExtensionTask.new( 'mini_racer_extension', gem )
|
26
|
+
end
|
27
|
+
|
16
28
|
|
17
29
|
|
18
30
|
# via http://blog.flavorjon.es/2009/06/easily-valgrind-gdb-your-ruby-c.html
|
@@ -1,4 +1,10 @@
|
|
1
1
|
require 'mkmf'
|
2
|
+
|
3
|
+
if RUBY_ENGINE == "truffleruby"
|
4
|
+
File.write("Makefile", dummy_makefile($srcdir).join(""))
|
5
|
+
return
|
6
|
+
end
|
7
|
+
|
2
8
|
require_relative '../../lib/mini_racer/version'
|
3
9
|
gem 'libv8-node', MiniRacer::LIBV8_NODE_VERSION
|
4
10
|
require 'libv8-node'
|
@@ -70,6 +76,9 @@ end
|
|
70
76
|
|
71
77
|
Libv8::Node.configure_makefile
|
72
78
|
|
79
|
+
# --exclude-libs is only for i386 PE and ELF targeted ports
|
80
|
+
append_ldflags("-Wl,--exclude-libs=ALL ")
|
81
|
+
|
73
82
|
if enable_config('asan')
|
74
83
|
$CXXFLAGS.insert(0, " -fsanitize=address ")
|
75
84
|
$LDFLAGS.insert(0, " -fsanitize=address ")
|
@@ -305,8 +305,7 @@ static std::unique_ptr<Platform> current_platform = NULL;
|
|
305
305
|
static std::mutex platform_lock;
|
306
306
|
|
307
307
|
static pthread_attr_t *thread_attr_p;
|
308
|
-
static
|
309
|
-
static bool ruby_exiting = false; // guarded by exit_lock
|
308
|
+
static std::atomic_int ruby_exiting(0);
|
310
309
|
static bool single_threaded = false;
|
311
310
|
|
312
311
|
static void mark_context(void *);
|
@@ -335,10 +334,7 @@ static const rb_data_type_t isolate_type = {
|
|
335
334
|
static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
336
335
|
bool platform_already_initialized = false;
|
337
336
|
|
338
|
-
|
339
|
-
rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE" (should be a string)",
|
340
|
-
rb_obj_class(flag_as_str));
|
341
|
-
}
|
337
|
+
Check_Type(flag_as_str, T_STRING);
|
342
338
|
|
343
339
|
platform_lock.lock();
|
344
340
|
|
@@ -664,11 +660,7 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
664
660
|
v8::String::Utf8Value symbol_name(isolate,
|
665
661
|
Local<Symbol>::Cast(value)->Name());
|
666
662
|
|
667
|
-
VALUE str_symbol =
|
668
|
-
*symbol_name,
|
669
|
-
symbol_name.length(),
|
670
|
-
rb_enc_find("utf-8")
|
671
|
-
);
|
663
|
+
VALUE str_symbol = rb_utf8_str_new(*symbol_name, symbol_name.length());
|
672
664
|
|
673
665
|
return rb_str_intern(str_symbol);
|
674
666
|
}
|
@@ -679,7 +671,7 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
679
671
|
return Qnil;
|
680
672
|
} else {
|
681
673
|
Local<String> rstr = rstr_maybe.ToLocalChecked();
|
682
|
-
return
|
674
|
+
return rb_utf8_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate));
|
683
675
|
}
|
684
676
|
}
|
685
677
|
|
@@ -861,7 +853,7 @@ StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
|
|
861
853
|
return result;
|
862
854
|
}
|
863
855
|
|
864
|
-
static VALUE rb_snapshot_size(VALUE self
|
856
|
+
static VALUE rb_snapshot_size(VALUE self) {
|
865
857
|
SnapshotInfo* snapshot_info;
|
866
858
|
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
867
859
|
|
@@ -872,10 +864,7 @@ static VALUE rb_snapshot_load(VALUE self, VALUE str) {
|
|
872
864
|
SnapshotInfo* snapshot_info;
|
873
865
|
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
874
866
|
|
875
|
-
|
876
|
-
rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
|
877
|
-
rb_obj_class(str));
|
878
|
-
}
|
867
|
+
Check_Type(str, T_STRING);
|
879
868
|
|
880
869
|
init_v8();
|
881
870
|
|
@@ -891,7 +880,7 @@ static VALUE rb_snapshot_load(VALUE self, VALUE str) {
|
|
891
880
|
return Qnil;
|
892
881
|
}
|
893
882
|
|
894
|
-
static VALUE rb_snapshot_dump(VALUE self
|
883
|
+
static VALUE rb_snapshot_dump(VALUE self) {
|
895
884
|
SnapshotInfo* snapshot_info;
|
896
885
|
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
897
886
|
|
@@ -902,10 +891,7 @@ static VALUE rb_snapshot_warmup_unsafe(VALUE self, VALUE str) {
|
|
902
891
|
SnapshotInfo* snapshot_info;
|
903
892
|
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
904
893
|
|
905
|
-
|
906
|
-
rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
|
907
|
-
rb_obj_class(str));
|
908
|
-
}
|
894
|
+
Check_Type(str, T_STRING);
|
909
895
|
|
910
896
|
init_v8();
|
911
897
|
|
@@ -1084,8 +1070,7 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
1084
1070
|
// If we were terminated or have the memory softlimit flag set
|
1085
1071
|
if (marshal_stack_maxdepth_reached) {
|
1086
1072
|
ruby_exception = rb_eScriptRuntimeError;
|
1087
|
-
|
1088
|
-
message = rb_enc_str_new(msg.c_str(), msg.length(), rb_enc_find("utf-8"));
|
1073
|
+
message = rb_utf8_str_new_literal("Marshal object depth too deep. Script terminated.");
|
1089
1074
|
} else if (result.terminated || mem_softlimit_reached) {
|
1090
1075
|
ruby_exception = mem_softlimit_reached ? rb_eV8OutOfMemoryError : rb_eScriptTerminatedError;
|
1091
1076
|
} else {
|
@@ -1120,7 +1105,7 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
1120
1105
|
|
1121
1106
|
if (result.json) {
|
1122
1107
|
Local<String> rstr = tmp->ToString(p_ctx->Get(isolate)).ToLocalChecked();
|
1123
|
-
VALUE json_string =
|
1108
|
+
VALUE json_string = rb_utf8_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate));
|
1124
1109
|
ret = rb_funcall(rb_mJSON, rb_intern("parse"), 1, json_string);
|
1125
1110
|
} else {
|
1126
1111
|
StackCounter::Reset(isolate);
|
@@ -1149,13 +1134,10 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
|
|
1149
1134
|
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1150
1135
|
Isolate* isolate = context_info->isolate_info->isolate;
|
1151
1136
|
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1156
|
-
if(filename != Qnil && TYPE(filename) != T_STRING) {
|
1157
|
-
rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be nil or a string)",
|
1158
|
-
rb_obj_class(filename));
|
1137
|
+
Check_Type(str, T_STRING);
|
1138
|
+
|
1139
|
+
if (!NIL_P(filename)) {
|
1140
|
+
Check_Type(filename, T_STRING);
|
1159
1141
|
}
|
1160
1142
|
|
1161
1143
|
{
|
@@ -1474,42 +1456,33 @@ static void free_context_raw(void *arg) {
|
|
1474
1456
|
if (isolate_info) {
|
1475
1457
|
isolate_info->release();
|
1476
1458
|
}
|
1477
|
-
|
1478
|
-
xfree(context_info);
|
1479
1459
|
}
|
1480
1460
|
|
1481
1461
|
static void *free_context_thr(void* arg) {
|
1482
|
-
if (
|
1483
|
-
|
1462
|
+
if (ruby_exiting.load() == 0) {
|
1463
|
+
free_context_raw(arg);
|
1464
|
+
xfree(arg);
|
1484
1465
|
}
|
1485
|
-
if (ruby_exiting) {
|
1486
|
-
return NULL;
|
1487
|
-
}
|
1488
|
-
|
1489
|
-
free_context_raw(arg);
|
1490
|
-
|
1491
|
-
pthread_rwlock_unlock(&exit_lock);
|
1492
|
-
|
1493
1466
|
return NULL;
|
1494
1467
|
}
|
1495
1468
|
|
1496
1469
|
// destroys everything except freeing the ContextInfo struct (see deallocate())
|
1497
1470
|
static void free_context(ContextInfo* context_info) {
|
1498
|
-
|
1499
1471
|
IsolateInfo* isolate_info = context_info->isolate_info;
|
1500
1472
|
|
1501
|
-
ContextInfo* context_info_copy = ALLOC(ContextInfo);
|
1502
|
-
context_info_copy->isolate_info = context_info->isolate_info;
|
1503
|
-
context_info_copy->context = context_info->context;
|
1504
|
-
|
1505
1473
|
if (isolate_info && isolate_info->refs() > 1) {
|
1506
1474
|
pthread_t free_context_thread;
|
1475
|
+
ContextInfo* context_info_copy = ALLOC(ContextInfo);
|
1476
|
+
|
1477
|
+
context_info_copy->isolate_info = context_info->isolate_info;
|
1478
|
+
context_info_copy->context = context_info->context;
|
1507
1479
|
if (pthread_create(&free_context_thread, thread_attr_p,
|
1508
1480
|
free_context_thr, (void*)context_info_copy)) {
|
1509
1481
|
fprintf(stderr, "WARNING failed to release memory in MiniRacer, thread to release could not be created, process will leak memory\n");
|
1482
|
+
xfree(context_info_copy);
|
1510
1483
|
}
|
1511
1484
|
} else {
|
1512
|
-
free_context_raw(
|
1485
|
+
free_context_raw(context_info);
|
1513
1486
|
}
|
1514
1487
|
|
1515
1488
|
context_info->context = NULL;
|
@@ -1782,9 +1755,7 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1782
1755
|
}
|
1783
1756
|
|
1784
1757
|
VALUE function_name = argv[0];
|
1785
|
-
|
1786
|
-
rb_raise(rb_eTypeError, "first argument should be a String");
|
1787
|
-
}
|
1758
|
+
Check_Type(function_name, T_STRING);
|
1788
1759
|
|
1789
1760
|
char *fname = RSTRING_PTR(function_name);
|
1790
1761
|
if (!fname) {
|
@@ -1870,12 +1841,7 @@ static VALUE rb_context_create_isolate_value(VALUE self) {
|
|
1870
1841
|
static void set_ruby_exiting(VALUE value) {
|
1871
1842
|
(void)value;
|
1872
1843
|
|
1873
|
-
|
1874
|
-
|
1875
|
-
ruby_exiting = true;
|
1876
|
-
if (res == 0) {
|
1877
|
-
pthread_rwlock_unlock(&exit_lock);
|
1878
|
-
}
|
1844
|
+
ruby_exiting.store(1);
|
1879
1845
|
}
|
1880
1846
|
|
1881
1847
|
extern "C" {
|
@@ -1941,9 +1907,5 @@ extern "C" {
|
|
1941
1907
|
thread_attr_p = &attr;
|
1942
1908
|
}
|
1943
1909
|
}
|
1944
|
-
auto on_fork_for_child = []() {
|
1945
|
-
exit_lock = PTHREAD_RWLOCK_INITIALIZER;
|
1946
|
-
};
|
1947
|
-
pthread_atfork(nullptr, nullptr, on_fork_for_child);
|
1948
1910
|
}
|
1949
1911
|
}
|
@@ -0,0 +1,353 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MiniRacer
|
4
|
+
|
5
|
+
class Context
|
6
|
+
|
7
|
+
class ExternalFunction
|
8
|
+
private
|
9
|
+
|
10
|
+
def notify_v8
|
11
|
+
name = @name.encode(::Encoding::UTF_8)
|
12
|
+
wrapped = lambda do |*args|
|
13
|
+
converted = @parent.send(:convert_js_to_ruby, args)
|
14
|
+
begin
|
15
|
+
result = @callback.call(*converted)
|
16
|
+
rescue Polyglot::ForeignException => e
|
17
|
+
e = RuntimeError.new(e.message)
|
18
|
+
e.set_backtrace(e.backtrace)
|
19
|
+
@parent.instance_variable_set(:@current_exception, e)
|
20
|
+
raise e
|
21
|
+
rescue => e
|
22
|
+
@parent.instance_variable_set(:@current_exception, e)
|
23
|
+
raise e
|
24
|
+
end
|
25
|
+
@parent.send(:convert_ruby_to_js, result)
|
26
|
+
end
|
27
|
+
|
28
|
+
if @parent_object.nil?
|
29
|
+
# set global name to proc
|
30
|
+
result = @parent.eval_in_context('this')
|
31
|
+
result[name] = wrapped
|
32
|
+
else
|
33
|
+
parent_object_eval = @parent_object_eval.encode(::Encoding::UTF_8)
|
34
|
+
begin
|
35
|
+
result = @parent.eval_in_context(parent_object_eval)
|
36
|
+
rescue Polyglot::ForeignException, StandardError => e
|
37
|
+
raise ParseError, "Was expecting #{@parent_object} to be an object", e.backtrace
|
38
|
+
end
|
39
|
+
result[name] = wrapped
|
40
|
+
# set evaluated object results name to proc
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def heap_stats
|
46
|
+
{
|
47
|
+
total_physical_size: 0,
|
48
|
+
total_heap_size_executable: 0,
|
49
|
+
total_heap_size: 0,
|
50
|
+
used_heap_size: 0,
|
51
|
+
heap_size_limit: 0,
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
def stop
|
56
|
+
if @entered
|
57
|
+
@context.stop
|
58
|
+
@stopped = true
|
59
|
+
stop_attached
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
@context_initialized = false
|
66
|
+
@use_strict = false
|
67
|
+
|
68
|
+
def init_unsafe(isolate, snapshot)
|
69
|
+
unless defined?(Polyglot::InnerContext)
|
70
|
+
raise "TruffleRuby #{RUBY_ENGINE_VERSION} does not have support for inner contexts, use a more recent version"
|
71
|
+
end
|
72
|
+
|
73
|
+
unless Polyglot.languages.include? "js"
|
74
|
+
warn "You also need to install the 'js' component with 'gu install js' on GraalVM 22.2+", uplevel: 0 if $VERBOSE
|
75
|
+
end
|
76
|
+
|
77
|
+
@context = Polyglot::InnerContext.new(on_cancelled: -> {
|
78
|
+
raise ScriptTerminatedError, 'JavaScript was terminated (either by timeout or explicitly)'
|
79
|
+
})
|
80
|
+
Context.instance_variable_set(:@context_initialized, true)
|
81
|
+
@js_object = @context.eval('js', 'Object')
|
82
|
+
@isolate_mutex = Mutex.new
|
83
|
+
@stopped = false
|
84
|
+
@entered = false
|
85
|
+
@has_entered = false
|
86
|
+
@current_exception = nil
|
87
|
+
if isolate && snapshot
|
88
|
+
isolate.instance_variable_set(:@snapshot, snapshot)
|
89
|
+
end
|
90
|
+
if snapshot
|
91
|
+
@snapshot = snapshot
|
92
|
+
elsif isolate
|
93
|
+
@snapshot = isolate.instance_variable_get(:@snapshot)
|
94
|
+
else
|
95
|
+
@snapshot = nil
|
96
|
+
end
|
97
|
+
@is_object_or_array_func, @is_time_func, @js_date_to_time_func, @is_symbol_func, @js_symbol_to_symbol_func, @js_new_date_func, @js_new_array_func = eval_in_context <<-CODE
|
98
|
+
[
|
99
|
+
(x) => { return (x instanceof Object || x instanceof Array) && !(x instanceof Date) && !(x instanceof Function) },
|
100
|
+
(x) => { return x instanceof Date },
|
101
|
+
(x) => { return x.getTime(x) },
|
102
|
+
(x) => { return typeof x === 'symbol' },
|
103
|
+
(x) => { var r = x.description; return r === undefined ? 'undefined' : r },
|
104
|
+
(x) => { return new Date(x) },
|
105
|
+
(x) => { return new Array(x) },
|
106
|
+
]
|
107
|
+
CODE
|
108
|
+
end
|
109
|
+
|
110
|
+
def dispose_unsafe
|
111
|
+
@context.close
|
112
|
+
end
|
113
|
+
|
114
|
+
def eval_unsafe(str, filename)
|
115
|
+
@entered = true
|
116
|
+
if !@has_entered && @snapshot
|
117
|
+
snapshot_src = encode(@snapshot.instance_variable_get(:@source))
|
118
|
+
begin
|
119
|
+
eval_in_context(snapshot_src, filename)
|
120
|
+
rescue Polyglot::ForeignException => e
|
121
|
+
raise RuntimeError, e.message, e.backtrace
|
122
|
+
end
|
123
|
+
end
|
124
|
+
@has_entered = true
|
125
|
+
raise RuntimeError, "TruffleRuby does not support eval after stop" if @stopped
|
126
|
+
raise TypeError, "wrong type argument #{str.class} (should be a string)" unless str.is_a?(String)
|
127
|
+
raise TypeError, "wrong type argument #{filename.class} (should be a string)" unless filename.nil? || filename.is_a?(String)
|
128
|
+
|
129
|
+
str = encode(str)
|
130
|
+
begin
|
131
|
+
translate do
|
132
|
+
eval_in_context(str, filename)
|
133
|
+
end
|
134
|
+
rescue Polyglot::ForeignException => e
|
135
|
+
raise RuntimeError, e.message, e.backtrace
|
136
|
+
rescue ::RuntimeError => e
|
137
|
+
if @current_exception
|
138
|
+
e = @current_exception
|
139
|
+
@current_exception = nil
|
140
|
+
raise e
|
141
|
+
else
|
142
|
+
raise e, e.message
|
143
|
+
end
|
144
|
+
end
|
145
|
+
ensure
|
146
|
+
@entered = false
|
147
|
+
end
|
148
|
+
|
149
|
+
def call_unsafe(function_name, *arguments)
|
150
|
+
@entered = true
|
151
|
+
if !@has_entered && @snapshot
|
152
|
+
src = encode(@snapshot.instance_variable_get(:source))
|
153
|
+
begin
|
154
|
+
eval_in_context(src)
|
155
|
+
rescue Polyglot::ForeignException => e
|
156
|
+
raise RuntimeError, e.message, e.backtrace
|
157
|
+
end
|
158
|
+
end
|
159
|
+
@has_entered = true
|
160
|
+
raise RuntimeError, "TruffleRuby does not support call after stop" if @stopped
|
161
|
+
begin
|
162
|
+
translate do
|
163
|
+
function = eval_in_context(function_name)
|
164
|
+
function.call(*convert_ruby_to_js(arguments))
|
165
|
+
end
|
166
|
+
rescue Polyglot::ForeignException => e
|
167
|
+
raise RuntimeError, e.message, e.backtrace
|
168
|
+
end
|
169
|
+
ensure
|
170
|
+
@entered = false
|
171
|
+
end
|
172
|
+
|
173
|
+
def create_isolate_value
|
174
|
+
# Returning a dummy object since TruffleRuby does not have a 1-1 concept with isolate.
|
175
|
+
# However, code and ASTs are shared between contexts.
|
176
|
+
Isolate.new
|
177
|
+
end
|
178
|
+
|
179
|
+
def isolate_mutex
|
180
|
+
@isolate_mutex
|
181
|
+
end
|
182
|
+
|
183
|
+
def translate
|
184
|
+
convert_js_to_ruby yield
|
185
|
+
rescue Object => e
|
186
|
+
message = e.message
|
187
|
+
if @current_exception
|
188
|
+
raise @current_exception
|
189
|
+
elsif e.message && e.message.start_with?('SyntaxError:')
|
190
|
+
error_class = MiniRacer::ParseError
|
191
|
+
elsif e.is_a?(MiniRacer::ScriptTerminatedError)
|
192
|
+
error_class = MiniRacer::ScriptTerminatedError
|
193
|
+
else
|
194
|
+
error_class = MiniRacer::RuntimeError
|
195
|
+
end
|
196
|
+
|
197
|
+
if error_class == MiniRacer::RuntimeError
|
198
|
+
bls = e.backtrace_locations&.select { |bl| bl&.source_location&.language == 'js' }
|
199
|
+
if bls && !bls.empty?
|
200
|
+
if '(eval)' != bls[0].path
|
201
|
+
message = "#{e.message}\n at #{bls[0]}\n" + bls[1..].map(&:to_s).join("\n")
|
202
|
+
else
|
203
|
+
message = "#{e.message}\n" + bls.map(&:to_s).join("\n")
|
204
|
+
end
|
205
|
+
end
|
206
|
+
raise error_class, message
|
207
|
+
else
|
208
|
+
raise error_class, message, e.backtrace
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def convert_js_to_ruby(value)
|
213
|
+
case value
|
214
|
+
when true, false, Integer, Float
|
215
|
+
value
|
216
|
+
else
|
217
|
+
if value.nil?
|
218
|
+
nil
|
219
|
+
elsif value.respond_to?(:call)
|
220
|
+
MiniRacer::JavaScriptFunction.new
|
221
|
+
elsif value.respond_to?(:to_str)
|
222
|
+
value.to_str.dup
|
223
|
+
elsif value.respond_to?(:to_ary)
|
224
|
+
value.to_ary.map do |e|
|
225
|
+
if e.respond_to?(:call)
|
226
|
+
nil
|
227
|
+
else
|
228
|
+
convert_js_to_ruby(e)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
elsif time?(value)
|
232
|
+
js_date_to_time(value)
|
233
|
+
elsif symbol?(value)
|
234
|
+
js_symbol_to_symbol(value)
|
235
|
+
else
|
236
|
+
object = value
|
237
|
+
h = {}
|
238
|
+
object.instance_variables.each do |member|
|
239
|
+
v = object[member]
|
240
|
+
unless v.respond_to?(:call)
|
241
|
+
h[member.to_s] = convert_js_to_ruby(v)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
h
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
def object_or_array?(val)
|
250
|
+
@is_object_or_array_func.call(val)
|
251
|
+
end
|
252
|
+
|
253
|
+
def time?(value)
|
254
|
+
@is_time_func.call(value)
|
255
|
+
end
|
256
|
+
|
257
|
+
def js_date_to_time(value)
|
258
|
+
millis = @js_date_to_time_func.call(value)
|
259
|
+
Time.at(Rational(millis, 1000))
|
260
|
+
end
|
261
|
+
|
262
|
+
def symbol?(value)
|
263
|
+
@is_symbol_func.call(value)
|
264
|
+
end
|
265
|
+
|
266
|
+
def js_symbol_to_symbol(value)
|
267
|
+
@js_symbol_to_symbol_func.call(value).to_s.to_sym
|
268
|
+
end
|
269
|
+
|
270
|
+
def js_new_date(value)
|
271
|
+
@js_new_date_func.call(value)
|
272
|
+
end
|
273
|
+
|
274
|
+
def js_new_array(size)
|
275
|
+
@js_new_array_func.call(size)
|
276
|
+
end
|
277
|
+
|
278
|
+
def convert_ruby_to_js(value)
|
279
|
+
case value
|
280
|
+
when nil, true, false, Integer, Float
|
281
|
+
value
|
282
|
+
when Array
|
283
|
+
ary = js_new_array(value.size)
|
284
|
+
value.each_with_index do |v, i|
|
285
|
+
ary[i] = convert_ruby_to_js(v)
|
286
|
+
end
|
287
|
+
ary
|
288
|
+
when Hash
|
289
|
+
h = @js_object.new
|
290
|
+
value.each_pair do |k, v|
|
291
|
+
h[convert_ruby_to_js(k.to_s)] = convert_ruby_to_js(v)
|
292
|
+
end
|
293
|
+
h
|
294
|
+
when String, Symbol
|
295
|
+
Truffle::Interop.as_truffle_string value
|
296
|
+
when Time
|
297
|
+
js_new_date(value.to_f * 1000)
|
298
|
+
when DateTime
|
299
|
+
js_new_date(value.to_time.to_f * 1000)
|
300
|
+
else
|
301
|
+
"Undefined Conversion"
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
def encode(string)
|
306
|
+
raise ArgumentError unless string
|
307
|
+
string.encode(::Encoding::UTF_8)
|
308
|
+
end
|
309
|
+
|
310
|
+
class_eval <<-'RUBY', "(mini_racer)", 1
|
311
|
+
def eval_in_context(code, file = nil); code = ('"use strict";' + code) if Context.instance_variable_get(:@use_strict); @context.eval('js', code, file || '(mini_racer)'); end
|
312
|
+
RUBY
|
313
|
+
|
314
|
+
end
|
315
|
+
|
316
|
+
class Isolate
|
317
|
+
def init_with_snapshot(snapshot)
|
318
|
+
# TruffleRuby does not have a 1-1 concept with isolate.
|
319
|
+
# However, isolate can hold a snapshot, and code and ASTs are shared between contexts.
|
320
|
+
@snapshot = snapshot
|
321
|
+
end
|
322
|
+
|
323
|
+
def low_memory_notification
|
324
|
+
GC.start
|
325
|
+
end
|
326
|
+
|
327
|
+
def idle_notification(idle_time)
|
328
|
+
true
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
class Platform
|
333
|
+
def self.set_flag_as_str!(flag)
|
334
|
+
raise TypeError, "wrong type argument #{flag.class} (should be a string)" unless flag.is_a?(String)
|
335
|
+
raise MiniRacer::PlatformAlreadyInitialized, "The platform is already initialized." if Context.instance_variable_get(:@context_initialized)
|
336
|
+
Context.instance_variable_set(:@use_strict, true) if "--use_strict" == flag
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
class Snapshot
|
341
|
+
def load(str)
|
342
|
+
raise TypeError, "wrong type argument #{str.class} (should be a string)" unless str.is_a?(String)
|
343
|
+
# Intentionally noop since TruffleRuby mocks the snapshot API
|
344
|
+
end
|
345
|
+
|
346
|
+
def warmup_unsafe!(src)
|
347
|
+
raise TypeError, "wrong type argument #{src.class} (should be a string)" unless src.is_a?(String)
|
348
|
+
# Intentionally noop since TruffleRuby mocks the snapshot API
|
349
|
+
# by replaying snapshot source before the first eval/call
|
350
|
+
self
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
data/lib/mini_racer/version.rb
CHANGED
data/lib/mini_racer.rb
CHANGED
@@ -1,17 +1,22 @@
|
|
1
1
|
require "mini_racer/version"
|
2
|
-
require "mini_racer_loader"
|
3
2
|
require "pathname"
|
4
3
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
4
|
+
if RUBY_ENGINE == "truffleruby"
|
5
|
+
require "mini_racer/truffleruby"
|
6
|
+
else
|
7
|
+
require "mini_racer_loader"
|
8
|
+
ext_filename = "mini_racer_extension.#{RbConfig::CONFIG['DLEXT']}"
|
9
|
+
ext_path = Gem.loaded_specs['mini_racer'].require_paths
|
10
|
+
.map { |p| (p = Pathname.new(p)).absolute? ? p : Pathname.new(__dir__).parent + p }
|
11
|
+
ext_found = ext_path.map { |p| p + ext_filename }.find { |p| p.file? }
|
12
|
+
|
13
|
+
raise LoadError, "Could not find #{ext_filename} in #{ext_path.map(&:to_s)}" unless ext_found
|
14
|
+
MiniRacer::Loader.load(ext_found.to_s)
|
15
|
+
end
|
12
16
|
|
13
17
|
require "thread"
|
14
18
|
require "json"
|
19
|
+
require "io/wait"
|
15
20
|
|
16
21
|
module MiniRacer
|
17
22
|
|
@@ -202,7 +207,7 @@ module MiniRacer
|
|
202
207
|
end
|
203
208
|
|
204
209
|
if !(File === f)
|
205
|
-
raise ArgumentError
|
210
|
+
raise ArgumentError, "file_or_io"
|
206
211
|
end
|
207
212
|
|
208
213
|
write_heap_snapshot_unsafe(f)
|
@@ -349,7 +354,7 @@ module MiniRacer
|
|
349
354
|
|
350
355
|
t = Thread.new do
|
351
356
|
begin
|
352
|
-
result =
|
357
|
+
result = rp.wait_readable(@timeout/1000.0)
|
353
358
|
if !result
|
354
359
|
mutex.synchronize do
|
355
360
|
stop unless done
|
@@ -366,7 +371,7 @@ module MiniRacer
|
|
366
371
|
done = true
|
367
372
|
end
|
368
373
|
|
369
|
-
wp.
|
374
|
+
wp.close
|
370
375
|
|
371
376
|
# ensure we do not leak a thread in state
|
372
377
|
t.join
|
@@ -375,12 +380,9 @@ module MiniRacer
|
|
375
380
|
rval
|
376
381
|
ensure
|
377
382
|
# exceptions need to be handled
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
end
|
382
|
-
wp.close if wp
|
383
|
-
rp.close if rp
|
383
|
+
wp&.close
|
384
|
+
t&.join
|
385
|
+
rp&.close
|
384
386
|
end
|
385
387
|
|
386
388
|
def check_init_options!(isolate:, snapshot:, max_memory:, marshal_stack_depth:, ensure_gc_after_idle:, timeout:)
|
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.6.
|
4
|
+
version: 0.6.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Saffron
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-08-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -106,7 +106,6 @@ files:
|
|
106
106
|
- ".dockerignore"
|
107
107
|
- ".github/workflows/ci.yml"
|
108
108
|
- ".gitignore"
|
109
|
-
- ".travis.yml"
|
110
109
|
- CHANGELOG
|
111
110
|
- CODE_OF_CONDUCT.md
|
112
111
|
- Dockerfile
|
@@ -121,6 +120,7 @@ files:
|
|
121
120
|
- ext/mini_racer_loader/extconf.rb
|
122
121
|
- ext/mini_racer_loader/mini_racer_loader.c
|
123
122
|
- lib/mini_racer.rb
|
123
|
+
- lib/mini_racer/truffleruby.rb
|
124
124
|
- lib/mini_racer/version.rb
|
125
125
|
- mini_racer.gemspec
|
126
126
|
homepage: https://github.com/discourse/mini_racer
|
@@ -128,9 +128,9 @@ licenses:
|
|
128
128
|
- MIT
|
129
129
|
metadata:
|
130
130
|
bug_tracker_uri: https://github.com/discourse/mini_racer/issues
|
131
|
-
changelog_uri: https://github.com/discourse/mini_racer/blob/v0.6.
|
132
|
-
documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.6.
|
133
|
-
source_code_uri: https://github.com/discourse/mini_racer/tree/v0.6.
|
131
|
+
changelog_uri: https://github.com/discourse/mini_racer/blob/v0.6.3/CHANGELOG
|
132
|
+
documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.6.3
|
133
|
+
source_code_uri: https://github.com/discourse/mini_racer/tree/v0.6.3
|
134
134
|
post_install_message:
|
135
135
|
rdoc_options: []
|
136
136
|
require_paths:
|
@@ -147,7 +147,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
147
147
|
- !ruby/object:Gem::Version
|
148
148
|
version: '0'
|
149
149
|
requirements: []
|
150
|
-
rubygems_version: 3.
|
150
|
+
rubygems_version: 3.3.20
|
151
151
|
signing_key:
|
152
152
|
specification_version: 4
|
153
153
|
summary: Minimal embedded v8 for Ruby
|
data/.travis.yml
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
language: ruby
|
2
|
-
os: linux
|
3
|
-
rvm:
|
4
|
-
- 2.6
|
5
|
-
- 2.7
|
6
|
-
- 3.0
|
7
|
-
- ruby-head
|
8
|
-
arch:
|
9
|
-
- amd64
|
10
|
-
- arm64
|
11
|
-
jobs:
|
12
|
-
include:
|
13
|
-
- rvm: 2.6
|
14
|
-
os: osx
|
15
|
-
osx_image: xcode11.3
|
16
|
-
- rvm: 2.6
|
17
|
-
os: osx
|
18
|
-
osx_image: xcode12.2
|
19
|
-
- rvm: 2.7
|
20
|
-
os: osx
|
21
|
-
osx_image: xcode12.2
|
22
|
-
dist: xenial
|
23
|
-
cache: bundler
|