mini_racer 0.6.0 → 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 +25 -1
- data/README.md +18 -16
- data/Rakefile +15 -3
- data/ext/mini_racer_extension/extconf.rb +14 -0
- data/ext/mini_racer_extension/mini_racer_extension.cc +81 -108
- 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,5 +1,30 @@
|
|
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
|
12
|
+
|
13
|
+
- 0.6.2
|
14
|
+
|
15
|
+
- Fix support for compilation on 2.6, llvm compiles
|
16
|
+
- Stability patches to handle rare memory leaks
|
17
|
+
- Corrected re-raising of exceptions to support strings
|
18
|
+
- During early termination of context under certain conditions MiniRacer could crash
|
19
|
+
|
20
|
+
|
1
21
|
- 31-12-2021
|
2
22
|
|
23
|
+
- 0.6.1
|
24
|
+
|
25
|
+
- Added support for single threaded platform: `MiniRacer::Platform.set_flags! :single_threaded`
|
26
|
+
must be called prior to booting ANY MiniRacer::Context
|
27
|
+
|
3
28
|
- 0.6.0
|
4
29
|
|
5
30
|
- Ruby 3.1 support
|
@@ -13,7 +38,6 @@
|
|
13
38
|
- Fixes issues on aarch (Apple M1)
|
14
39
|
- Update to use libv8-node 16.x (#210) [Loic Nageleisen]
|
15
40
|
- FEATURE: Configurable max marshal stack depth (#202) [seanmakesgames]
|
16
|
-
- FEATURE: Configurable max marshal stack depth (#202) [seanmakesgames]
|
17
41
|
- Ruby 2.3 and 2.4 are EOL, we no longer support them
|
18
42
|
|
19
43
|
- 0.4.0
|
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,9 +76,17 @@ 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 ")
|
76
85
|
end
|
77
86
|
|
87
|
+
# there doesn't seem to be a CPP macro for this in Ruby 2.6:
|
88
|
+
if RUBY_ENGINE == 'ruby'
|
89
|
+
$CPPFLAGS += ' -DENGINE_IS_CRUBY '
|
90
|
+
end
|
91
|
+
|
78
92
|
create_makefile 'mini_racer_extension'
|
@@ -2,6 +2,7 @@
|
|
2
2
|
#include <ruby.h>
|
3
3
|
#include <ruby/thread.h>
|
4
4
|
#include <ruby/io.h>
|
5
|
+
#include <ruby/version.h>
|
5
6
|
#include <v8.h>
|
6
7
|
#include <v8-profiler.h>
|
7
8
|
#include <libplatform/libplatform.h>
|
@@ -13,6 +14,14 @@
|
|
13
14
|
#include <math.h>
|
14
15
|
#include <errno.h>
|
15
16
|
|
17
|
+
/* workaround C Ruby <= 2.x problems w/ clang in C++ mode */
|
18
|
+
#if defined(ENGINE_IS_CRUBY) && \
|
19
|
+
RUBY_API_VERSION_MAJOR == 2 && RUBY_API_VERSION_MINOR <= 6
|
20
|
+
# define MR_METHOD_FUNC(fn) RUBY_METHOD_FUNC(fn)
|
21
|
+
#else
|
22
|
+
# define MR_METHOD_FUNC(fn) fn
|
23
|
+
#endif
|
24
|
+
|
16
25
|
using namespace v8;
|
17
26
|
|
18
27
|
typedef struct {
|
@@ -296,8 +305,7 @@ static std::unique_ptr<Platform> current_platform = NULL;
|
|
296
305
|
static std::mutex platform_lock;
|
297
306
|
|
298
307
|
static pthread_attr_t *thread_attr_p;
|
299
|
-
static
|
300
|
-
static bool ruby_exiting = false; // guarded by exit_lock
|
308
|
+
static std::atomic_int ruby_exiting(0);
|
301
309
|
static bool single_threaded = false;
|
302
310
|
|
303
311
|
static void mark_context(void *);
|
@@ -326,10 +334,7 @@ static const rb_data_type_t isolate_type = {
|
|
326
334
|
static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
327
335
|
bool platform_already_initialized = false;
|
328
336
|
|
329
|
-
|
330
|
-
rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE" (should be a string)",
|
331
|
-
rb_obj_class(flag_as_str));
|
332
|
-
}
|
337
|
+
Check_Type(flag_as_str, T_STRING);
|
333
338
|
|
334
339
|
platform_lock.lock();
|
335
340
|
|
@@ -337,7 +342,7 @@ static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
|
337
342
|
if (!strcmp(RSTRING_PTR(flag_as_str), "--single_threaded")) {
|
338
343
|
single_threaded = true;
|
339
344
|
}
|
340
|
-
V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), (
|
345
|
+
V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), RSTRING_LENINT(flag_as_str));
|
341
346
|
} else {
|
342
347
|
platform_already_initialized = true;
|
343
348
|
}
|
@@ -360,7 +365,11 @@ static void init_v8() {
|
|
360
365
|
|
361
366
|
if (current_platform == NULL) {
|
362
367
|
V8::InitializeICU();
|
363
|
-
|
368
|
+
if (single_threaded) {
|
369
|
+
current_platform = platform::NewSingleThreadedDefaultPlatform();
|
370
|
+
} else {
|
371
|
+
current_platform = platform::NewDefaultPlatform();
|
372
|
+
}
|
364
373
|
V8::InitializePlatform(current_platform.get());
|
365
374
|
V8::Initialize();
|
366
375
|
}
|
@@ -405,28 +414,32 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
405
414
|
Local<Value> local_value = v8res.ToLocalChecked();
|
406
415
|
if ((local_value->IsObject() || local_value->IsArray()) &&
|
407
416
|
!local_value->IsDate() && !local_value->IsFunction()) {
|
408
|
-
|
409
|
-
context, String::NewFromUtf8Literal(isolate, "JSON"))
|
410
|
-
.ToLocalChecked().As<Object>();
|
411
|
-
|
412
|
-
Local<Function> stringify = JSON->Get(
|
413
|
-
context, v8::String::NewFromUtf8Literal(isolate, "stringify"))
|
414
|
-
.ToLocalChecked().As<Function>();
|
417
|
+
MaybeLocal<v8::Value> ml = context->Global()->Get(
|
418
|
+
context, String::NewFromUtf8Literal(isolate, "JSON"));
|
415
419
|
|
416
|
-
|
417
|
-
const unsigned argc = 1;
|
418
|
-
Local<Value> argv[argc] = { object };
|
419
|
-
MaybeLocal<Value> json = stringify->Call(context, JSON, argc, argv);
|
420
|
-
|
421
|
-
if (json.IsEmpty()) {
|
420
|
+
if (ml.IsEmpty()) { // exception
|
422
421
|
evalRes.executed = false;
|
423
422
|
} else {
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
423
|
+
Local<Object> JSON = ml.ToLocalChecked().As<Object>();
|
424
|
+
|
425
|
+
Local<Function> stringify = JSON->Get(
|
426
|
+
context, v8::String::NewFromUtf8Literal(isolate, "stringify"))
|
427
|
+
.ToLocalChecked().As<Function>();
|
428
|
+
|
429
|
+
Local<Object> object = local_value->ToObject(context).ToLocalChecked();
|
430
|
+
const unsigned argc = 1;
|
431
|
+
Local<Value> argv[argc] = { object };
|
432
|
+
MaybeLocal<Value> json = stringify->Call(context, JSON, argc, argv);
|
433
|
+
|
434
|
+
if (json.IsEmpty()) {
|
435
|
+
evalRes.executed = false;
|
436
|
+
} else {
|
437
|
+
evalRes.json = true;
|
438
|
+
Persistent<Value>* persistent = new Persistent<Value>();
|
439
|
+
persistent->Reset(isolate, json.ToLocalChecked());
|
440
|
+
evalRes.value = persistent;
|
441
|
+
}
|
428
442
|
}
|
429
|
-
|
430
443
|
} else {
|
431
444
|
Persistent<Value>* persistent = new Persistent<Value>();
|
432
445
|
persistent->Reset(isolate, local_value);
|
@@ -647,11 +660,7 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
647
660
|
v8::String::Utf8Value symbol_name(isolate,
|
648
661
|
Local<Symbol>::Cast(value)->Name());
|
649
662
|
|
650
|
-
VALUE str_symbol =
|
651
|
-
*symbol_name,
|
652
|
-
symbol_name.length(),
|
653
|
-
rb_enc_find("utf-8")
|
654
|
-
);
|
663
|
+
VALUE str_symbol = rb_utf8_str_new(*symbol_name, symbol_name.length());
|
655
664
|
|
656
665
|
return rb_str_intern(str_symbol);
|
657
666
|
}
|
@@ -662,7 +671,7 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
662
671
|
return Qnil;
|
663
672
|
} else {
|
664
673
|
Local<String> rstr = rstr_maybe.ToLocalChecked();
|
665
|
-
return
|
674
|
+
return rb_utf8_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate));
|
666
675
|
}
|
667
676
|
}
|
668
677
|
|
@@ -707,7 +716,7 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
707
716
|
case T_FLOAT:
|
708
717
|
return scope.Escape(Number::New(isolate, NUM2DBL(value)));
|
709
718
|
case T_STRING:
|
710
|
-
return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, (
|
719
|
+
return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, RSTRING_LENINT(value)).ToLocalChecked());
|
711
720
|
case T_NIL:
|
712
721
|
return scope.Escape(Null(isolate));
|
713
722
|
case T_TRUE:
|
@@ -735,7 +744,7 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
735
744
|
return scope.Escape(object);
|
736
745
|
case T_SYMBOL:
|
737
746
|
value = rb_funcall(value, rb_intern("to_s"), 0);
|
738
|
-
return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, (
|
747
|
+
return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, RSTRING_LENINT(value)).ToLocalChecked());
|
739
748
|
case T_DATA:
|
740
749
|
klass = rb_funcall(value, rb_intern("class"), 0);
|
741
750
|
if (klass == rb_cTime || klass == rb_cDateTime)
|
@@ -844,7 +853,7 @@ StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
|
|
844
853
|
return result;
|
845
854
|
}
|
846
855
|
|
847
|
-
static VALUE rb_snapshot_size(VALUE self
|
856
|
+
static VALUE rb_snapshot_size(VALUE self) {
|
848
857
|
SnapshotInfo* snapshot_info;
|
849
858
|
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
850
859
|
|
@@ -855,10 +864,7 @@ static VALUE rb_snapshot_load(VALUE self, VALUE str) {
|
|
855
864
|
SnapshotInfo* snapshot_info;
|
856
865
|
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
857
866
|
|
858
|
-
|
859
|
-
rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
|
860
|
-
rb_obj_class(str));
|
861
|
-
}
|
867
|
+
Check_Type(str, T_STRING);
|
862
868
|
|
863
869
|
init_v8();
|
864
870
|
|
@@ -874,7 +880,7 @@ static VALUE rb_snapshot_load(VALUE self, VALUE str) {
|
|
874
880
|
return Qnil;
|
875
881
|
}
|
876
882
|
|
877
|
-
static VALUE rb_snapshot_dump(VALUE self
|
883
|
+
static VALUE rb_snapshot_dump(VALUE self) {
|
878
884
|
SnapshotInfo* snapshot_info;
|
879
885
|
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
880
886
|
|
@@ -885,10 +891,7 @@ static VALUE rb_snapshot_warmup_unsafe(VALUE self, VALUE str) {
|
|
885
891
|
SnapshotInfo* snapshot_info;
|
886
892
|
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
887
893
|
|
888
|
-
|
889
|
-
rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
|
890
|
-
rb_obj_class(str));
|
891
|
-
}
|
894
|
+
Check_Type(str, T_STRING);
|
892
895
|
|
893
896
|
init_v8();
|
894
897
|
|
@@ -1053,7 +1056,7 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
1053
1056
|
// a v8 scope, if we do the scope is never cleaned up properly and we leak
|
1054
1057
|
if (!result.parsed) {
|
1055
1058
|
if(TYPE(message) == T_STRING) {
|
1056
|
-
rb_raise(rb_eParseError, "%
|
1059
|
+
rb_raise(rb_eParseError, "%" PRIsVALUE, message);
|
1057
1060
|
} else {
|
1058
1061
|
rb_raise(rb_eParseError, "Unknown JavaScript Error during parse");
|
1059
1062
|
}
|
@@ -1067,8 +1070,7 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
1067
1070
|
// If we were terminated or have the memory softlimit flag set
|
1068
1071
|
if (marshal_stack_maxdepth_reached) {
|
1069
1072
|
ruby_exception = rb_eScriptRuntimeError;
|
1070
|
-
|
1071
|
-
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.");
|
1072
1074
|
} else if (result.terminated || mem_softlimit_reached) {
|
1073
1075
|
ruby_exception = mem_softlimit_reached ? rb_eV8OutOfMemoryError : rb_eScriptTerminatedError;
|
1074
1076
|
} else {
|
@@ -1077,15 +1079,17 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
1077
1079
|
|
1078
1080
|
// exception report about what happened
|
1079
1081
|
if (TYPE(backtrace) == T_STRING) {
|
1080
|
-
rb_raise(ruby_exception, "%
|
1082
|
+
rb_raise(ruby_exception, "%" PRIsVALUE, backtrace);
|
1081
1083
|
} else if(TYPE(message) == T_STRING) {
|
1082
|
-
rb_raise(ruby_exception, "%
|
1084
|
+
rb_raise(ruby_exception, "%" PRIsVALUE, message);
|
1083
1085
|
} else {
|
1084
1086
|
rb_raise(ruby_exception, "Unknown JavaScript Error during execution");
|
1085
1087
|
}
|
1088
|
+
} else if (rb_obj_is_kind_of(ruby_exception, rb_eException)) {
|
1089
|
+
rb_exc_raise(ruby_exception);
|
1086
1090
|
} else {
|
1087
1091
|
VALUE rb_str = rb_funcall(ruby_exception, rb_intern("to_s"), 0);
|
1088
|
-
rb_raise(CLASS_OF(ruby_exception), "%
|
1092
|
+
rb_raise(CLASS_OF(ruby_exception), "%" PRIsVALUE, rb_str);
|
1089
1093
|
}
|
1090
1094
|
}
|
1091
1095
|
|
@@ -1101,7 +1105,7 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
1101
1105
|
|
1102
1106
|
if (result.json) {
|
1103
1107
|
Local<String> rstr = tmp->ToString(p_ctx->Get(isolate)).ToLocalChecked();
|
1104
|
-
VALUE json_string =
|
1108
|
+
VALUE json_string = rb_utf8_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate));
|
1105
1109
|
ret = rb_funcall(rb_mJSON, rb_intern("parse"), 1, json_string);
|
1106
1110
|
} else {
|
1107
1111
|
StackCounter::Reset(isolate);
|
@@ -1130,13 +1134,10 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
|
|
1130
1134
|
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
1131
1135
|
Isolate* isolate = context_info->isolate_info->isolate;
|
1132
1136
|
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1137
|
-
if(filename != Qnil && TYPE(filename) != T_STRING) {
|
1138
|
-
rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be nil or a string)",
|
1139
|
-
rb_obj_class(filename));
|
1137
|
+
Check_Type(str, T_STRING);
|
1138
|
+
|
1139
|
+
if (!NIL_P(filename)) {
|
1140
|
+
Check_Type(filename, T_STRING);
|
1140
1141
|
}
|
1141
1142
|
|
1142
1143
|
{
|
@@ -1145,13 +1146,13 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
|
|
1145
1146
|
HandleScope handle_scope(isolate);
|
1146
1147
|
|
1147
1148
|
Local<String> eval = String::NewFromUtf8(isolate, RSTRING_PTR(str),
|
1148
|
-
NewStringType::kNormal, (
|
1149
|
+
NewStringType::kNormal, RSTRING_LENINT(str)).ToLocalChecked();
|
1149
1150
|
|
1150
1151
|
Local<String> local_filename;
|
1151
1152
|
|
1152
1153
|
if (filename != Qnil) {
|
1153
1154
|
local_filename = String::NewFromUtf8(isolate, RSTRING_PTR(filename),
|
1154
|
-
NewStringType::kNormal, (
|
1155
|
+
NewStringType::kNormal, RSTRING_LENINT(filename)).ToLocalChecked();
|
1155
1156
|
eval_params.filename = &local_filename;
|
1156
1157
|
} else {
|
1157
1158
|
eval_params.filename = NULL;
|
@@ -1275,8 +1276,8 @@ gvl_ruby_callback(void* data) {
|
|
1275
1276
|
VALUE callback_data_value = (VALUE)&callback_data;
|
1276
1277
|
|
1277
1278
|
// TODO: use rb_vrescue2 in Ruby 2.7 and above
|
1278
|
-
result = rb_rescue2(protected_callback, callback_data_value,
|
1279
|
-
rescue_callback, callback_data_value, rb_eException, (VALUE)0);
|
1279
|
+
result = rb_rescue2(MR_METHOD_FUNC(protected_callback), callback_data_value,
|
1280
|
+
MR_METHOD_FUNC(rescue_callback), callback_data_value, rb_eException, (VALUE)0);
|
1280
1281
|
|
1281
1282
|
if(callback_data.failed) {
|
1282
1283
|
rb_iv_set(parent, "@current_exception", result);
|
@@ -1337,7 +1338,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1337
1338
|
|
1338
1339
|
Local<String> v8_str =
|
1339
1340
|
String::NewFromUtf8(isolate, RSTRING_PTR(name),
|
1340
|
-
NewStringType::kNormal, (
|
1341
|
+
NewStringType::kNormal, RSTRING_LENINT(name))
|
1341
1342
|
.ToLocalChecked();
|
1342
1343
|
|
1343
1344
|
// Note that self (rb_cExternalFunction) is a pure Ruby T_OBJECT,
|
@@ -1357,7 +1358,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1357
1358
|
Local<String> eval =
|
1358
1359
|
String::NewFromUtf8(isolate, RSTRING_PTR(parent_object_eval),
|
1359
1360
|
NewStringType::kNormal,
|
1360
|
-
(
|
1361
|
+
RSTRING_LENINT(parent_object_eval))
|
1361
1362
|
.ToLocalChecked();
|
1362
1363
|
|
1363
1364
|
MaybeLocal<Script> parsed_script = Script::Compile(context, eval);
|
@@ -1455,42 +1456,33 @@ static void free_context_raw(void *arg) {
|
|
1455
1456
|
if (isolate_info) {
|
1456
1457
|
isolate_info->release();
|
1457
1458
|
}
|
1458
|
-
|
1459
|
-
xfree(context_info);
|
1460
1459
|
}
|
1461
1460
|
|
1462
1461
|
static void *free_context_thr(void* arg) {
|
1463
|
-
if (
|
1464
|
-
|
1465
|
-
|
1466
|
-
if (ruby_exiting) {
|
1467
|
-
return NULL;
|
1462
|
+
if (ruby_exiting.load() == 0) {
|
1463
|
+
free_context_raw(arg);
|
1464
|
+
xfree(arg);
|
1468
1465
|
}
|
1469
|
-
|
1470
|
-
free_context_raw(arg);
|
1471
|
-
|
1472
|
-
pthread_rwlock_unlock(&exit_lock);
|
1473
|
-
|
1474
1466
|
return NULL;
|
1475
1467
|
}
|
1476
1468
|
|
1477
1469
|
// destroys everything except freeing the ContextInfo struct (see deallocate())
|
1478
1470
|
static void free_context(ContextInfo* context_info) {
|
1479
|
-
|
1480
1471
|
IsolateInfo* isolate_info = context_info->isolate_info;
|
1481
1472
|
|
1482
|
-
ContextInfo* context_info_copy = ALLOC(ContextInfo);
|
1483
|
-
context_info_copy->isolate_info = context_info->isolate_info;
|
1484
|
-
context_info_copy->context = context_info->context;
|
1485
|
-
|
1486
1473
|
if (isolate_info && isolate_info->refs() > 1) {
|
1487
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;
|
1488
1479
|
if (pthread_create(&free_context_thread, thread_attr_p,
|
1489
1480
|
free_context_thr, (void*)context_info_copy)) {
|
1490
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);
|
1491
1483
|
}
|
1492
1484
|
} else {
|
1493
|
-
free_context_raw(
|
1485
|
+
free_context_raw(context_info);
|
1494
1486
|
}
|
1495
1487
|
|
1496
1488
|
context_info->context = NULL;
|
@@ -1763,9 +1755,7 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1763
1755
|
}
|
1764
1756
|
|
1765
1757
|
VALUE function_name = argv[0];
|
1766
|
-
|
1767
|
-
rb_raise(rb_eTypeError, "first argument should be a String");
|
1768
|
-
}
|
1758
|
+
Check_Type(function_name, T_STRING);
|
1769
1759
|
|
1770
1760
|
char *fname = RSTRING_PTR(function_name);
|
1771
1761
|
if (!fname) {
|
@@ -1816,23 +1806,15 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1816
1806
|
if (val.IsEmpty() || !val.ToLocalChecked()->IsFunction()) {
|
1817
1807
|
missingFunction = true;
|
1818
1808
|
} else {
|
1819
|
-
|
1820
1809
|
Local<v8::Function> fun = Local<v8::Function>::Cast(val.ToLocalChecked());
|
1810
|
+
VALUE tmp;
|
1821
1811
|
call.fun = fun;
|
1822
|
-
|
1823
|
-
|
1824
|
-
|
1825
|
-
call.argv = (v8::Local<Value> *) malloc(sizeof(void *) * fun_argc);
|
1826
|
-
if (!call.argv) {
|
1827
|
-
return Qnil;
|
1828
|
-
}
|
1829
|
-
for(int i=0; i < fun_argc; i++) {
|
1830
|
-
call.argv[i] = convert_ruby_to_v8(isolate, context, call_argv[i]);
|
1831
|
-
}
|
1812
|
+
call.argv = (v8::Local<Value> *)RB_ALLOCV_N(void *, tmp, call.argc);
|
1813
|
+
for(int i=0; i < call.argc; i++) {
|
1814
|
+
call.argv[i] = convert_ruby_to_v8(isolate, context, call_argv[i]);
|
1832
1815
|
}
|
1833
1816
|
rb_thread_call_without_gvl(nogvl_context_call, &call, unblock_function, &call);
|
1834
|
-
|
1835
|
-
|
1817
|
+
RB_ALLOCV_END(tmp);
|
1836
1818
|
}
|
1837
1819
|
}
|
1838
1820
|
|
@@ -1859,12 +1841,7 @@ static VALUE rb_context_create_isolate_value(VALUE self) {
|
|
1859
1841
|
static void set_ruby_exiting(VALUE value) {
|
1860
1842
|
(void)value;
|
1861
1843
|
|
1862
|
-
|
1863
|
-
|
1864
|
-
ruby_exiting = true;
|
1865
|
-
if (res == 0) {
|
1866
|
-
pthread_rwlock_unlock(&exit_lock);
|
1867
|
-
}
|
1844
|
+
ruby_exiting.store(1);
|
1868
1845
|
}
|
1869
1846
|
|
1870
1847
|
extern "C" {
|
@@ -1930,9 +1907,5 @@ extern "C" {
|
|
1930
1907
|
thread_attr_p = &attr;
|
1931
1908
|
}
|
1932
1909
|
}
|
1933
|
-
auto on_fork_for_child = []() {
|
1934
|
-
exit_lock = PTHREAD_RWLOCK_INITIALIZER;
|
1935
|
-
};
|
1936
|
-
pthread_atfork(nullptr, nullptr, on_fork_for_child);
|
1937
1910
|
}
|
1938
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:
|
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
|