mini_racer 0.2.13 → 0.4.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 420c1d474734931c13f1246154eb045600ce24db66c05904c5df19681a03e41b
4
- data.tar.gz: 403320470422a7085305e6139cf769cdd5fe7e0df236830038d412ac2558c961
3
+ metadata.gz: c805c80e637874adae6cd80514e54a7e56bccb744ba3f3951fb1b8a575dd306f
4
+ data.tar.gz: 25721453b4d1fb59056bec90ca12363ce5f05c78fc984b5ee0c3007311d96b8c
5
5
  SHA512:
6
- metadata.gz: ef58b7272a5f641f3dc264241414d45cde6d689a34d3433b5a039dc1caeac054c435150a3b5c2532f7957efad083584fe43d4a6c8b21789c542d611c632ae080
7
- data.tar.gz: 74b33249d8141b4d869b3c8f74617e40e271c36e472b32950391a061d27069c9b108a29884acf056ce1467c4bc3c22a6f4f84e022df53153cf30ccae387c69f1
6
+ metadata.gz: ad2122baf3beb6e14861a5c1d5a8d50884ea803e6a59a6c62e72e6041b010da075d260c76e18f913cdcf30974d47ed013321f2660de204a06f8140d8cb313d87
7
+ data.tar.gz: 1462cae8c8d61310db4e6a1484c2d7526c2b69005b6fd42254dde1682c75f8e3d1d8eda1bac59811dbce8aae89ba195f0d4f808c2315906110b99e031d73369b
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,80 @@
1
+ name: Test
2
+ on:
3
+ - push
4
+
5
+ jobs:
6
+ test-darwin:
7
+ strategy:
8
+ fail-fast: false
9
+ matrix:
10
+ os:
11
+ - '10.15'
12
+ - '11.0'
13
+ platform:
14
+ - x86_64
15
+ # arm64
16
+ name: Test (darwin)
17
+ runs-on: macos-${{ matrix.os }}
18
+ steps:
19
+ - name: Checkout
20
+ uses: actions/checkout@v2
21
+ - name: Bundle
22
+ run: bundle install
23
+ - name: Compile
24
+ run: bundle exec rake compile
25
+ - name: Test
26
+ run: bundle exec rake test
27
+ test-linux:
28
+ strategy:
29
+ fail-fast: false
30
+ matrix:
31
+ ruby:
32
+ - '2.4'
33
+ - '2.5'
34
+ - '2.6'
35
+ - '2.7'
36
+ - '3.0'
37
+ platform:
38
+ - amd64
39
+ - arm64
40
+ # arm
41
+ # ppc64le
42
+ # s390x
43
+ libc:
44
+ - gnu
45
+ - musl
46
+ name: Test (linux)
47
+ runs-on: ubuntu-20.04
48
+ steps:
49
+ - name: Enable ${{ matrix.platform }} platform
50
+ id: qemu
51
+ if: ${{ matrix.platform != 'amd64' }}
52
+ run: |
53
+ docker run --privileged --rm tonistiigi/binfmt:latest --install ${{ matrix.platform }} | tee platforms.json
54
+ echo "::set-output name=platforms::$(cat platforms.json)"
55
+ - name: Start container
56
+ id: container
57
+ run: |
58
+ case ${{ matrix.libc }} in
59
+ gnu)
60
+ echo 'ruby:${{ matrix.ruby }}'
61
+ ;;
62
+ musl)
63
+ echo 'ruby:${{ matrix.ruby }}-alpine'
64
+ ;;
65
+ esac > container_image
66
+ echo "::set-output name=image::$(cat container_image)"
67
+ docker run --rm -d -v "${PWD}":"${PWD}" -w "${PWD}" --platform linux/${{ matrix.platform }} $(cat container_image) /bin/sleep 64d | tee container_id
68
+ docker exec -w "${PWD}" $(cat container_id) uname -a
69
+ echo "::set-output name=id::$(cat container_id)"
70
+ - name: Install Alpine system dependencies
71
+ if: ${{ matrix.libc == 'musl' }}
72
+ run: docker exec -w "${PWD}" ${{ steps.container.outputs.id }} apk add --no-cache build-base linux-headers bash python2 python3 git curl tar clang binutils-gold
73
+ - name: Checkout
74
+ uses: actions/checkout@v2
75
+ - name: Bundle
76
+ run: docker exec -w "${PWD}" ${{ steps.container.outputs.id }} bundle install
77
+ - name: Compile
78
+ run: docker exec -w "${PWD}" ${{ steps.container.outputs.id }} bundle exec rake compile
79
+ - name: Test
80
+ run: docker exec -w "${PWD}" ${{ steps.container.outputs.id }} bundle exec rake test
data/.gitignore CHANGED
@@ -8,4 +8,5 @@ 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
data/.travis.yml CHANGED
@@ -1,23 +1,30 @@
1
1
  language: ruby
2
+ os: linux
2
3
  rvm:
3
4
  - 2.4
4
5
  - 2.5
5
6
  - 2.6
6
7
  - 2.7
8
+ - 3.0
7
9
  - ruby-head
8
- matrix:
10
+ arch:
11
+ - amd64
12
+ - arm64
13
+ jobs:
9
14
  include:
10
- - rvm: 2.5.1
15
+ - rvm: 2.5
11
16
  os: osx
12
17
  osx_image: xcode9.4
13
- - rvm: 2.4.0
18
+ - rvm: 2.6
14
19
  os: osx
15
- osx_image: xcode8.3
16
- - rvm: 2.4.0
20
+ osx_image: xcode11.3
21
+ - rvm: 2.6
17
22
  os: osx
18
- osx_image: xcode7.3
19
- dist: trusty
20
- sudo: true
23
+ osx_image: xcode12.2
24
+ - rvm: 2.7
25
+ os: osx
26
+ osx_image: xcode12.2
27
+ dist: xenial
21
28
  before_install:
22
29
  - gem update --system
23
30
  - gem install bundler -v 1.16.2
data/CHANGELOG CHANGED
@@ -1,6 +1,39 @@
1
+ - 08-04-2021
2
+
3
+ - 0.4.0.beta1
4
+
5
+ - FIX: on downgrade mkmf was picking the wrong version of libv8, this fix will correct future issues
6
+ - FEATURE: upgraded libv8 to use node libv8 build which supports M1 and various ARM builds v8 moved to (8.6.395.17)
7
+
8
+ - 23-07-2020
9
+
10
+ - 0.3.1
11
+
12
+ - FIX: specify that libv8 must be larger than 8.4.255 but smaller than 8.5, this avoids issues going forward
13
+
14
+ - 22-07-2020
15
+
16
+ - 0.3.0
17
+
18
+ - FEATURE: upgraded to libv8 version 8.4.255.0
19
+
20
+ - 29-06-2020
21
+
22
+ - 0.2.15
23
+
24
+ - FEATURE: basic wasm support via pump_message_loop
25
+
26
+ - 15-05-2020
27
+
28
+ - 0.2.14
29
+
30
+ - FIX: ensure_gc_after_idle should take in milliseconds like the rest of the APIs not seconds
31
+ - FEATURE: strict params on MiniRacer::Context.new
32
+
1
33
  - 15-05-2020
2
34
 
3
35
  - 0.2.13
36
+
4
37
  - FIX: edge case around ensure_gc_after_idle possibly firing when context is not idle
5
38
 
6
39
  - 15-05-2020
data/Dockerfile ADDED
@@ -0,0 +1,21 @@
1
+ ARG RUBY_VERSION=2.7
2
+ FROM ruby:${RUBY_VERSION}
3
+
4
+ RUN test ! -f /etc/alpine-release || apk add --no-cache build-base git
5
+
6
+ # without this `COPY .git`, we get the following error:
7
+ # fatal: not a git repository (or any of the parent directories): .git
8
+ # but with it we need the full gem just to compile the extension because
9
+ # of gemspec's `git --ls-files`
10
+ # COPY .git /code/.git
11
+ COPY Gemfile mini_racer.gemspec /code/
12
+ COPY lib/mini_racer/version.rb /code/lib/mini_racer/version.rb
13
+ WORKDIR /code
14
+ RUN bundle install
15
+
16
+ COPY Rakefile /code/
17
+ COPY ext /code/ext/
18
+ RUN bundle exec rake compile
19
+
20
+ COPY . /code/
21
+ CMD bundle exec irb -rmini_racer
data/README.md CHANGED
@@ -239,7 +239,7 @@ This can come in handy to force V8 GC runs for example in between requests if yo
239
239
 
240
240
  Note that this method maps directly to [`v8::Isolate::IdleNotification`](http://bespin.cz/~ondras/html/classv8_1_1Isolate.html#aea16cbb2e351de9a3ae7be2b7cb48297), and that in particular its return value is the same (true if there is no further garbage to collect, false otherwise) and the same caveats apply, in particular that `there is no guarantee that the [call will return] within the time limit.`
241
241
 
242
- Additionally you may automate this process on a context by defining it with `MiniRacer::Content.new(ensure_gc_after_idle: 1)`. Using this will ensure V8 will run a full GC using `context.isolate.low_memory_notification` 1 second after the last eval on the context. Low memory notification is both slower and more aggressive than an idle_notification and will ensure long living isolates use minimal amounts of memory.
242
+ Additionally you may automate this process on a context by defining it with `MiniRacer::Content.new(ensure_gc_after_idle: 1000)`. Using this will ensure V8 will run a full GC using `context.isolate.low_memory_notification` 1 second after the last eval on the context. Low memory notification is both slower and more aggressive than an idle_notification and will ensure long living isolates use minimal amounts of memory.
243
243
 
244
244
  ### V8 Runtime flags
245
245
 
@@ -249,7 +249,7 @@ It is possible to set V8 Runtime flags:
249
249
  MiniRacer::Platform.set_flags! :noconcurrent_recompilation, max_inlining_levels: 10
250
250
  ```
251
251
 
252
- This can come in handy if you want to use MiniRacer with Unicorn, which doesn't seem to alwatys appreciate V8's liberal use of threading:
252
+ This can come in handy if you want to use MiniRacer with Unicorn, which doesn't seem to always appreciate V8's liberal use of threading:
253
253
  ```ruby
254
254
  MiniRacer::Platform.set_flags! :noconcurrent_recompilation, :noconcurrent_sweeping
255
255
  ```
@@ -315,6 +315,16 @@ context.eval("a = 2")
315
315
  # nothing works on the context from now on, its a shell waiting to be disposed
316
316
  ```
317
317
 
318
+ A MiniRacer context can also be dumped in a heapsnapshot file using `#write_heap_snapshot(file_or_io)`
319
+
320
+ ```ruby
321
+ context = MiniRacer::Context.new(timeout: 5)
322
+ context.eval("let a='testing';")
323
+ context.write_heap_snapshot("test.heapsnapshot")
324
+ ```
325
+
326
+ This file can then be loaded in the memory tab of the chrome dev console.
327
+
318
328
  ### Function call
319
329
 
320
330
  This calls the function passed as first argument:
data/Rakefile CHANGED
@@ -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
 
@@ -11,11 +13,17 @@ $CPPFLAGS += " -rdynamic" unless $CPPFLAGS.split.include? "-rdynamic"
11
13
  $CPPFLAGS += " -fPIC" unless $CPPFLAGS.split.include? "-rdynamic" or IS_DARWIN
12
14
  $CPPFLAGS += " -std=c++0x"
13
15
  $CPPFLAGS += " -fpermissive"
16
+ $CPPFLAGS += " -DV8_COMPRESS_POINTERS"
17
+ $CPPFLAGS += " -fvisibility=hidden "
14
18
 
15
19
  $CPPFLAGS += " -Wno-reserved-user-defined-literal" if IS_DARWIN
16
20
 
17
21
  $LDFLAGS.insert(0, " -stdlib=libc++ ") if IS_DARWIN
18
22
 
23
+ # check for missing symbols at link time
24
+ # $LDFLAGS += " -Wl,--no-undefined " unless IS_DARWIN
25
+ # $LDFLAGS += " -Wl,-undefined,error " if IS_DARWIN
26
+
19
27
  if ENV['CXX']
20
28
  puts "SETTING CXX"
21
29
  CONFIG['CXX'] = ENV['CXX']
@@ -53,7 +61,7 @@ if enable_config('debug') || enable_config('asan')
53
61
  CONFIG['debugflags'] << ' -ggdb3 -O0'
54
62
  end
55
63
 
56
- Libv8.configure_makefile
64
+ Libv8::Node.configure_makefile
57
65
 
58
66
  if enable_config('asan')
59
67
  $CPPFLAGS.insert(0, " -fsanitize=address ")
@@ -25,6 +25,7 @@ public:
25
25
  ArrayBuffer::Allocator* allocator;
26
26
  StartupData* startup_data;
27
27
  bool interrupted;
28
+ bool added_gc_cb;
28
29
  pid_t pid;
29
30
  VALUE mutex;
30
31
 
@@ -42,15 +43,12 @@ public:
42
43
 
43
44
 
44
45
  IsolateInfo() : isolate(nullptr), allocator(nullptr), startup_data(nullptr),
45
- interrupted(false), pid(getpid()), refs_count(0) {
46
+ interrupted(false), added_gc_cb(false), pid(getpid()), refs_count(0) {
46
47
  VALUE cMutex = rb_const_get(rb_cThread, rb_intern("Mutex"));
47
48
  mutex = rb_class_new_instance(0, nullptr, cMutex);
48
49
  }
49
50
 
50
- ~IsolateInfo() {
51
- void free_isolate(IsolateInfo*);
52
- free_isolate(this);
53
- }
51
+ ~IsolateInfo();
54
52
 
55
53
  void init(SnapshotInfo* snapshot_info = nullptr);
56
54
 
@@ -158,7 +156,8 @@ static std::mutex platform_lock;
158
156
 
159
157
  static pthread_attr_t *thread_attr_p;
160
158
  static pthread_rwlock_t exit_lock = PTHREAD_RWLOCK_INITIALIZER;
161
- static bool ruby_exiting; // guarded by exit_lock
159
+ static bool ruby_exiting = false; // guarded by exit_lock
160
+ static bool single_threaded = false;
162
161
 
163
162
  static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
164
163
  bool platform_already_initialized = false;
@@ -171,6 +170,9 @@ static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
171
170
  platform_lock.lock();
172
171
 
173
172
  if (current_platform == NULL) {
173
+ if (!strcmp(RSTRING_PTR(flag_as_str), "--single_threaded")) {
174
+ single_threaded = true;
175
+ }
174
176
  V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), (int)RSTRING_LEN(flag_as_str));
175
177
  } else {
176
178
  platform_already_initialized = true;
@@ -237,11 +239,13 @@ static void prepare_result(MaybeLocal<Value> v8res,
237
239
  Local<Value> local_value = v8res.ToLocalChecked();
238
240
  if ((local_value->IsObject() || local_value->IsArray()) &&
239
241
  !local_value->IsDate() && !local_value->IsFunction()) {
240
- Local<Object> JSON = context->Global()->Get(String::NewFromUtf8(isolate, "JSON"))
241
- ->ToObject(context).ToLocalChecked();
242
+ Local<Object> JSON = context->Global()->Get(
243
+ context, String::NewFromUtf8Literal(isolate, "JSON"))
244
+ .ToLocalChecked().As<Object>();
242
245
 
243
- Local<Function> stringify = JSON->Get(v8::String::NewFromUtf8(isolate, "stringify"))
244
- .As<Function>();
246
+ Local<Function> stringify = JSON->Get(
247
+ context, v8::String::NewFromUtf8Literal(isolate, "stringify"))
248
+ .ToLocalChecked().As<Function>();
245
249
 
246
250
  Local<Object> object = local_value->ToObject(context).ToLocalChecked();
247
251
  const unsigned argc = 1;
@@ -295,7 +299,7 @@ static void prepare_result(MaybeLocal<Value> v8res,
295
299
  } else if(trycatch.HasTerminated()) {
296
300
  evalRes.terminated = true;
297
301
  evalRes.message = new Persistent<Value>();
298
- Local<String> tmp = String::NewFromUtf8(isolate, "JavaScript was terminated (either by timeout or explicitly)");
302
+ Local<String> tmp = String::NewFromUtf8Literal(isolate, "JavaScript was terminated (either by timeout or explicitly)");
299
303
  evalRes.message->Reset(isolate, tmp);
300
304
  }
301
305
  if (!trycatch.StackTrace(context).IsEmpty()) {
@@ -312,7 +316,8 @@ nogvl_context_eval(void* arg) {
312
316
 
313
317
  EvalParams* eval_params = (EvalParams*)arg;
314
318
  EvalResult* result = eval_params->result;
315
- Isolate* isolate = eval_params->context_info->isolate_info->isolate;
319
+ IsolateInfo* isolate_info = eval_params->context_info->isolate_info;
320
+ Isolate* isolate = isolate_info->isolate;
316
321
 
317
322
  Isolate::Scope isolate_scope(isolate);
318
323
  HandleScope handle_scope(isolate);
@@ -356,7 +361,10 @@ nogvl_context_eval(void* arg) {
356
361
  // parsing successful
357
362
  if (eval_params->max_memory > 0) {
358
363
  isolate->SetData(MEM_SOFTLIMIT_VALUE, &eval_params->max_memory);
364
+ if (!isolate_info->added_gc_cb) {
359
365
  isolate->AddGCEpilogueCallback(gc_callback);
366
+ isolate_info->added_gc_cb = true;
367
+ }
360
368
  }
361
369
 
362
370
  maybe_value = parsed_script.ToLocalChecked()->Run(context);
@@ -369,6 +377,12 @@ nogvl_context_eval(void* arg) {
369
377
  return NULL;
370
378
  }
371
379
 
380
+ static VALUE new_empty_failed_conv_obj() {
381
+ // TODO isolate code that translates execption to ruby
382
+ // exception so we can properly return it
383
+ return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
384
+ }
385
+
372
386
  // assumes isolate locking is in place
373
387
  static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
374
388
  Local<Value> value) {
@@ -400,8 +414,11 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
400
414
  VALUE rb_array = rb_ary_new();
401
415
  Local<Array> arr = Local<Array>::Cast(value);
402
416
  for(uint32_t i=0; i < arr->Length(); i++) {
403
- Local<Value> element = arr->Get(i);
404
- VALUE rb_elem = convert_v8_to_ruby(isolate, context, element);
417
+ MaybeLocal<Value> element = arr->Get(context, i);
418
+ if (element.IsEmpty()) {
419
+ continue;
420
+ }
421
+ VALUE rb_elem = convert_v8_to_ruby(isolate, context, element.ToLocalChecked());
405
422
  if (rb_funcall(rb_elem, rb_intern("class"), 0) == rb_cFailedV8Conversion) {
406
423
  return rb_elem;
407
424
  }
@@ -431,18 +448,20 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
431
448
  if (!maybe_props.IsEmpty()) {
432
449
  Local<Array> props = maybe_props.ToLocalChecked();
433
450
  for(uint32_t i=0; i < props->Length(); i++) {
434
- Local<Value> key = props->Get(i);
435
- VALUE rb_key = convert_v8_to_ruby(isolate, context, key);
436
- Local<Value> prop_value = object->Get(key);
437
- // this may have failed due to Get raising
451
+ MaybeLocal<Value> key = props->Get(context, i);
452
+ if (key.IsEmpty()) {
453
+ return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
454
+ }
455
+ VALUE rb_key = convert_v8_to_ruby(isolate, context, key.ToLocalChecked());
438
456
 
439
- if (trycatch.HasCaught()) {
440
- // TODO isolate code that translates execption to ruby
441
- // exception so we can properly return it
442
- return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
457
+ MaybeLocal<Value> prop_value = object->Get(context, key.ToLocalChecked());
458
+ // this may have failed due to Get raising
459
+ if (prop_value.IsEmpty() || trycatch.HasCaught()) {
460
+ return new_empty_failed_conv_obj();
443
461
  }
444
462
 
445
- VALUE rb_value = convert_v8_to_ruby(isolate, context, prop_value);
463
+ VALUE rb_value = convert_v8_to_ruby(
464
+ isolate, context, prop_value.ToLocalChecked());
446
465
  rb_hash_aset(rb_hash, rb_key, rb_value);
447
466
  }
448
467
  }
@@ -524,7 +543,7 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
524
543
  length = RARRAY_LEN(value);
525
544
  array = Array::New(isolate, (int)length);
526
545
  for(i=0; i<length; i++) {
527
- array->Set(i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
546
+ array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
528
547
  }
529
548
  return scope.Escape(array);
530
549
  case T_HASH:
@@ -533,7 +552,7 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
533
552
  length = RARRAY_LEN(hash_as_array);
534
553
  for(i=0; i<length; i++) {
535
554
  pair = rb_ary_entry(hash_as_array, i);
536
- object->Set(convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
555
+ object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
537
556
  convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 1)));
538
557
  }
539
558
  return scope.Escape(object);
@@ -563,9 +582,9 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context,
563
582
  case T_UNDEF:
564
583
  case T_NODE:
565
584
  default:
566
- return scope.Escape(String::NewFromUtf8(isolate, "Undefined Conversion"));
585
+ return scope.Escape(String::NewFromUtf8Literal(isolate, "Undefined Conversion"));
586
+ }
567
587
  }
568
- }
569
588
 
570
589
  static void unblock_eval(void *ptr) {
571
590
  EvalParams* eval = (EvalParams*)ptr;
@@ -576,53 +595,43 @@ static void unblock_eval(void *ptr) {
576
595
  * The implementations of the run_extra_code(), create_snapshot_data_blob() and
577
596
  * warm_up_snapshot_data_blob() functions have been derived from V8's test suite.
578
597
  */
579
- bool run_extra_code(Isolate *isolate, Local<v8::Context> context,
598
+ static bool run_extra_code(Isolate *isolate, Local<v8::Context> context,
580
599
  const char *utf8_source, const char *name) {
581
600
  Context::Scope context_scope(context);
582
601
  TryCatch try_catch(isolate);
583
602
  Local<String> source_string;
584
- if (!String::NewFromUtf8(isolate, utf8_source,
585
- NewStringType::kNormal)
586
- .ToLocal(&source_string)) {
603
+ if (!String::NewFromUtf8(isolate, utf8_source).ToLocal(&source_string)) {
587
604
  return false;
588
605
  }
589
- Local<v8::String> resource_name =
590
- String::NewFromUtf8(isolate, name, NewStringType::kNormal)
591
- .ToLocalChecked();
606
+ Local<String> resource_name =
607
+ String::NewFromUtf8(isolate, name).ToLocalChecked();
592
608
  ScriptOrigin origin(resource_name);
593
609
  ScriptCompiler::Source source(source_string, origin);
594
610
  Local<Script> script;
595
611
  if (!ScriptCompiler::Compile(context, &source).ToLocal(&script))
596
612
  return false;
597
- if (script->Run(context).IsEmpty())
598
- return false;
599
- // CHECK(!try_catch.HasCaught());
613
+ if (script->Run(context).IsEmpty()) return false;
600
614
  return true;
601
615
  }
602
616
 
603
- StartupData
617
+ static StartupData
604
618
  create_snapshot_data_blob(const char *embedded_source = nullptr) {
605
- // Create a new isolate and a new context from scratch, optionally run
606
- // a script to embed, and serialize to create a snapshot blob.
607
- StartupData result = {nullptr, 0};
608
- {
609
- SnapshotCreator snapshot_creator;
610
- Isolate *isolate = snapshot_creator.GetIsolate();
619
+ Isolate *isolate = Isolate::Allocate();
620
+
621
+ // Optionally run a script to embed, and serialize to create a snapshot blob.
622
+ SnapshotCreator snapshot_creator(isolate);
611
623
  {
612
624
  HandleScope scope(isolate);
613
- Local<Context> context = Context::New(isolate);
625
+ Local<v8::Context> context = v8::Context::New(isolate);
614
626
  if (embedded_source != nullptr &&
615
- !run_extra_code(isolate, context, embedded_source,
616
- "<embedded>")) {
617
- return result;
627
+ !run_extra_code(isolate, context, embedded_source, "<embedded>")) {
628
+ return {};
618
629
  }
619
630
  snapshot_creator.SetDefaultContext(context);
620
631
  }
621
- result = snapshot_creator.CreateBlob(
632
+ return snapshot_creator.CreateBlob(
622
633
  SnapshotCreator::FunctionCodeHandling::kClear);
623
634
  }
624
- return result;
625
- }
626
635
 
627
636
  StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
628
637
  const char *warmup_source) {
@@ -779,6 +788,19 @@ static VALUE rb_isolate_low_memory_notification(VALUE self) {
779
788
  return Qnil;
780
789
  }
781
790
 
791
+ static VALUE rb_isolate_pump_message_loop(VALUE self) {
792
+ IsolateInfo* isolate_info;
793
+ Data_Get_Struct(self, IsolateInfo, isolate_info);
794
+
795
+ if (current_platform == NULL) return Qfalse;
796
+
797
+ if (platform::PumpMessageLoop(current_platform.get(), isolate_info->isolate)){
798
+ return Qtrue;
799
+ } else {
800
+ return Qfalse;
801
+ }
802
+ }
803
+
782
804
  static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
783
805
  ContextInfo* context_info;
784
806
  Data_Get_Struct(self, ContextInfo, context_info);
@@ -1049,7 +1071,9 @@ gvl_ruby_callback(void* data) {
1049
1071
  callback_data.failed = false;
1050
1072
 
1051
1073
  if ((bool)args->GetIsolate()->GetData(DO_TERMINATE) == true) {
1052
- args->GetIsolate()->ThrowException(String::NewFromUtf8(args->GetIsolate(), "Terminated execution during transition from Ruby to JS"));
1074
+ args->GetIsolate()->ThrowException(
1075
+ String::NewFromUtf8Literal(args->GetIsolate(),
1076
+ "Terminated execution during transition from Ruby to JS"));
1053
1077
  args->GetIsolate()->TerminateExecution();
1054
1078
  if (length > 0) {
1055
1079
  rb_ary_clear(ruby_args);
@@ -1063,7 +1087,7 @@ gvl_ruby_callback(void* data) {
1063
1087
 
1064
1088
  if(callback_data.failed) {
1065
1089
  rb_iv_set(parent, "@current_exception", result);
1066
- args->GetIsolate()->ThrowException(String::NewFromUtf8(args->GetIsolate(), "Ruby exception"));
1090
+ args->GetIsolate()->ThrowException(String::NewFromUtf8Literal(args->GetIsolate(), "Ruby exception"));
1067
1091
  }
1068
1092
  else {
1069
1093
  HandleScope scope(args->GetIsolate());
@@ -1134,7 +1158,9 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1134
1158
 
1135
1159
  if (parent_object == Qnil) {
1136
1160
  context->Global()->Set(
1137
- v8_str, FunctionTemplate::New(isolate, ruby_callback, external)
1161
+ context,
1162
+ v8_str,
1163
+ FunctionTemplate::New(isolate, ruby_callback, external)
1138
1164
  ->GetFunction(context)
1139
1165
  .ToLocalChecked());
1140
1166
 
@@ -1147,7 +1173,7 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1147
1173
 
1148
1174
  MaybeLocal<Script> parsed_script = Script::Compile(context, eval);
1149
1175
  if (parsed_script.IsEmpty()) {
1150
- parse_error = true;
1176
+ parse_error = true;
1151
1177
  } else {
1152
1178
  MaybeLocal<Value> maybe_value =
1153
1179
  parsed_script.ToLocalChecked()->Run(context);
@@ -1157,11 +1183,12 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
1157
1183
  Local<Value> value = maybe_value.ToLocalChecked();
1158
1184
  if (value->IsObject()) {
1159
1185
  value.As<Object>()->Set(
1160
- v8_str, FunctionTemplate::New(
1161
- isolate, ruby_callback, external)
1186
+ context,
1187
+ v8_str,
1188
+ FunctionTemplate::New(isolate, ruby_callback, external)
1162
1189
  ->GetFunction(context)
1163
1190
  .ToLocalChecked());
1164
- attach_error = false;
1191
+ attach_error = false;
1165
1192
  }
1166
1193
  }
1167
1194
  }
@@ -1191,32 +1218,36 @@ static VALUE rb_context_isolate_mutex(VALUE self) {
1191
1218
  return context_info->isolate_info->mutex;
1192
1219
  }
1193
1220
 
1194
- void free_isolate(IsolateInfo* isolate_info) {
1195
-
1196
- if (isolate_info->isolate) {
1197
- Locker lock(isolate_info->isolate);
1198
- }
1199
-
1200
- if (isolate_info->isolate) {
1201
- if (isolate_info->interrupted) {
1202
- fprintf(stderr, "WARNING: V8 isolate was interrupted by Ruby, it can not be disposed and memory will not be reclaimed till the Ruby process exits.\n");
1221
+ IsolateInfo::~IsolateInfo() {
1222
+ if (isolate) {
1223
+ if (this->interrupted) {
1224
+ fprintf(stderr, "WARNING: V8 isolate was interrupted by Ruby, "
1225
+ "it can not be disposed and memory will not be "
1226
+ "reclaimed till the Ruby process exits.\n");
1203
1227
  } else {
1204
-
1205
- if (isolate_info->pid != getpid()) {
1206
- fprintf(stderr, "WARNING: V8 isolate was forked, it can not be disposed and memory will not be reclaimed till the Ruby process exits.\n");
1228
+ if (this->pid != getpid() && !single_threaded) {
1229
+ fprintf(stderr, "WARNING: V8 isolate was forked, "
1230
+ "it can not be disposed and "
1231
+ "memory will not be reclaimed "
1232
+ "till the Ruby process exits.\n"
1233
+ "It is VERY likely your process will hang.\n"
1234
+ "If you wish to use v8 in forked environment "
1235
+ "please ensure the platform is initialized with:\n"
1236
+ "MiniRacer::Platform.set_flags! :single_threaded\n"
1237
+ );
1207
1238
  } else {
1208
- isolate_info->isolate->Dispose();
1239
+ isolate->Dispose();
1209
1240
  }
1210
1241
  }
1211
- isolate_info->isolate = NULL;
1242
+ isolate = nullptr;
1212
1243
  }
1213
1244
 
1214
- if (isolate_info->startup_data) {
1215
- delete[] isolate_info->startup_data->data;
1216
- delete isolate_info->startup_data;
1245
+ if (startup_data) {
1246
+ delete[] startup_data->data;
1247
+ delete startup_data;
1217
1248
  }
1218
1249
 
1219
- delete isolate_info->allocator;
1250
+ delete allocator;
1220
1251
  }
1221
1252
 
1222
1253
  static void free_context_raw(void *arg) {
@@ -1288,7 +1319,7 @@ static void mark_isolate(void* data) {
1288
1319
  isolate_info->mark();
1289
1320
  }
1290
1321
 
1291
- void deallocate(void* data) {
1322
+ static void deallocate(void* data) {
1292
1323
  ContextInfo* context_info = (ContextInfo*)data;
1293
1324
 
1294
1325
  free_context(context_info);
@@ -1303,22 +1334,22 @@ static void mark_context(void* data) {
1303
1334
  }
1304
1335
  }
1305
1336
 
1306
- void deallocate_external_function(void * data) {
1337
+ static void deallocate_external_function(void * data) {
1307
1338
  xfree(data);
1308
1339
  }
1309
1340
 
1310
- void deallocate_snapshot(void * data) {
1341
+ static void deallocate_snapshot(void * data) {
1311
1342
  SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
1312
1343
  delete[] snapshot_info->data;
1313
1344
  xfree(snapshot_info);
1314
1345
  }
1315
1346
 
1316
- VALUE allocate_external_function(VALUE klass) {
1347
+ static VALUE allocate_external_function(VALUE klass) {
1317
1348
  VALUE* self = ALLOC(VALUE);
1318
1349
  return Data_Wrap_Struct(klass, NULL, deallocate_external_function, (void*)self);
1319
1350
  }
1320
1351
 
1321
- VALUE allocate(VALUE klass) {
1352
+ static VALUE allocate(VALUE klass) {
1322
1353
  ContextInfo* context_info = ALLOC(ContextInfo);
1323
1354
  context_info->isolate_info = NULL;
1324
1355
  context_info->context = NULL;
@@ -1326,7 +1357,7 @@ VALUE allocate(VALUE klass) {
1326
1357
  return Data_Wrap_Struct(klass, mark_context, deallocate, (void*)context_info);
1327
1358
  }
1328
1359
 
1329
- VALUE allocate_snapshot(VALUE klass) {
1360
+ static VALUE allocate_snapshot(VALUE klass) {
1330
1361
  SnapshotInfo* snapshot_info = ALLOC(SnapshotInfo);
1331
1362
  snapshot_info->data = NULL;
1332
1363
  snapshot_info->raw_size = 0;
@@ -1334,7 +1365,7 @@ VALUE allocate_snapshot(VALUE klass) {
1334
1365
  return Data_Wrap_Struct(klass, NULL, deallocate_snapshot, (void*)snapshot_info);
1335
1366
  }
1336
1367
 
1337
- VALUE allocate_isolate(VALUE klass) {
1368
+ static VALUE allocate_isolate(VALUE klass) {
1338
1369
  IsolateInfo* isolate_info = new IsolateInfo();
1339
1370
 
1340
1371
  return Data_Wrap_Struct(klass, mark_isolate, deallocate_isolate, (void*)isolate_info);
@@ -1473,7 +1504,8 @@ nogvl_context_call(void *args) {
1473
1504
  if (!call) {
1474
1505
  return NULL;
1475
1506
  }
1476
- Isolate* isolate = call->context_info->isolate_info->isolate;
1507
+ IsolateInfo *isolate_info = call->context_info->isolate_info;
1508
+ Isolate* isolate = isolate_info->isolate;
1477
1509
 
1478
1510
  // in gvl flag
1479
1511
  isolate->SetData(IN_GVL, (void*)false);
@@ -1483,7 +1515,10 @@ nogvl_context_call(void *args) {
1483
1515
  if (call->max_memory > 0) {
1484
1516
  isolate->SetData(MEM_SOFTLIMIT_VALUE, &call->max_memory);
1485
1517
  isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)false);
1518
+ if (!isolate_info->added_gc_cb) {
1486
1519
  isolate->AddGCEpilogueCallback(gc_callback);
1520
+ isolate_info->added_gc_cb = true;
1521
+ }
1487
1522
  }
1488
1523
 
1489
1524
  Isolate::Scope isolate_scope(isolate);
@@ -1561,8 +1596,11 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1561
1596
 
1562
1597
  // examples of such usage can be found in
1563
1598
  // https://github.com/v8/v8/blob/36b32aa28db5e993312f4588d60aad5c8330c8a5/test/cctest/test-api.cc#L15711
1564
- Local<String> fname = String::NewFromUtf8(isolate, call.function_name);
1565
- MaybeLocal<v8::Value> val = context->Global()->Get(fname);
1599
+ MaybeLocal<String> fname = String::NewFromUtf8(isolate, call.function_name);
1600
+ MaybeLocal<v8::Value> val;
1601
+ if (!fname.IsEmpty()) {
1602
+ val = context->Global()->Get(context, fname.ToLocalChecked());
1603
+ }
1566
1604
 
1567
1605
  if (val.IsEmpty() || !val.ToLocalChecked()->IsFunction()) {
1568
1606
  missingFunction = true;
@@ -1611,6 +1649,7 @@ static void set_ruby_exiting(VALUE value) {
1611
1649
  (void)value;
1612
1650
 
1613
1651
  int res = pthread_rwlock_wrlock(&exit_lock);
1652
+
1614
1653
  ruby_exiting = true;
1615
1654
  if (res == 0) {
1616
1655
  pthread_rwlock_unlock(&exit_lock);
@@ -1619,7 +1658,7 @@ static void set_ruby_exiting(VALUE value) {
1619
1658
 
1620
1659
  extern "C" {
1621
1660
 
1622
- void Init_mini_racer_extension ( void )
1661
+ __attribute__((visibility("default"))) void Init_mini_racer_extension ( void )
1623
1662
  {
1624
1663
  VALUE rb_mMiniRacer = rb_define_module("MiniRacer");
1625
1664
  rb_cContext = rb_define_class_under(rb_mMiniRacer, "Context", rb_cObject);
@@ -1668,7 +1707,7 @@ extern "C" {
1668
1707
 
1669
1708
  rb_define_method(rb_cIsolate, "idle_notification", (VALUE(*)(...))&rb_isolate_idle_notification, 1);
1670
1709
  rb_define_method(rb_cIsolate, "low_memory_notification", (VALUE(*)(...))&rb_isolate_low_memory_notification, 0);
1671
-
1710
+ rb_define_method(rb_cIsolate, "pump_message_loop", (VALUE(*)(...))&rb_isolate_pump_message_loop, 0);
1672
1711
  rb_define_private_method(rb_cIsolate, "init_with_snapshot",(VALUE(*)(...))&rb_isolate_init_with_snapshot, 1);
1673
1712
 
1674
1713
  rb_define_singleton_method(rb_cPlatform, "set_flag_as_str!", (VALUE(*)(...))&rb_platform_set_flag_as_str, 1);
@@ -1681,5 +1720,9 @@ extern "C" {
1681
1720
  thread_attr_p = &attr;
1682
1721
  }
1683
1722
  }
1723
+ auto on_fork_for_child = []() {
1724
+ exit_lock = PTHREAD_RWLOCK_INITIALIZER;
1725
+ };
1726
+ pthread_atfork(nullptr, nullptr, on_fork_for_child);
1684
1727
  }
1685
1728
  }
@@ -0,0 +1,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
+ }
data/lib/mini_racer.rb CHANGED
@@ -1,5 +1,15 @@
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
 
@@ -130,23 +140,22 @@ module MiniRacer
130
140
  end
131
141
  end
132
142
 
133
- def initialize(options = nil)
143
+ def initialize(max_memory: nil, timeout: nil, isolate: nil, ensure_gc_after_idle: nil, snapshot: nil)
134
144
  options ||= {}
135
145
 
136
- check_init_options!(options)
146
+ check_init_options!(isolate: isolate, snapshot: snapshot, max_memory: max_memory, ensure_gc_after_idle: ensure_gc_after_idle, timeout: timeout)
137
147
 
138
148
  @functions = {}
139
149
  @timeout = nil
140
150
  @max_memory = nil
141
151
  @current_exception = nil
142
- @timeout = options[:timeout]
143
- if options[:max_memory].is_a?(Numeric) && options[:max_memory] > 0
144
- @max_memory = options[:max_memory]
145
- end
152
+ @timeout = timeout
153
+ @max_memory = max_memory
154
+
146
155
  # false signals it should be fetched if requested
147
- @isolate = options[:isolate] || false
156
+ @isolate = isolate || false
148
157
 
149
- @ensure_gc_after_idle = options[:ensure_gc_after_idle]
158
+ @ensure_gc_after_idle = ensure_gc_after_idle
150
159
 
151
160
  if @ensure_gc_after_idle
152
161
  @last_eval = nil
@@ -162,7 +171,7 @@ module MiniRacer
162
171
  @eval_thread = nil
163
172
 
164
173
  # defined in the C class
165
- init_unsafe(options[:isolate], options[:snapshot])
174
+ init_unsafe(isolate, snapshot)
166
175
  end
167
176
 
168
177
  def isolate
@@ -290,6 +299,7 @@ module MiniRacer
290
299
  @ensure_gc_mutex.synchronize do
291
300
  @ensure_gc_thread = nil if !@ensure_gc_thread&.alive?
292
301
  @ensure_gc_thread ||= Thread.new do
302
+ ensure_gc_after_idle_seconds = @ensure_gc_after_idle / 1000.0
293
303
  done = false
294
304
  while !done
295
305
  now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
@@ -299,7 +309,7 @@ module MiniRacer
299
309
  break
300
310
  end
301
311
 
302
- if !@eval_thread && @ensure_gc_after_idle < now - @last_eval
312
+ if !@eval_thread && ensure_gc_after_idle_seconds < now - @last_eval
303
313
  @ensure_gc_mutex.synchronize do
304
314
  isolate_mutex.synchronize do
305
315
  if !@eval_thread
@@ -310,7 +320,7 @@ module MiniRacer
310
320
  end
311
321
  end
312
322
  end
313
- sleep @ensure_gc_after_idle if !done
323
+ sleep ensure_gc_after_idle_seconds if !done
314
324
  end
315
325
  end
316
326
  end
@@ -369,15 +379,29 @@ module MiniRacer
369
379
  rp.close if rp
370
380
  end
371
381
 
372
- def check_init_options!(options)
373
- assert_option_is_nil_or_a('isolate', options[:isolate], Isolate)
374
- assert_option_is_nil_or_a('snapshot', options[:snapshot], Snapshot)
382
+ def check_init_options!(isolate:, snapshot:, max_memory:, ensure_gc_after_idle:, timeout:)
383
+ assert_option_is_nil_or_a('isolate', isolate, Isolate)
384
+ assert_option_is_nil_or_a('snapshot', snapshot, Snapshot)
385
+
386
+ assert_numeric_or_nil('max_memory', max_memory, min_value: 10_000)
387
+ assert_numeric_or_nil('ensure_gc_after_idle', ensure_gc_after_idle, min_value: 1)
388
+ assert_numeric_or_nil('timeout', timeout, min_value: 1)
375
389
 
376
- if options[:isolate] && options[:snapshot]
390
+ if isolate && snapshot
377
391
  raise ArgumentError, 'can only pass one of isolate and snapshot options'
378
392
  end
379
393
  end
380
394
 
395
+ def assert_numeric_or_nil(option_name, object, min_value:)
396
+ if object.is_a?(Numeric) && object < min_value
397
+ raise ArgumentError, "#{option_name} must be larger than #{min_value}"
398
+ end
399
+
400
+ if !object.nil? && !object.is_a?(Numeric)
401
+ raise ArgumentError, "#{option_name} must be a number, passed a #{object.inspect}"
402
+ end
403
+ end
404
+
381
405
  def assert_option_is_nil_or_a(option_name, object, klass)
382
406
  unless object.nil? || object.is_a?(klass)
383
407
  raise ArgumentError, "#{option_name} must be a #{klass} object, passed a #{object.inspect}"
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MiniRacer
4
- VERSION = "0.2.13"
4
+ VERSION = "0.4.0.beta1"
5
+ LIBV8_NODE_VERSION = "~> 15.12.0.0.beta1"
5
6
  end
data/mini_racer.gemspec CHANGED
@@ -30,11 +30,12 @@ Gem::Specification.new do |spec|
30
30
  spec.add_development_dependency "rake", ">= 12.3.3"
31
31
  spec.add_development_dependency "minitest", "~> 5.0"
32
32
  spec.add_development_dependency "rake-compiler"
33
+ spec.add_development_dependency "m"
33
34
 
34
- spec.add_dependency 'libv8', '> 7.3'
35
+ spec.add_dependency 'libv8-node', MiniRacer::LIBV8_NODE_VERSION
35
36
  spec.require_paths = ["lib", "ext"]
36
37
 
37
- spec.extensions = ["ext/mini_racer_extension/extconf.rb"]
38
+ spec.extensions = ["ext/mini_racer_loader/extconf.rb", "ext/mini_racer_extension/extconf.rb"]
38
39
 
39
40
  spec.required_ruby_version = '>= 2.3'
40
41
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mini_racer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.13
4
+ version: 0.4.0.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Saffron
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-05-15 00:00:00.000000000 Z
11
+ date: 2021-04-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -67,31 +67,49 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: libv8
70
+ name: m
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ">"
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
74
81
  - !ruby/object:Gem::Version
75
- version: '7.3'
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: libv8-node
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 15.12.0.0.beta1
76
90
  type: :runtime
77
91
  prerelease: false
78
92
  version_requirements: !ruby/object:Gem::Requirement
79
93
  requirements:
80
- - - ">"
94
+ - - "~>"
81
95
  - !ruby/object:Gem::Version
82
- version: '7.3'
96
+ version: 15.12.0.0.beta1
83
97
  description: Minimal embedded v8 engine for Ruby
84
98
  email:
85
99
  - sam.saffron@gmail.com
86
100
  executables: []
87
101
  extensions:
102
+ - ext/mini_racer_loader/extconf.rb
88
103
  - ext/mini_racer_extension/extconf.rb
89
104
  extra_rdoc_files: []
90
105
  files:
106
+ - ".dockerignore"
107
+ - ".github/workflows/ci.yml"
91
108
  - ".gitignore"
92
109
  - ".travis.yml"
93
110
  - CHANGELOG
94
111
  - CODE_OF_CONDUCT.md
112
+ - Dockerfile
95
113
  - Gemfile
96
114
  - LICENSE.txt
97
115
  - README.md
@@ -100,6 +118,8 @@ files:
100
118
  - bin/setup
101
119
  - ext/mini_racer_extension/extconf.rb
102
120
  - ext/mini_racer_extension/mini_racer_extension.cc
121
+ - ext/mini_racer_loader/extconf.rb
122
+ - ext/mini_racer_loader/mini_racer_loader.c
103
123
  - lib/mini_racer.rb
104
124
  - lib/mini_racer/version.rb
105
125
  - mini_racer.gemspec
@@ -108,10 +128,10 @@ licenses:
108
128
  - MIT
109
129
  metadata:
110
130
  bug_tracker_uri: https://github.com/discourse/mini_racer/issues
111
- changelog_uri: https://github.com/discourse/mini_racer/blob/v0.2.13/CHANGELOG
112
- documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.2.13
113
- source_code_uri: https://github.com/discourse/mini_racer/tree/v0.2.13
114
- post_install_message:
131
+ changelog_uri: https://github.com/discourse/mini_racer/blob/v0.4.0.beta1/CHANGELOG
132
+ documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.4.0.beta1
133
+ source_code_uri: https://github.com/discourse/mini_racer/tree/v0.4.0.beta1
134
+ post_install_message:
115
135
  rdoc_options: []
116
136
  require_paths:
117
137
  - lib
@@ -123,12 +143,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
123
143
  version: '2.3'
124
144
  required_rubygems_version: !ruby/object:Gem::Requirement
125
145
  requirements:
126
- - - ">="
146
+ - - ">"
127
147
  - !ruby/object:Gem::Version
128
- version: '0'
148
+ version: 1.3.1
129
149
  requirements: []
130
- rubygems_version: 3.0.3
131
- signing_key:
150
+ rubygems_version: 3.2.2
151
+ signing_key:
132
152
  specification_version: 4
133
153
  summary: Minimal embedded v8 for Ruby
134
154
  test_files: []