mini_racer 0.2.13 → 0.4.0.beta1

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: 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: []