mini_racer 0.3.0 → 0.5.0.pre
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 +78 -0
- data/.gitignore +2 -0
- data/.travis.yml +15 -9
- data/CHANGELOG +20 -0
- data/Dockerfile +21 -0
- data/README.md +17 -4
- data/Rakefile +1 -0
- data/ext/mini_racer_extension/extconf.rb +11 -4
- data/ext/mini_racer_extension/mini_racer_extension.cc +245 -52
- data/ext/mini_racer_loader/extconf.rb +8 -0
- data/ext/mini_racer_loader/mini_racer_loader.c +123 -0
- data/lib/mini_racer/version.rb +2 -1
- data/lib/mini_racer.rb +26 -7
- data/mini_racer.gemspec +3 -5
- metadata +24 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b0a067ca868ffe75b89fc350d6d62b6e4d2670a4319eda9f6cd05fe424c762ec
|
4
|
+
data.tar.gz: 26c865123cceec38530a9be40e32c1042063f3cbb400172438308a7eb0e06568
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0e1de5f130d4cf5ddca606f57cf7864f9d05d87c9775cd6873d6bb6151983216f74bc7a7eb3e20a9382e5cf95a2ccd8958c7a548ddb491ac8cb9cb89d1c17fd8
|
7
|
+
data.tar.gz: 7df5b7f82afbc9040a6d317bdd8d8600d6656a965bcb44c3ce4f7b6f02007ae1e9fc088022e6c5c10c4755aa4c0dea9badbd4ca8087e3e9185f6f93f42835775
|
data/.dockerignore
ADDED
@@ -0,0 +1,78 @@
|
|
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.6'
|
33
|
+
- '2.7'
|
34
|
+
- '3.0'
|
35
|
+
platform:
|
36
|
+
- amd64
|
37
|
+
- arm64
|
38
|
+
# arm
|
39
|
+
# ppc64le
|
40
|
+
# s390x
|
41
|
+
libc:
|
42
|
+
- gnu
|
43
|
+
- musl
|
44
|
+
name: Test (linux)
|
45
|
+
runs-on: ubuntu-20.04
|
46
|
+
steps:
|
47
|
+
- name: Enable ${{ matrix.platform }} platform
|
48
|
+
id: qemu
|
49
|
+
if: ${{ matrix.platform != 'amd64' }}
|
50
|
+
run: |
|
51
|
+
docker run --privileged --rm tonistiigi/binfmt:latest --install ${{ matrix.platform }} | tee platforms.json
|
52
|
+
echo "::set-output name=platforms::$(cat platforms.json)"
|
53
|
+
- name: Start container
|
54
|
+
id: container
|
55
|
+
run: |
|
56
|
+
case ${{ matrix.libc }} in
|
57
|
+
gnu)
|
58
|
+
echo 'ruby:${{ matrix.ruby }}'
|
59
|
+
;;
|
60
|
+
musl)
|
61
|
+
echo 'ruby:${{ matrix.ruby }}-alpine'
|
62
|
+
;;
|
63
|
+
esac > container_image
|
64
|
+
echo "::set-output name=image::$(cat container_image)"
|
65
|
+
docker run --rm -d -v "${PWD}":"${PWD}" -w "${PWD}" --platform linux/${{ matrix.platform }} $(cat container_image) /bin/sleep 64d | tee container_id
|
66
|
+
docker exec -w "${PWD}" $(cat container_id) uname -a
|
67
|
+
echo "::set-output name=id::$(cat container_id)"
|
68
|
+
- name: Install Alpine system dependencies
|
69
|
+
if: ${{ matrix.libc == 'musl' }}
|
70
|
+
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
|
71
|
+
- name: Checkout
|
72
|
+
uses: actions/checkout@v2
|
73
|
+
- name: Bundle
|
74
|
+
run: docker exec -w "${PWD}" ${{ steps.container.outputs.id }} bundle install
|
75
|
+
- name: Compile
|
76
|
+
run: docker exec -w "${PWD}" ${{ steps.container.outputs.id }} bundle exec rake compile
|
77
|
+
- name: Test
|
78
|
+
run: docker exec -w "${PWD}" ${{ steps.container.outputs.id }} bundle exec rake test
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,17 +1,23 @@
|
|
1
1
|
language: ruby
|
2
|
+
os: linux
|
2
3
|
rvm:
|
3
|
-
- 2.5
|
4
4
|
- 2.6
|
5
5
|
- 2.7
|
6
|
+
- 3.0
|
6
7
|
- ruby-head
|
7
|
-
|
8
|
+
arch:
|
9
|
+
- amd64
|
10
|
+
- arm64
|
11
|
+
jobs:
|
8
12
|
include:
|
9
|
-
- rvm: 2.
|
13
|
+
- rvm: 2.6
|
10
14
|
os: osx
|
11
|
-
osx_image:
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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
|
17
23
|
cache: bundler
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,23 @@
|
|
1
|
+
- 11-04-2021
|
2
|
+
|
3
|
+
- 0.4.0
|
4
|
+
|
5
|
+
- FEATURE: upgrade to libv8 node 15.14.0 (v8 8.6.395.17)
|
6
|
+
- Promote 0.4.0.beta1 to release, using libv8-node release path
|
7
|
+
|
8
|
+
- 08-04-2021
|
9
|
+
|
10
|
+
- 0.4.0.beta1
|
11
|
+
|
12
|
+
- FIX: on downgrade mkmf was picking the wrong version of libv8, this fix will correct future issues
|
13
|
+
- FEATURE: upgraded libv8 to use node libv8 build which supports M1 and various ARM builds v8 moved to (8.6.395.17)
|
14
|
+
|
15
|
+
- 23-07-2020
|
16
|
+
|
17
|
+
- 0.3.1
|
18
|
+
|
19
|
+
- FIX: specify that libv8 must be larger than 8.4.255 but smaller than 8.5, this avoids issues going forward
|
20
|
+
|
1
21
|
- 22-07-2020
|
2
22
|
|
3
23
|
- 0.3.0
|
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
@@ -85,6 +85,19 @@ context.eval 'var a = new Array(10000); while(true) {a = a.concat(new Array(1000
|
|
85
85
|
# => V8OutOfMemoryError is raised
|
86
86
|
```
|
87
87
|
|
88
|
+
### Object marshal max stackdepth support
|
89
|
+
|
90
|
+
Contexts can specify a stack depth limit for object marshalling. Max depth is unbounded by default.
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
# terminates script if stack depth exceeds max during marshal
|
94
|
+
context = MiniRacer::Context.new(marshal_stack_depth: 500)
|
95
|
+
context.attach("a", proc{|a| a})
|
96
|
+
|
97
|
+
context.eval("let arr = []; arr.push(arr); a(arr)")
|
98
|
+
# => RuntimeError is raised
|
99
|
+
```
|
100
|
+
|
88
101
|
### Rich debugging with "filename" support
|
89
102
|
|
90
103
|
```ruby
|
@@ -249,7 +262,7 @@ It is possible to set V8 Runtime flags:
|
|
249
262
|
MiniRacer::Platform.set_flags! :noconcurrent_recompilation, max_inlining_levels: 10
|
250
263
|
```
|
251
264
|
|
252
|
-
This can come in handy if you want to use MiniRacer with Unicorn, which doesn't seem to
|
265
|
+
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
266
|
```ruby
|
254
267
|
MiniRacer::Platform.set_flags! :noconcurrent_recompilation, :noconcurrent_sweeping
|
255
268
|
```
|
@@ -403,14 +416,14 @@ Or install it yourself as:
|
|
403
416
|
$ gem install mini_racer
|
404
417
|
|
405
418
|
|
406
|
-
**Note** using v8.h and compiling MiniRacer requires a C++11 standard compiler, more specifically clang 3.5 (or later) or
|
419
|
+
**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).
|
407
420
|
|
408
421
|
|
409
422
|
## Travis-ci
|
410
423
|
|
411
|
-
To install `mini-racer` you will need a version of
|
424
|
+
To install `mini-racer` you will need a version of GCC that supports C++11 (GCC 6.3) this is included by default in ubuntu trusty based images.
|
412
425
|
|
413
|
-
Travis today ships by default with a precise based image. Precise Pangolin (12.04 LTS) was first released in August 2012. Even though you can install GCC
|
426
|
+
Travis today ships by default with a precise based image. Precise Pangolin (12.04 LTS) was first released in August 2012. Even though you can install GCC 6.3 on precise the simpler approach is to opt for the trusty based image.
|
414
427
|
|
415
428
|
Add this to your .travis.yml file:
|
416
429
|
|
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
|
|
@@ -9,14 +11,19 @@ $CPPFLAGS += " -Wall" unless $CPPFLAGS.split.include? "-Wall"
|
|
9
11
|
$CPPFLAGS += " -g" unless $CPPFLAGS.split.include? "-g"
|
10
12
|
$CPPFLAGS += " -rdynamic" unless $CPPFLAGS.split.include? "-rdynamic"
|
11
13
|
$CPPFLAGS += " -fPIC" unless $CPPFLAGS.split.include? "-rdynamic" or IS_DARWIN
|
12
|
-
$CPPFLAGS += " -std=c++
|
14
|
+
$CPPFLAGS += " -std=c++14"
|
13
15
|
$CPPFLAGS += " -fpermissive"
|
14
|
-
|
16
|
+
#$CPPFLAGS += " -DV8_COMPRESS_POINTERS"
|
17
|
+
$CPPFLAGS += " -fvisibility=hidden "
|
15
18
|
|
16
19
|
$CPPFLAGS += " -Wno-reserved-user-defined-literal" if IS_DARWIN
|
17
20
|
|
18
21
|
$LDFLAGS.insert(0, " -stdlib=libc++ ") if IS_DARWIN
|
19
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
|
+
|
20
27
|
if ENV['CXX']
|
21
28
|
puts "SETTING CXX"
|
22
29
|
CONFIG['CXX'] = ENV['CXX']
|
@@ -54,7 +61,7 @@ if enable_config('debug') || enable_config('asan')
|
|
54
61
|
CONFIG['debugflags'] << ' -ggdb3 -O0'
|
55
62
|
end
|
56
63
|
|
57
|
-
Libv8.configure_makefile
|
64
|
+
Libv8::Node.configure_makefile
|
58
65
|
|
59
66
|
if enable_config('asan')
|
60
67
|
$CPPFLAGS.insert(0, " -fsanitize=address ")
|
@@ -115,6 +115,7 @@ typedef struct {
|
|
115
115
|
useconds_t timeout;
|
116
116
|
EvalResult* result;
|
117
117
|
size_t max_memory;
|
118
|
+
size_t marshal_stackdepth;
|
118
119
|
} EvalParams;
|
119
120
|
|
120
121
|
typedef struct {
|
@@ -126,13 +127,152 @@ typedef struct {
|
|
126
127
|
Local<Value> *argv;
|
127
128
|
EvalResult result;
|
128
129
|
size_t max_memory;
|
130
|
+
size_t marshal_stackdepth;
|
129
131
|
} FunctionCall;
|
130
132
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
133
|
+
class IsolateData {
|
134
|
+
public:
|
135
|
+
enum Flag {
|
136
|
+
// first flags are bitfield
|
137
|
+
// max count: sizeof(uintptr_t) * 8
|
138
|
+
IN_GVL, // whether we are inside of ruby gvl or not
|
139
|
+
DO_TERMINATE, // terminate as soon as possible
|
140
|
+
MEM_SOFTLIMIT_REACHED, // we've hit the memory soft limit
|
141
|
+
MEM_SOFTLIMIT_MAX, // maximum memory value
|
142
|
+
MARSHAL_STACKDEPTH_REACHED, // we've hit our max stack depth
|
143
|
+
MARSHAL_STACKDEPTH_VALUE, // current stackdepth
|
144
|
+
MARSHAL_STACKDEPTH_MAX, // maximum stack depth during marshal
|
145
|
+
};
|
146
|
+
|
147
|
+
static void Init(Isolate *isolate) {
|
148
|
+
// zero out all fields in the bitfield
|
149
|
+
isolate->SetData(0, 0);
|
150
|
+
}
|
151
|
+
|
152
|
+
static uintptr_t Get(Isolate *isolate, Flag flag) {
|
153
|
+
Bitfield u = { reinterpret_cast<uint64_t>(isolate->GetData(0)) };
|
154
|
+
switch (flag) {
|
155
|
+
case IN_GVL: return u.IN_GVL;
|
156
|
+
case DO_TERMINATE: return u.DO_TERMINATE;
|
157
|
+
case MEM_SOFTLIMIT_REACHED: return u.MEM_SOFTLIMIT_REACHED;
|
158
|
+
case MEM_SOFTLIMIT_MAX: return static_cast<uintptr_t>(u.MEM_SOFTLIMIT_MAX) << 10;
|
159
|
+
case MARSHAL_STACKDEPTH_REACHED: return u.MARSHAL_STACKDEPTH_REACHED;
|
160
|
+
case MARSHAL_STACKDEPTH_VALUE: return u.MARSHAL_STACKDEPTH_VALUE;
|
161
|
+
case MARSHAL_STACKDEPTH_MAX: return u.MARSHAL_STACKDEPTH_MAX;
|
162
|
+
}
|
163
|
+
|
164
|
+
// avoid compiler warning
|
165
|
+
return u.IN_GVL;
|
166
|
+
}
|
167
|
+
|
168
|
+
static void Set(Isolate *isolate, Flag flag, uintptr_t value) {
|
169
|
+
Bitfield u = { reinterpret_cast<uint64_t>(isolate->GetData(0)) };
|
170
|
+
switch (flag) {
|
171
|
+
case IN_GVL: u.IN_GVL = value; break;
|
172
|
+
case DO_TERMINATE: u.DO_TERMINATE = value; break;
|
173
|
+
case MEM_SOFTLIMIT_REACHED: u.MEM_SOFTLIMIT_REACHED = value; break;
|
174
|
+
// drop least significant 10 bits 'store memory amount in kb'
|
175
|
+
case MEM_SOFTLIMIT_MAX: u.MEM_SOFTLIMIT_MAX = value >> 10; break;
|
176
|
+
case MARSHAL_STACKDEPTH_REACHED: u.MARSHAL_STACKDEPTH_REACHED = value; break;
|
177
|
+
case MARSHAL_STACKDEPTH_VALUE: u.MARSHAL_STACKDEPTH_VALUE = value; break;
|
178
|
+
case MARSHAL_STACKDEPTH_MAX: u.MARSHAL_STACKDEPTH_MAX = value; break;
|
179
|
+
}
|
180
|
+
isolate->SetData(0, reinterpret_cast<void*>(u.dataPtr));
|
181
|
+
}
|
182
|
+
|
183
|
+
private:
|
184
|
+
struct Bitfield {
|
185
|
+
// WARNING: this would explode on platforms below 64 bit ptrs
|
186
|
+
// compiler will fail here, making it clear for them.
|
187
|
+
// Additionally, using the other part of the union to reinterpret the
|
188
|
+
// memory is undefined behavior according to spec, but is / has been stable
|
189
|
+
// across major compilers for decades.
|
190
|
+
static_assert(sizeof(uintptr_t) >= sizeof(uint64_t), "mini_racer not supported on this platform. ptr size must be at least 64 bit.");
|
191
|
+
union {
|
192
|
+
uint64_t dataPtr: 64;
|
193
|
+
// order in this struct matters. For cpu performance keep larger subobjects
|
194
|
+
// aligned on their boundaries (8 16 32), try not to straddle
|
195
|
+
struct {
|
196
|
+
size_t MEM_SOFTLIMIT_MAX:22;
|
197
|
+
bool IN_GVL:1;
|
198
|
+
bool DO_TERMINATE:1;
|
199
|
+
bool MEM_SOFTLIMIT_REACHED:1;
|
200
|
+
bool MARSHAL_STACKDEPTH_REACHED:1;
|
201
|
+
uint8_t :0; // align to next 8bit bound
|
202
|
+
size_t MARSHAL_STACKDEPTH_VALUE:10;
|
203
|
+
uint8_t :0; // align to next 8bit bound
|
204
|
+
size_t MARSHAL_STACKDEPTH_MAX:10;
|
205
|
+
};
|
206
|
+
};
|
207
|
+
};
|
208
|
+
};
|
209
|
+
|
210
|
+
struct StackCounter {
|
211
|
+
static void Reset(Isolate* isolate) {
|
212
|
+
if (IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_MAX) > 0) {
|
213
|
+
IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE, 0);
|
214
|
+
IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED, false);
|
215
|
+
}
|
216
|
+
}
|
217
|
+
|
218
|
+
static void SetMax(Isolate* isolate, size_t marshalMaxStackDepth) {
|
219
|
+
if (marshalMaxStackDepth > 0) {
|
220
|
+
IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_MAX, marshalMaxStackDepth);
|
221
|
+
IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE, 0);
|
222
|
+
IsolateData::Set(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED, false);
|
223
|
+
}
|
224
|
+
}
|
225
|
+
|
226
|
+
StackCounter(Isolate* isolate) {
|
227
|
+
this->isActive = IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_MAX) > 0;
|
228
|
+
|
229
|
+
if (this->isActive) {
|
230
|
+
this->isolate = isolate;
|
231
|
+
this->IncDepth(1);
|
232
|
+
}
|
233
|
+
}
|
234
|
+
|
235
|
+
bool IsTooDeep() {
|
236
|
+
if (!this->IsActive()) {
|
237
|
+
return false;
|
238
|
+
}
|
239
|
+
|
240
|
+
size_t depth = IsolateData::Get(this->isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE);
|
241
|
+
size_t maxDepth = IsolateData::Get(this->isolate, IsolateData::MARSHAL_STACKDEPTH_MAX);
|
242
|
+
if (depth > maxDepth) {
|
243
|
+
IsolateData::Set(this->isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED, true);
|
244
|
+
return true;
|
245
|
+
}
|
246
|
+
|
247
|
+
return false;
|
248
|
+
}
|
249
|
+
|
250
|
+
bool IsActive() {
|
251
|
+
return this->isActive && !IsolateData::Get(this->isolate, IsolateData::DO_TERMINATE);
|
252
|
+
}
|
253
|
+
|
254
|
+
~StackCounter() {
|
255
|
+
if (this->IsActive()) {
|
256
|
+
this->IncDepth(-1);
|
257
|
+
}
|
258
|
+
}
|
259
|
+
|
260
|
+
private:
|
261
|
+
Isolate* isolate;
|
262
|
+
bool isActive;
|
263
|
+
|
264
|
+
void IncDepth(int direction) {
|
265
|
+
int inc = direction > 0 ? 1 : -1;
|
266
|
+
|
267
|
+
size_t depth = IsolateData::Get(this->isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE);
|
268
|
+
|
269
|
+
// don't decrement past 0
|
270
|
+
if (inc > 0 || depth > 0) {
|
271
|
+
depth += inc;
|
272
|
+
}
|
273
|
+
|
274
|
+
IsolateData::Set(this->isolate, IsolateData::MARSHAL_STACKDEPTH_VALUE, depth);
|
275
|
+
}
|
136
276
|
};
|
137
277
|
|
138
278
|
static VALUE rb_cContext;
|
@@ -156,7 +296,8 @@ static std::mutex platform_lock;
|
|
156
296
|
|
157
297
|
static pthread_attr_t *thread_attr_p;
|
158
298
|
static pthread_rwlock_t exit_lock = PTHREAD_RWLOCK_INITIALIZER;
|
159
|
-
static bool ruby_exiting; // guarded by exit_lock
|
299
|
+
static bool ruby_exiting = false; // guarded by exit_lock
|
300
|
+
static bool single_threaded = false;
|
160
301
|
|
161
302
|
static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
162
303
|
bool platform_already_initialized = false;
|
@@ -169,6 +310,9 @@ static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
|
169
310
|
platform_lock.lock();
|
170
311
|
|
171
312
|
if (current_platform == NULL) {
|
313
|
+
if (!strcmp(RSTRING_PTR(flag_as_str), "--single_threaded")) {
|
314
|
+
single_threaded = true;
|
315
|
+
}
|
172
316
|
V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), (int)RSTRING_LEN(flag_as_str));
|
173
317
|
} else {
|
174
318
|
platform_already_initialized = true;
|
@@ -201,16 +345,18 @@ static void init_v8() {
|
|
201
345
|
}
|
202
346
|
|
203
347
|
static void gc_callback(Isolate *isolate, GCType type, GCCallbackFlags flags) {
|
204
|
-
if((
|
348
|
+
if (IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_REACHED)) {
|
349
|
+
return;
|
350
|
+
}
|
205
351
|
|
206
|
-
size_t softlimit =
|
352
|
+
size_t softlimit = IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_MAX);
|
207
353
|
|
208
354
|
HeapStatistics stats;
|
209
355
|
isolate->GetHeapStatistics(&stats);
|
210
356
|
size_t used = stats.used_heap_size();
|
211
357
|
|
212
358
|
if(used > softlimit) {
|
213
|
-
isolate
|
359
|
+
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_REACHED, true);
|
214
360
|
isolate->TerminateExecution();
|
215
361
|
}
|
216
362
|
}
|
@@ -322,14 +468,15 @@ nogvl_context_eval(void* arg) {
|
|
322
468
|
Context::Scope context_scope(context);
|
323
469
|
v8::ScriptOrigin *origin = NULL;
|
324
470
|
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
471
|
+
IsolateData::Init(isolate);
|
472
|
+
|
473
|
+
if (eval_params->max_memory > 0) {
|
474
|
+
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_MAX, eval_params->max_memory);
|
475
|
+
if (!isolate_info->added_gc_cb) {
|
476
|
+
isolate->AddGCEpilogueCallback(gc_callback);
|
477
|
+
isolate_info->added_gc_cb = true;
|
478
|
+
}
|
479
|
+
}
|
333
480
|
|
334
481
|
MaybeLocal<Script> parsed_script;
|
335
482
|
|
@@ -355,12 +502,8 @@ nogvl_context_eval(void* arg) {
|
|
355
502
|
result->message->Reset(isolate, trycatch.Exception());
|
356
503
|
} else {
|
357
504
|
// parsing successful
|
358
|
-
if (eval_params->
|
359
|
-
isolate
|
360
|
-
if (!isolate_info->added_gc_cb) {
|
361
|
-
isolate->AddGCEpilogueCallback(gc_callback);
|
362
|
-
isolate_info->added_gc_cb = true;
|
363
|
-
}
|
505
|
+
if (eval_params->marshal_stackdepth > 0) {
|
506
|
+
StackCounter::SetMax(isolate, eval_params->marshal_stackdepth);
|
364
507
|
}
|
365
508
|
|
366
509
|
maybe_value = parsed_script.ToLocalChecked()->Run(context);
|
@@ -368,7 +511,7 @@ nogvl_context_eval(void* arg) {
|
|
368
511
|
|
369
512
|
prepare_result(maybe_value, trycatch, isolate, context, *result);
|
370
513
|
|
371
|
-
isolate
|
514
|
+
IsolateData::Set(isolate, IsolateData::IN_GVL, true);
|
372
515
|
|
373
516
|
return NULL;
|
374
517
|
}
|
@@ -386,6 +529,18 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
386
529
|
Isolate::Scope isolate_scope(isolate);
|
387
530
|
HandleScope scope(isolate);
|
388
531
|
|
532
|
+
StackCounter stackCounter(isolate);
|
533
|
+
|
534
|
+
if (IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED)) {
|
535
|
+
return Qnil;
|
536
|
+
}
|
537
|
+
|
538
|
+
if (stackCounter.IsTooDeep()) {
|
539
|
+
IsolateData::Set(isolate, IsolateData::DO_TERMINATE, true);
|
540
|
+
isolate->TerminateExecution();
|
541
|
+
return Qnil;
|
542
|
+
}
|
543
|
+
|
389
544
|
if (value->IsNull() || value->IsUndefined()){
|
390
545
|
return Qnil;
|
391
546
|
}
|
@@ -474,7 +629,7 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
474
629
|
rb_enc_find("utf-8")
|
475
630
|
);
|
476
631
|
|
477
|
-
return
|
632
|
+
return rb_str_intern(str_symbol);
|
478
633
|
}
|
479
634
|
|
480
635
|
MaybeLocal<String> rstr_maybe = value->ToString(context);
|
@@ -539,7 +694,8 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
539
694
|
length = RARRAY_LEN(value);
|
540
695
|
array = Array::New(isolate, (int)length);
|
541
696
|
for(i=0; i<length; i++) {
|
542
|
-
array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
|
697
|
+
Maybe<bool> success = array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
|
698
|
+
(void)(success);
|
543
699
|
}
|
544
700
|
return scope.Escape(array);
|
545
701
|
case T_HASH:
|
@@ -548,8 +704,9 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
|
|
548
704
|
length = RARRAY_LEN(hash_as_array);
|
549
705
|
for(i=0; i<length; i++) {
|
550
706
|
pair = rb_ary_entry(hash_as_array, i);
|
551
|
-
object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
|
707
|
+
Maybe<bool> success = object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
|
552
708
|
convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 1)));
|
709
|
+
(void)(success);
|
553
710
|
}
|
554
711
|
return scope.Escape(object);
|
555
712
|
case T_SYMBOL:
|
@@ -880,9 +1037,14 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
880
1037
|
if (!result.executed) {
|
881
1038
|
VALUE ruby_exception = rb_iv_get(self, "@current_exception");
|
882
1039
|
if (ruby_exception == Qnil) {
|
883
|
-
bool mem_softlimit_reached = (
|
1040
|
+
bool mem_softlimit_reached = IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_REACHED);
|
1041
|
+
bool marshal_stack_maxdepth_reached = IsolateData::Get(isolate, IsolateData::MARSHAL_STACKDEPTH_REACHED);
|
884
1042
|
// If we were terminated or have the memory softlimit flag set
|
885
|
-
if (
|
1043
|
+
if (marshal_stack_maxdepth_reached) {
|
1044
|
+
ruby_exception = rb_eScriptRuntimeError;
|
1045
|
+
std::string msg = std::string("Marshal object depth too deep. Script terminated.");
|
1046
|
+
message = rb_enc_str_new(msg.c_str(), msg.length(), rb_enc_find("utf-8"));
|
1047
|
+
} else if (result.terminated || mem_softlimit_reached) {
|
886
1048
|
ruby_exception = mem_softlimit_reached ? rb_eV8OutOfMemoryError : rb_eScriptTerminatedError;
|
887
1049
|
} else {
|
888
1050
|
ruby_exception = rb_eScriptRuntimeError;
|
@@ -917,6 +1079,7 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
917
1079
|
VALUE json_string = rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
|
918
1080
|
ret = rb_funcall(rb_mJSON, rb_intern("parse"), 1, json_string);
|
919
1081
|
} else {
|
1082
|
+
StackCounter::Reset(isolate);
|
920
1083
|
ret = convert_v8_to_ruby(isolate, *p_ctx, tmp);
|
921
1084
|
}
|
922
1085
|
|
@@ -974,6 +1137,7 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
|
|
974
1137
|
eval_params.result = &eval_result;
|
975
1138
|
eval_params.timeout = 0;
|
976
1139
|
eval_params.max_memory = 0;
|
1140
|
+
eval_params.marshal_stackdepth = 0;
|
977
1141
|
VALUE timeout = rb_iv_get(self, "@timeout");
|
978
1142
|
if (timeout != Qnil) {
|
979
1143
|
eval_params.timeout = (useconds_t)NUM2LONG(timeout);
|
@@ -984,6 +1148,11 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
|
|
984
1148
|
eval_params.max_memory = (size_t)NUM2ULONG(mem_softlimit);
|
985
1149
|
}
|
986
1150
|
|
1151
|
+
VALUE stack_depth = rb_iv_get(self, "@marshal_stack_depth");
|
1152
|
+
if (stack_depth != Qnil) {
|
1153
|
+
eval_params.marshal_stackdepth = (size_t)NUM2ULONG(stack_depth);
|
1154
|
+
}
|
1155
|
+
|
987
1156
|
eval_result.message = NULL;
|
988
1157
|
eval_result.backtrace = NULL;
|
989
1158
|
|
@@ -1053,6 +1222,7 @@ gvl_ruby_callback(void* data) {
|
|
1053
1222
|
|
1054
1223
|
for (int i = 0; i < length; i++) {
|
1055
1224
|
Local<Value> value = ((*args)[i]).As<Value>();
|
1225
|
+
StackCounter::Reset(args->GetIsolate());
|
1056
1226
|
VALUE tmp = convert_v8_to_ruby(args->GetIsolate(),
|
1057
1227
|
*context_info->context, value);
|
1058
1228
|
rb_ary_push(ruby_args, tmp);
|
@@ -1066,7 +1236,7 @@ gvl_ruby_callback(void* data) {
|
|
1066
1236
|
callback_data.ruby_args = ruby_args;
|
1067
1237
|
callback_data.failed = false;
|
1068
1238
|
|
1069
|
-
if ((
|
1239
|
+
if (IsolateData::Get(args->GetIsolate(), IsolateData::DO_TERMINATE)) {
|
1070
1240
|
args->GetIsolate()->ThrowException(
|
1071
1241
|
String::NewFromUtf8Literal(args->GetIsolate(),
|
1072
1242
|
"Terminated execution during transition from Ruby to JS"));
|
@@ -1078,8 +1248,11 @@ gvl_ruby_callback(void* data) {
|
|
1078
1248
|
return NULL;
|
1079
1249
|
}
|
1080
1250
|
|
1081
|
-
|
1082
|
-
|
1251
|
+
VALUE callback_data_value = (VALUE)&callback_data;
|
1252
|
+
|
1253
|
+
// TODO: use rb_vrescue2 in Ruby 2.7 and above
|
1254
|
+
result = rb_rescue2(RUBY_METHOD_FUNC(protected_callback), callback_data_value,
|
1255
|
+
RUBY_METHOD_FUNC(rescue_callback), callback_data_value, rb_eException, (VALUE)0);
|
1083
1256
|
|
1084
1257
|
if(callback_data.failed) {
|
1085
1258
|
rb_iv_set(parent, "@current_exception", result);
|
@@ -1096,23 +1269,22 @@ gvl_ruby_callback(void* data) {
|
|
1096
1269
|
rb_gc_force_recycle(ruby_args);
|
1097
1270
|
}
|
1098
1271
|
|
1099
|
-
if ((
|
1100
|
-
|
1101
|
-
isolate->TerminateExecution();
|
1272
|
+
if (IsolateData::Get(args->GetIsolate(), IsolateData::DO_TERMINATE)) {
|
1273
|
+
args->GetIsolate()->TerminateExecution();
|
1102
1274
|
}
|
1103
1275
|
|
1104
1276
|
return NULL;
|
1105
1277
|
}
|
1106
1278
|
|
1107
1279
|
static void ruby_callback(const FunctionCallbackInfo<Value>& args) {
|
1108
|
-
bool has_gvl = (
|
1280
|
+
bool has_gvl = IsolateData::Get(args.GetIsolate(), IsolateData::IN_GVL);
|
1109
1281
|
|
1110
1282
|
if(has_gvl) {
|
1111
1283
|
gvl_ruby_callback((void*)&args);
|
1112
1284
|
} else {
|
1113
|
-
args.GetIsolate()
|
1285
|
+
IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, true);
|
1114
1286
|
rb_thread_call_with_gvl(gvl_ruby_callback, (void*)(&args));
|
1115
|
-
args.GetIsolate()
|
1287
|
+
IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, false);
|
1116
1288
|
}
|
1117
1289
|
}
|
1118
1290
|
|
@@ -1153,12 +1325,13 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1153
1325
|
Local<Value> external = External::New(isolate, self_copy);
|
1154
1326
|
|
1155
1327
|
if (parent_object == Qnil) {
|
1156
|
-
context->Global()->Set(
|
1328
|
+
Maybe<bool> success = context->Global()->Set(
|
1157
1329
|
context,
|
1158
1330
|
v8_str,
|
1159
1331
|
FunctionTemplate::New(isolate, ruby_callback, external)
|
1160
1332
|
->GetFunction(context)
|
1161
1333
|
.ToLocalChecked());
|
1334
|
+
(void)success;
|
1162
1335
|
|
1163
1336
|
} else {
|
1164
1337
|
Local<String> eval =
|
@@ -1178,12 +1351,13 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1178
1351
|
if (!maybe_value.IsEmpty()) {
|
1179
1352
|
Local<Value> value = maybe_value.ToLocalChecked();
|
1180
1353
|
if (value->IsObject()) {
|
1181
|
-
value.As<Object>()->Set(
|
1354
|
+
Maybe<bool> success = value.As<Object>()->Set(
|
1182
1355
|
context,
|
1183
1356
|
v8_str,
|
1184
1357
|
FunctionTemplate::New(isolate, ruby_callback, external)
|
1185
1358
|
->GetFunction(context)
|
1186
1359
|
.ToLocalChecked());
|
1360
|
+
(void)success;
|
1187
1361
|
attach_error = false;
|
1188
1362
|
}
|
1189
1363
|
}
|
@@ -1221,11 +1395,16 @@ IsolateInfo::~IsolateInfo() {
|
|
1221
1395
|
"it can not be disposed and memory will not be "
|
1222
1396
|
"reclaimed till the Ruby process exits.\n");
|
1223
1397
|
} else {
|
1224
|
-
if (this->pid != getpid()) {
|
1398
|
+
if (this->pid != getpid() && !single_threaded) {
|
1225
1399
|
fprintf(stderr, "WARNING: V8 isolate was forked, "
|
1226
1400
|
"it can not be disposed and "
|
1227
1401
|
"memory will not be reclaimed "
|
1228
|
-
"till the Ruby process exits.\n"
|
1402
|
+
"till the Ruby process exits.\n"
|
1403
|
+
"It is VERY likely your process will hang.\n"
|
1404
|
+
"If you wish to use v8 in forked environment "
|
1405
|
+
"please ensure the platform is initialized with:\n"
|
1406
|
+
"MiniRacer::Platform.set_flags! :single_threaded\n"
|
1407
|
+
);
|
1229
1408
|
} else {
|
1230
1409
|
isolate->Dispose();
|
1231
1410
|
}
|
@@ -1469,7 +1648,7 @@ rb_context_stop(VALUE self) {
|
|
1469
1648
|
Isolate* isolate = context_info->isolate_info->isolate;
|
1470
1649
|
|
1471
1650
|
// flag for termination
|
1472
|
-
isolate
|
1651
|
+
IsolateData::Set(isolate, IsolateData::DO_TERMINATE, true);
|
1473
1652
|
|
1474
1653
|
isolate->TerminateExecution();
|
1475
1654
|
rb_funcall(self, rb_intern("stop_attached"), 0);
|
@@ -1498,18 +1677,20 @@ nogvl_context_call(void *args) {
|
|
1498
1677
|
IsolateInfo *isolate_info = call->context_info->isolate_info;
|
1499
1678
|
Isolate* isolate = isolate_info->isolate;
|
1500
1679
|
|
1501
|
-
|
1502
|
-
isolate
|
1503
|
-
// terminate ASAP
|
1504
|
-
isolate->SetData(DO_TERMINATE, (void*)false);
|
1680
|
+
IsolateData::Set(isolate, IsolateData::IN_GVL, false);
|
1681
|
+
IsolateData::Set(isolate, IsolateData::DO_TERMINATE, false);
|
1505
1682
|
|
1506
1683
|
if (call->max_memory > 0) {
|
1507
|
-
isolate
|
1508
|
-
isolate
|
1684
|
+
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_MAX, call->max_memory);
|
1685
|
+
IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_REACHED, false);
|
1509
1686
|
if (!isolate_info->added_gc_cb) {
|
1510
|
-
|
1687
|
+
isolate->AddGCEpilogueCallback(gc_callback);
|
1511
1688
|
isolate_info->added_gc_cb = true;
|
1689
|
+
}
|
1512
1690
|
}
|
1691
|
+
|
1692
|
+
if (call->marshal_stackdepth > 0) {
|
1693
|
+
StackCounter::SetMax(isolate, call->marshal_stackdepth);
|
1513
1694
|
}
|
1514
1695
|
|
1515
1696
|
Isolate::Scope isolate_scope(isolate);
|
@@ -1527,7 +1708,7 @@ nogvl_context_call(void *args) {
|
|
1527
1708
|
MaybeLocal<v8::Value> res = fun->Call(context, context->Global(), call->argc, call->argv);
|
1528
1709
|
prepare_result(res, trycatch, isolate, context, eval_res);
|
1529
1710
|
|
1530
|
-
isolate
|
1711
|
+
IsolateData::Set(isolate, IsolateData::IN_GVL, true);
|
1531
1712
|
|
1532
1713
|
return NULL;
|
1533
1714
|
}
|
@@ -1575,6 +1756,13 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1575
1756
|
unsigned long sl_int = NUM2ULONG(mem_softlimit);
|
1576
1757
|
call.max_memory = (size_t)sl_int;
|
1577
1758
|
}
|
1759
|
+
|
1760
|
+
call.marshal_stackdepth = 0;
|
1761
|
+
VALUE marshal_stackdepth = rb_iv_get(self, "@marshal_stack_depth");
|
1762
|
+
if (marshal_stackdepth != Qnil) {
|
1763
|
+
unsigned long sl_int = NUM2ULONG(marshal_stackdepth);
|
1764
|
+
call.marshal_stackdepth = (size_t)sl_int;
|
1765
|
+
}
|
1578
1766
|
|
1579
1767
|
bool missingFunction = false;
|
1580
1768
|
{
|
@@ -1640,6 +1828,7 @@ static void set_ruby_exiting(VALUE value) {
|
|
1640
1828
|
(void)value;
|
1641
1829
|
|
1642
1830
|
int res = pthread_rwlock_wrlock(&exit_lock);
|
1831
|
+
|
1643
1832
|
ruby_exiting = true;
|
1644
1833
|
if (res == 0) {
|
1645
1834
|
pthread_rwlock_unlock(&exit_lock);
|
@@ -1648,7 +1837,7 @@ static void set_ruby_exiting(VALUE value) {
|
|
1648
1837
|
|
1649
1838
|
extern "C" {
|
1650
1839
|
|
1651
|
-
void Init_mini_racer_extension ( void )
|
1840
|
+
__attribute__((visibility("default"))) void Init_mini_racer_extension ( void )
|
1652
1841
|
{
|
1653
1842
|
VALUE rb_mMiniRacer = rb_define_module("MiniRacer");
|
1654
1843
|
rb_cContext = rb_define_class_under(rb_mMiniRacer, "Context", rb_cObject);
|
@@ -1710,5 +1899,9 @@ extern "C" {
|
|
1710
1899
|
thread_attr_p = &attr;
|
1711
1900
|
}
|
1712
1901
|
}
|
1902
|
+
auto on_fork_for_child = []() {
|
1903
|
+
exit_lock = PTHREAD_RWLOCK_INITIALIZER;
|
1904
|
+
};
|
1905
|
+
pthread_atfork(nullptr, nullptr, on_fork_for_child);
|
1713
1906
|
}
|
1714
1907
|
}
|
@@ -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/version.rb
CHANGED
data/lib/mini_racer.rb
CHANGED
@@ -1,10 +1,23 @@
|
|
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
|
|
6
16
|
module MiniRacer
|
7
17
|
|
18
|
+
MARSHAL_STACKDEPTH_DEFAULT = 2**9-2
|
19
|
+
MARSHAL_STACKDEPTH_MAX_VALUE = 2**10-2
|
20
|
+
|
8
21
|
class Error < ::StandardError; end
|
9
22
|
|
10
23
|
class ContextDisposedError < Error; end
|
@@ -130,10 +143,10 @@ module MiniRacer
|
|
130
143
|
end
|
131
144
|
end
|
132
145
|
|
133
|
-
def initialize(max_memory: nil, timeout: nil, isolate: nil, ensure_gc_after_idle: nil, snapshot: nil)
|
146
|
+
def initialize(max_memory: nil, timeout: nil, isolate: nil, ensure_gc_after_idle: nil, snapshot: nil, marshal_stack_depth: nil)
|
134
147
|
options ||= {}
|
135
148
|
|
136
|
-
check_init_options!(isolate: isolate, snapshot: snapshot, max_memory: max_memory, ensure_gc_after_idle: ensure_gc_after_idle, timeout: timeout)
|
149
|
+
check_init_options!(isolate: isolate, snapshot: snapshot, max_memory: max_memory, marshal_stack_depth: marshal_stack_depth, ensure_gc_after_idle: ensure_gc_after_idle, timeout: timeout)
|
137
150
|
|
138
151
|
@functions = {}
|
139
152
|
@timeout = nil
|
@@ -141,6 +154,7 @@ module MiniRacer
|
|
141
154
|
@current_exception = nil
|
142
155
|
@timeout = timeout
|
143
156
|
@max_memory = max_memory
|
157
|
+
@marshal_stack_depth = marshal_stack_depth
|
144
158
|
|
145
159
|
# false signals it should be fetched if requested
|
146
160
|
@isolate = isolate || false
|
@@ -369,11 +383,12 @@ module MiniRacer
|
|
369
383
|
rp.close if rp
|
370
384
|
end
|
371
385
|
|
372
|
-
def check_init_options!(isolate:, snapshot:, max_memory:, ensure_gc_after_idle:, timeout:)
|
386
|
+
def check_init_options!(isolate:, snapshot:, max_memory:, marshal_stack_depth:, ensure_gc_after_idle:, timeout:)
|
373
387
|
assert_option_is_nil_or_a('isolate', isolate, Isolate)
|
374
388
|
assert_option_is_nil_or_a('snapshot', snapshot, Snapshot)
|
375
389
|
|
376
|
-
assert_numeric_or_nil('max_memory', max_memory, min_value: 10_000)
|
390
|
+
assert_numeric_or_nil('max_memory', max_memory, min_value: 10_000, max_value: 2**32-1)
|
391
|
+
assert_numeric_or_nil('marshal_stack_depth', marshal_stack_depth, min_value: 1, max_value: MARSHAL_STACKDEPTH_MAX_VALUE)
|
377
392
|
assert_numeric_or_nil('ensure_gc_after_idle', ensure_gc_after_idle, min_value: 1)
|
378
393
|
assert_numeric_or_nil('timeout', timeout, min_value: 1)
|
379
394
|
|
@@ -382,9 +397,13 @@ module MiniRacer
|
|
382
397
|
end
|
383
398
|
end
|
384
399
|
|
385
|
-
def assert_numeric_or_nil(option_name, object, min_value:)
|
400
|
+
def assert_numeric_or_nil(option_name, object, min_value:, max_value: nil)
|
401
|
+
if max_value && object.is_a?(Numeric) && object > max_value
|
402
|
+
raise ArgumentError, "#{option_name} must be less than or equal to #{max_value}"
|
403
|
+
end
|
404
|
+
|
386
405
|
if object.is_a?(Numeric) && object < min_value
|
387
|
-
raise ArgumentError, "#{option_name} must be larger than #{min_value}"
|
406
|
+
raise ArgumentError, "#{option_name} must be larger than or equal to #{min_value}"
|
388
407
|
end
|
389
408
|
|
390
409
|
if !object.nil? && !object.is_a?(Numeric)
|
data/mini_racer.gemspec
CHANGED
@@ -22,8 +22,6 @@ Gem::Specification.new do |spec|
|
|
22
22
|
}
|
23
23
|
|
24
24
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(benchmark|test|spec|features|examples)/}) }
|
25
|
-
spec.bindir = "exe"
|
26
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
27
25
|
spec.require_paths = ["lib"]
|
28
26
|
|
29
27
|
spec.add_development_dependency "bundler"
|
@@ -32,10 +30,10 @@ Gem::Specification.new do |spec|
|
|
32
30
|
spec.add_development_dependency "rake-compiler"
|
33
31
|
spec.add_development_dependency "m"
|
34
32
|
|
35
|
-
spec.add_dependency 'libv8',
|
33
|
+
spec.add_dependency 'libv8-node', MiniRacer::LIBV8_NODE_VERSION
|
36
34
|
spec.require_paths = ["lib", "ext"]
|
37
35
|
|
38
|
-
spec.extensions = ["ext/mini_racer_extension/extconf.rb"]
|
36
|
+
spec.extensions = ["ext/mini_racer_loader/extconf.rb", "ext/mini_racer_extension/extconf.rb"]
|
39
37
|
|
40
|
-
spec.required_ruby_version = '>= 2.
|
38
|
+
spec.required_ruby_version = '>= 2.6'
|
41
39
|
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.5.0.pre
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Saffron
|
8
|
-
autorequire:
|
9
|
-
bindir:
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-11-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -81,31 +81,35 @@ dependencies:
|
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name: libv8
|
84
|
+
name: libv8-node
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- - "
|
87
|
+
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
89
|
+
version: 16.10.0.0
|
90
90
|
type: :runtime
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- - "
|
94
|
+
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
96
|
+
version: 16.10.0.0
|
97
97
|
description: Minimal embedded v8 engine for Ruby
|
98
98
|
email:
|
99
99
|
- sam.saffron@gmail.com
|
100
100
|
executables: []
|
101
101
|
extensions:
|
102
|
+
- ext/mini_racer_loader/extconf.rb
|
102
103
|
- ext/mini_racer_extension/extconf.rb
|
103
104
|
extra_rdoc_files: []
|
104
105
|
files:
|
106
|
+
- ".dockerignore"
|
107
|
+
- ".github/workflows/ci.yml"
|
105
108
|
- ".gitignore"
|
106
109
|
- ".travis.yml"
|
107
110
|
- CHANGELOG
|
108
111
|
- CODE_OF_CONDUCT.md
|
112
|
+
- Dockerfile
|
109
113
|
- Gemfile
|
110
114
|
- LICENSE.txt
|
111
115
|
- README.md
|
@@ -114,6 +118,8 @@ files:
|
|
114
118
|
- bin/setup
|
115
119
|
- ext/mini_racer_extension/extconf.rb
|
116
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
|
117
123
|
- lib/mini_racer.rb
|
118
124
|
- lib/mini_racer/version.rb
|
119
125
|
- mini_racer.gemspec
|
@@ -122,10 +128,10 @@ licenses:
|
|
122
128
|
- MIT
|
123
129
|
metadata:
|
124
130
|
bug_tracker_uri: https://github.com/discourse/mini_racer/issues
|
125
|
-
changelog_uri: https://github.com/discourse/mini_racer/blob/v0.
|
126
|
-
documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.
|
127
|
-
source_code_uri: https://github.com/discourse/mini_racer/tree/v0.
|
128
|
-
post_install_message:
|
131
|
+
changelog_uri: https://github.com/discourse/mini_racer/blob/v0.5.0.pre/CHANGELOG
|
132
|
+
documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.5.0.pre
|
133
|
+
source_code_uri: https://github.com/discourse/mini_racer/tree/v0.5.0.pre
|
134
|
+
post_install_message:
|
129
135
|
rdoc_options: []
|
130
136
|
require_paths:
|
131
137
|
- lib
|
@@ -134,15 +140,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
134
140
|
requirements:
|
135
141
|
- - ">="
|
136
142
|
- !ruby/object:Gem::Version
|
137
|
-
version: '2.
|
143
|
+
version: '2.6'
|
138
144
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
139
145
|
requirements:
|
140
|
-
- - "
|
146
|
+
- - ">"
|
141
147
|
- !ruby/object:Gem::Version
|
142
|
-
version:
|
148
|
+
version: 1.3.1
|
143
149
|
requirements: []
|
144
|
-
rubygems_version: 3.
|
145
|
-
signing_key:
|
150
|
+
rubygems_version: 3.1.6
|
151
|
+
signing_key:
|
146
152
|
specification_version: 4
|
147
153
|
summary: Minimal embedded v8 for Ruby
|
148
154
|
test_files: []
|