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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f8218ee18399c771370bf599a4d4cd46707e9f463502e058c98fb38de7505c36
4
- data.tar.gz: 8fb4a94f36528343c71b05d2b25473a86c02ff0caf4b651acad4be460ea5f016
3
+ metadata.gz: b0a067ca868ffe75b89fc350d6d62b6e4d2670a4319eda9f6cd05fe424c762ec
4
+ data.tar.gz: 26c865123cceec38530a9be40e32c1042063f3cbb400172438308a7eb0e06568
5
5
  SHA512:
6
- metadata.gz: 6765304fc32d76d8e7050eeed958130e0e2075d7d2826edc6e855eeb447799b5f009d29b87e448f20c1c1cebba583d9d16569d5f0f83d5f1a64e2530333a5cee
7
- data.tar.gz: b224d6b448e1bc064b051adace85bf7ead7334b7682fe132897e87e028de6506afe5fc1921612ec9b8da3b09232f49cf9a6ecba4e594739792479e6e2fbfa54f
6
+ metadata.gz: 0e1de5f130d4cf5ddca606f57cf7864f9d05d87c9775cd6873d6bb6151983216f74bc7a7eb3e20a9382e5cf95a2ccd8958c7a548ddb491ac8cb9cb89d1c17fd8
7
+ data.tar.gz: 7df5b7f82afbc9040a6d317bdd8d8600d6656a965bcb44c3ce4f7b6f02007ae1e9fc088022e6c5c10c4755aa4c0dea9badbd4ca8087e3e9185f6f93f42835775
data/.dockerignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ lib/mini_racer_extension.so
11
+ lib/mini_racer_loader.so
12
+ *.bundle
@@ -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
@@ -8,4 +8,6 @@ Gemfile.lock
8
8
  /spec/reports/
9
9
  /tmp/
10
10
  lib/mini_racer_extension.so
11
+ lib/mini_racer_loader.so
11
12
  *.bundle
13
+ .vscode/
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
- matrix:
8
+ arch:
9
+ - amd64
10
+ - arm64
11
+ jobs:
8
12
  include:
9
- - rvm: 2.5.1
13
+ - rvm: 2.6
10
14
  os: osx
11
- osx_image: xcode9.4
12
- dist: trusty
13
- sudo: true
14
- before_install:
15
- - gem update --system
16
- - gem install bundler -v 1.16.2
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 alwatys appreciate V8's liberal use of threading:
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 gcc 4.8 (or later).
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 gcc that supports C++11 (gcc 4.8) this is included by default in ubuntu trusty based images.
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 4.8 on precise the simpler approach is to opt for the trusty based image.
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
@@ -11,6 +11,7 @@ end
11
11
  task :default => [:compile, :test]
12
12
 
13
13
  gem = Gem::Specification.load( File.dirname(__FILE__) + '/mini_racer.gemspec' )
14
+ Rake::ExtensionTask.new( 'mini_racer_loader', gem )
14
15
  Rake::ExtensionTask.new( 'mini_racer_extension', gem )
15
16
 
16
17
 
@@ -1,5 +1,7 @@
1
1
  require 'mkmf'
2
- require 'libv8'
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++0x"
14
+ $CPPFLAGS += " -std=c++14"
13
15
  $CPPFLAGS += " -fpermissive"
14
- $CPPFLAGS += " -DV8_COMPRESS_POINTERS"
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
- enum IsolateFlags {
132
- IN_GVL,
133
- DO_TERMINATE,
134
- MEM_SOFTLIMIT_VALUE,
135
- MEM_SOFTLIMIT_REACHED,
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((bool)isolate->GetData(MEM_SOFTLIMIT_REACHED)) return;
348
+ if (IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_REACHED)) {
349
+ return;
350
+ }
205
351
 
206
- size_t softlimit = *(size_t*) isolate->GetData(MEM_SOFTLIMIT_VALUE);
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->SetData(MEM_SOFTLIMIT_REACHED, (void*)true);
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
- // in gvl flag
326
- isolate->SetData(IN_GVL, (void*)false);
327
- // terminate ASAP
328
- isolate->SetData(DO_TERMINATE, (void*)false);
329
- // Memory softlimit
330
- isolate->SetData(MEM_SOFTLIMIT_VALUE, (void*)false);
331
- // Memory softlimit hit flag
332
- isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)false);
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->max_memory > 0) {
359
- isolate->SetData(MEM_SOFTLIMIT_VALUE, &eval_params->max_memory);
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->SetData(IN_GVL, (void*)true);
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 ID2SYM(rb_intern_str(str_symbol));
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 = (bool)isolate->GetData(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 (result.terminated || mem_softlimit_reached) {
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 ((bool)args->GetIsolate()->GetData(DO_TERMINATE) == true) {
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
- result = rb_rescue2((VALUE(*)(...))&protected_callback, (VALUE)(&callback_data),
1082
- (VALUE(*)(...))&rescue_callback, (VALUE)(&callback_data), rb_eException, (VALUE)0);
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 ((bool)args->GetIsolate()->GetData(DO_TERMINATE) == true) {
1100
- Isolate* isolate = args->GetIsolate();
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 = (bool)args.GetIsolate()->GetData(IN_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()->SetData(IN_GVL, (void*)true);
1285
+ IsolateData::Set(args.GetIsolate(), IsolateData::IN_GVL, true);
1114
1286
  rb_thread_call_with_gvl(gvl_ruby_callback, (void*)(&args));
1115
- args.GetIsolate()->SetData(IN_GVL, (void*)false);
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->SetData(DO_TERMINATE, (void*)true);
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
- // in gvl flag
1502
- isolate->SetData(IN_GVL, (void*)false);
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->SetData(MEM_SOFTLIMIT_VALUE, &call->max_memory);
1508
- isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)false);
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
- isolate->AddGCEpilogueCallback(gc_callback);
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->SetData(IN_GVL, (void*)true);
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,8 @@
1
+ require 'mkmf'
2
+
3
+ extension_name = 'mini_racer_loader'
4
+ dir_config extension_name
5
+
6
+ $CPPFLAGS += " -fvisibility=hidden "
7
+
8
+ create_makefile extension_name
@@ -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
+ }
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MiniRacer
4
- VERSION = "0.3.0"
4
+ VERSION = "0.5.0.pre"
5
+ LIBV8_NODE_VERSION = "~> 16.10.0.0"
5
6
  end
data/lib/mini_racer.rb CHANGED
@@ -1,10 +1,23 @@
1
1
  require "mini_racer/version"
2
- require "mini_racer_extension"
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', '> 8.4'
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.3'
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.3.0
4
+ version: 0.5.0.pre
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Saffron
8
- autorequire:
9
- bindir: exe
8
+ autorequire:
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-21 00:00:00.000000000 Z
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: '8.4'
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: '8.4'
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.3.0/CHANGELOG
126
- documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.3.0
127
- source_code_uri: https://github.com/discourse/mini_racer/tree/v0.3.0
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.3'
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: '0'
148
+ version: 1.3.1
143
149
  requirements: []
144
- rubygems_version: 3.0.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: []