mini_racer 0.6.2 → 0.6.4

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: e832863760637f983976194ebef263d8815c149d48ff26c7200367ab33640dda
4
- data.tar.gz: 7c1c393ae6403a75d2a413395d56e7d5f3affe706b81c121273b4c6b5d471bf4
3
+ metadata.gz: 2d643f2cd1fb220fc7ac49d502671334d2e9390cd6eac47b62cdecfc39f2dd82
4
+ data.tar.gz: 2c1cbb7cb254dddefdeac0c7bd246d62e2f08dfb9df24c91680ef0e6016ea483
5
5
  SHA512:
6
- metadata.gz: 4504cb3a564e5b311b65de74bac321b058995af17df47f9f3dc01fcb589a87eebfda55497e0ddb135a2ab15ef1818760652561d434d3f497c0dbd036451934c6
7
- data.tar.gz: b83df123a61092ab4041212bb6e4f15b276d5e32e8be8d2b5cbc014988b8cf20ed0e0b3b30595de53ca4c0d599a86112d695009cda13891fe177742aff7d911e
6
+ metadata.gz: be7c745abba5a2bd5caf0e30f066601a58d00f847092bde4865d3faf7b79987af5bfe0c6cd9c2783f0ab7a7edc1e39b6a53afa489ce4e5dde0713981e26dfc51
7
+ data.tar.gz: e6128f7619cdb105b04f5a7abb0ed2c0964a2b2b353c3a4eecc79dfa3d329d88695f9885c7250d2d6615ff9bc93c22cf111d24bca54d9e57fbaa02672998879c
@@ -1,48 +1,92 @@
1
- name: Test
1
+ name: Tests
2
+
2
3
  on:
3
- - push
4
+ pull_request:
5
+ push:
6
+ branches:
7
+ - master
4
8
 
5
9
  jobs:
10
+ test-truffleruby:
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ os:
15
+ - "macos-11"
16
+ - "macos-12"
17
+ - "ubuntu-20.04"
18
+ ruby:
19
+ - "truffleruby+graalvm-head"
20
+
21
+ name: ${{ matrix.os }} - ${{ matrix.ruby }}
22
+ runs-on: ${{ matrix.os }}
23
+
24
+ env:
25
+ TRUFFLERUBYOPT: "--jvm --polyglot"
26
+
27
+ steps:
28
+ - uses: actions/checkout@v3
29
+ - uses: ruby/setup-ruby@v1
30
+ with:
31
+ ruby-version: ${{ matrix.ruby }}
32
+ bundler-cache: true
33
+ - name: Install GraalVM JS component
34
+ run: gu install js
35
+ - name: Compile
36
+ run: bundle exec rake compile
37
+ - name: Test
38
+ run: bundle exec rake test
39
+
6
40
  test-darwin:
7
41
  strategy:
8
42
  fail-fast: false
9
43
  matrix:
10
44
  os:
11
- - '10.15'
12
- - '11.0'
13
- platform:
14
- - x86_64
15
- # arm64
16
- name: Test (darwin)
17
- runs-on: macos-${{ matrix.os }}
45
+ - "macos-11"
46
+ - "macos-12"
47
+ ruby:
48
+ - "ruby-2.6"
49
+ - "ruby-2.7"
50
+ - "ruby-3.0"
51
+ - "ruby-3.1"
52
+
53
+ name: ${{ matrix.os }} - ${{ matrix.ruby }}
54
+ runs-on: ${{ matrix.os }}
55
+
18
56
  steps:
19
- - name: Checkout
20
- uses: actions/checkout@v2
21
- - name: Bundle
22
- run: bundle install
57
+ - uses: actions/checkout@v3
58
+ - uses: ruby/setup-ruby@v1
59
+ with:
60
+ ruby-version: ${{ matrix.ruby }}
61
+ bundler-cache: true
23
62
  - name: Compile
24
63
  run: bundle exec rake compile
25
64
  - name: Test
26
65
  run: bundle exec rake test
66
+
27
67
  test-linux:
28
68
  strategy:
29
69
  fail-fast: false
30
70
  matrix:
31
71
  ruby:
32
- - '2.6'
33
- - '2.7'
34
- - '3.0'
72
+ - "2.6"
73
+ - "2.7"
74
+ - "3.0"
75
+ - "3.1"
35
76
  platform:
36
- - amd64
37
- - arm64
38
- # arm
39
- # ppc64le
40
- # s390x
77
+ - "amd64"
78
+ - "arm64"
41
79
  libc:
42
- - gnu
43
- - musl
44
- name: Test (linux)
80
+ - "gnu"
81
+ - "musl"
82
+ exclude:
83
+ # there's no libv8-node (v16) for aarch64-linux-musl at the moment
84
+ - platform: "arm64"
85
+ libc: "musl"
86
+
87
+ name: linux-${{ matrix.platform }} - ruby-${{ matrix.ruby }} - ${{ matrix.libc }}
45
88
  runs-on: ubuntu-20.04
89
+
46
90
  steps:
47
91
  - name: Enable ${{ matrix.platform }} platform
48
92
  id: qemu
@@ -67,9 +111,11 @@ jobs:
67
111
  echo "::set-output name=id::$(cat container_id)"
68
112
  - name: Install Alpine system dependencies
69
113
  if: ${{ matrix.libc == 'musl' }}
70
- run: docker exec -w "${PWD}" ${{ steps.container.outputs.id }} apk add --no-cache build-base linux-headers bash python2 python3 git curl tar clang binutils-gold
114
+ run: docker exec -w "${PWD}" ${{ steps.container.outputs.id }} apk add --no-cache build-base bash git
71
115
  - name: Checkout
72
- uses: actions/checkout@v2
116
+ uses: actions/checkout@v3
117
+ - name: Update Rubygems
118
+ run: docker exec -w "${PWD}" ${{ steps.container.outputs.id }} gem update --system
73
119
  - name: Bundle
74
120
  run: docker exec -w "${PWD}" ${{ steps.container.outputs.id }} bundle install
75
121
  - name: Compile
data/CHANGELOG CHANGED
@@ -1,4 +1,20 @@
1
- - 17-01-2021
1
+ - 25-05-2022
2
+
3
+ - 0.6.4
4
+
5
+ - Target Node 16.19.0.0
6
+
7
+ - 16-08-2022
8
+
9
+ - 0.6.3
10
+
11
+ - Truffle ruby support! Thanks to Brandon Fish and the truffle team
12
+ - Hide libv8 symbols on ELF targets
13
+ - Slightly shrunk binary size
14
+ - Simplified timeout implementation
15
+ - Some stability fixes
16
+
17
+ - 17-01-2022
2
18
 
3
19
  - 0.6.2
4
20
 
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # MiniRacer
2
2
 
3
- [![Build Status](https://travis-ci.org/rubyjs/mini_racer.svg?branch=master)](https://travis-ci.org/rubyjs/mini_racer)
3
+ [![Test](https://github.com/rubyjs/mini_racer/actions/workflows/ci.yml/badge.svg)](https://github.com/rubyjs/mini_racer/actions/workflows/ci.yml)
4
4
 
5
5
  Minimal, modern embedded V8 for Ruby.
6
6
 
@@ -10,12 +10,14 @@ It was created as an alternative to the excellent [therubyracer](https://github.
10
10
 
11
11
  MiniRacer has an adapter for [execjs](https://github.com/rails/execjs) so it can be used directly with Rails projects to minify assets, run babel or compile CoffeeScript.
12
12
 
13
- ### A note about Ruby version Support
13
+ ### Supported Ruby Versions
14
14
 
15
- MiniRacer only supports non-EOL versions of Ruby. See [Ruby](https://www.ruby-lang.org/en/downloads) to see the list of non-EOL Rubies.
15
+ MiniRacer only supports non-EOL versions of Ruby. See [Ruby Maintenance Branches](https://www.ruby-lang.org/en/downloads/branches/) for the list of non-EOL Rubies.
16
16
 
17
17
  If you require support for older versions of Ruby install an older version of the gem.
18
18
 
19
+ MiniRacer **does not support** [Ruby built on MinGW](https://github.com/rubyjs/mini_racer/issues/252#issuecomment-1201172236, "pure windows" no Cygwin, no WSL2) (see https://github.com/rubyjs/libv8-node/issues/9).
20
+
19
21
  ## Features
20
22
 
21
23
  ### Simple eval for JavaScript
@@ -112,16 +114,21 @@ context.eval('bar()', filename: 'a/bar.js')
112
114
 
113
115
  ### Fork safety
114
116
 
115
- Some Ruby web servers employ forking (for example unicorn or puma in clustered mode). V8 is not fork safe.
116
- Sadly Ruby does not have support for fork notifications per [#5446](https://bugs.ruby-lang.org/issues/5446).
117
+ Some Ruby web servers employ forking (for example unicorn or puma in clustered mode). V8 is not fork safe by default and sadly Ruby does not have support for fork notifications per [#5446](https://bugs.ruby-lang.org/issues/5446).
118
+
119
+ Since 0.6.1 mini_racer does support V8 single threaded platform mode which should remove most forking related issues. To enable run this before using `MiniRacer::Context`:
120
+
121
+ ```ruby
122
+ MiniRacer::Platform.set_flags!(:single_threaded)
123
+ ```
117
124
 
118
125
  If you want to ensure your application does not leak memory after fork either:
119
126
 
120
- 1. Ensure no MiniRacer::Context objects are created in the master process
127
+ 1. Ensure no `MiniRacer::Context` objects are created in the master process
121
128
 
122
129
  Or
123
130
 
124
- 2. Dispose manually of all MiniRacer::Context objects prior to forking
131
+ 2. Dispose manually of all `MiniRacer::Context` objects prior to forking
125
132
 
126
133
  ```ruby
127
134
  # before fork
@@ -419,18 +426,15 @@ Or install it yourself as:
419
426
  **Note** using v8.h and compiling MiniRacer requires a C++11 standard compiler, more specifically clang 3.5 (or later) or GCC 6.3 (or later).
420
427
 
421
428
 
422
- ## Travis-ci
429
+ ### Troubleshooting
423
430
 
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.
431
+ If you have a problem installing mini_racer, please consider the following steps:
425
432
 
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.
427
-
428
- Add this to your .travis.yml file:
429
-
430
- ```
431
- - sudo: required
432
- - dist: trusty
433
- ```
433
+ * make sure you try the latest released version of mini_racer
434
+ * make sure you have Rubygems >= 3.2.13 and bundler >= 2.2.13 installed via `gem update --system`
435
+ * if you are using bundler, make sure to have `PLATFORMS` set correctly in `Gemfile.lock` via `bundle lock --add-platform`
436
+ * make sure to recompile/reinstall `mini_racer` and `libv8-node` after system upgrades (for example via `gem uninstall --all mini_racer libv8-node`)
437
+ * make sure you are on the latest patch/teeny version of a supported Ruby branch
434
438
 
435
439
  ## Similar Projects
436
440
 
data/Rakefile CHANGED
@@ -1,6 +1,5 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rake/testtask"
3
- require "rake/extensiontask"
4
3
 
5
4
  Rake::TestTask.new(:test) do |t|
6
5
  t.libs << "test"
@@ -11,8 +10,21 @@ end
11
10
  task :default => [:compile, :test]
12
11
 
13
12
  gem = Gem::Specification.load( File.dirname(__FILE__) + '/mini_racer.gemspec' )
14
- Rake::ExtensionTask.new( 'mini_racer_loader', gem )
15
- Rake::ExtensionTask.new( 'mini_racer_extension', gem )
13
+
14
+ if RUBY_ENGINE == "truffleruby"
15
+ task :compile do
16
+ # noop
17
+ end
18
+
19
+ task :clean do
20
+ # noop
21
+ end
22
+ else
23
+ require 'rake/extensiontask'
24
+ Rake::ExtensionTask.new( 'mini_racer_loader', gem )
25
+ Rake::ExtensionTask.new( 'mini_racer_extension', gem )
26
+ end
27
+
16
28
 
17
29
 
18
30
  # via http://blog.flavorjon.es/2009/06/easily-valgrind-gdb-your-ruby-c.html
@@ -1,4 +1,10 @@
1
1
  require 'mkmf'
2
+
3
+ if RUBY_ENGINE == "truffleruby"
4
+ File.write("Makefile", dummy_makefile($srcdir).join(""))
5
+ return
6
+ end
7
+
2
8
  require_relative '../../lib/mini_racer/version'
3
9
  gem 'libv8-node', MiniRacer::LIBV8_NODE_VERSION
4
10
  require 'libv8-node'
@@ -70,6 +76,9 @@ end
70
76
 
71
77
  Libv8::Node.configure_makefile
72
78
 
79
+ # --exclude-libs is only for i386 PE and ELF targeted ports
80
+ append_ldflags("-Wl,--exclude-libs=ALL ")
81
+
73
82
  if enable_config('asan')
74
83
  $CXXFLAGS.insert(0, " -fsanitize=address ")
75
84
  $LDFLAGS.insert(0, " -fsanitize=address ")
@@ -305,8 +305,7 @@ static std::unique_ptr<Platform> current_platform = NULL;
305
305
  static std::mutex platform_lock;
306
306
 
307
307
  static pthread_attr_t *thread_attr_p;
308
- static pthread_rwlock_t exit_lock = PTHREAD_RWLOCK_INITIALIZER;
309
- static bool ruby_exiting = false; // guarded by exit_lock
308
+ static std::atomic_int ruby_exiting(0);
310
309
  static bool single_threaded = false;
311
310
 
312
311
  static void mark_context(void *);
@@ -335,10 +334,7 @@ static const rb_data_type_t isolate_type = {
335
334
  static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
336
335
  bool platform_already_initialized = false;
337
336
 
338
- if(TYPE(flag_as_str) != T_STRING) {
339
- rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE" (should be a string)",
340
- rb_obj_class(flag_as_str));
341
- }
337
+ Check_Type(flag_as_str, T_STRING);
342
338
 
343
339
  platform_lock.lock();
344
340
 
@@ -664,11 +660,7 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
664
660
  v8::String::Utf8Value symbol_name(isolate,
665
661
  Local<Symbol>::Cast(value)->Name());
666
662
 
667
- VALUE str_symbol = rb_enc_str_new(
668
- *symbol_name,
669
- symbol_name.length(),
670
- rb_enc_find("utf-8")
671
- );
663
+ VALUE str_symbol = rb_utf8_str_new(*symbol_name, symbol_name.length());
672
664
 
673
665
  return rb_str_intern(str_symbol);
674
666
  }
@@ -679,7 +671,7 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
679
671
  return Qnil;
680
672
  } else {
681
673
  Local<String> rstr = rstr_maybe.ToLocalChecked();
682
- return rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
674
+ return rb_utf8_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate));
683
675
  }
684
676
  }
685
677
 
@@ -861,7 +853,7 @@ StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
861
853
  return result;
862
854
  }
863
855
 
864
- static VALUE rb_snapshot_size(VALUE self, VALUE str) {
856
+ static VALUE rb_snapshot_size(VALUE self) {
865
857
  SnapshotInfo* snapshot_info;
866
858
  TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
867
859
 
@@ -872,10 +864,7 @@ static VALUE rb_snapshot_load(VALUE self, VALUE str) {
872
864
  SnapshotInfo* snapshot_info;
873
865
  TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
874
866
 
875
- if(TYPE(str) != T_STRING) {
876
- rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
877
- rb_obj_class(str));
878
- }
867
+ Check_Type(str, T_STRING);
879
868
 
880
869
  init_v8();
881
870
 
@@ -891,7 +880,7 @@ static VALUE rb_snapshot_load(VALUE self, VALUE str) {
891
880
  return Qnil;
892
881
  }
893
882
 
894
- static VALUE rb_snapshot_dump(VALUE self, VALUE str) {
883
+ static VALUE rb_snapshot_dump(VALUE self) {
895
884
  SnapshotInfo* snapshot_info;
896
885
  TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
897
886
 
@@ -902,10 +891,7 @@ static VALUE rb_snapshot_warmup_unsafe(VALUE self, VALUE str) {
902
891
  SnapshotInfo* snapshot_info;
903
892
  TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
904
893
 
905
- if(TYPE(str) != T_STRING) {
906
- rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
907
- rb_obj_class(str));
908
- }
894
+ Check_Type(str, T_STRING);
909
895
 
910
896
  init_v8();
911
897
 
@@ -1084,8 +1070,7 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
1084
1070
  // If we were terminated or have the memory softlimit flag set
1085
1071
  if (marshal_stack_maxdepth_reached) {
1086
1072
  ruby_exception = rb_eScriptRuntimeError;
1087
- std::string msg = std::string("Marshal object depth too deep. Script terminated.");
1088
- message = rb_enc_str_new(msg.c_str(), msg.length(), rb_enc_find("utf-8"));
1073
+ message = rb_utf8_str_new_literal("Marshal object depth too deep. Script terminated.");
1089
1074
  } else if (result.terminated || mem_softlimit_reached) {
1090
1075
  ruby_exception = mem_softlimit_reached ? rb_eV8OutOfMemoryError : rb_eScriptTerminatedError;
1091
1076
  } else {
@@ -1120,7 +1105,7 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
1120
1105
 
1121
1106
  if (result.json) {
1122
1107
  Local<String> rstr = tmp->ToString(p_ctx->Get(isolate)).ToLocalChecked();
1123
- VALUE json_string = rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
1108
+ VALUE json_string = rb_utf8_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate));
1124
1109
  ret = rb_funcall(rb_mJSON, rb_intern("parse"), 1, json_string);
1125
1110
  } else {
1126
1111
  StackCounter::Reset(isolate);
@@ -1149,13 +1134,10 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
1149
1134
  TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
1150
1135
  Isolate* isolate = context_info->isolate_info->isolate;
1151
1136
 
1152
- if(TYPE(str) != T_STRING) {
1153
- rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
1154
- rb_obj_class(str));
1155
- }
1156
- if(filename != Qnil && TYPE(filename) != T_STRING) {
1157
- rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be nil or a string)",
1158
- rb_obj_class(filename));
1137
+ Check_Type(str, T_STRING);
1138
+
1139
+ if (!NIL_P(filename)) {
1140
+ Check_Type(filename, T_STRING);
1159
1141
  }
1160
1142
 
1161
1143
  {
@@ -1474,42 +1456,33 @@ static void free_context_raw(void *arg) {
1474
1456
  if (isolate_info) {
1475
1457
  isolate_info->release();
1476
1458
  }
1477
-
1478
- xfree(context_info);
1479
1459
  }
1480
1460
 
1481
1461
  static void *free_context_thr(void* arg) {
1482
- if (pthread_rwlock_tryrdlock(&exit_lock) != 0) {
1483
- return NULL;
1462
+ if (ruby_exiting.load() == 0) {
1463
+ free_context_raw(arg);
1464
+ xfree(arg);
1484
1465
  }
1485
- if (ruby_exiting) {
1486
- return NULL;
1487
- }
1488
-
1489
- free_context_raw(arg);
1490
-
1491
- pthread_rwlock_unlock(&exit_lock);
1492
-
1493
1466
  return NULL;
1494
1467
  }
1495
1468
 
1496
1469
  // destroys everything except freeing the ContextInfo struct (see deallocate())
1497
1470
  static void free_context(ContextInfo* context_info) {
1498
-
1499
1471
  IsolateInfo* isolate_info = context_info->isolate_info;
1500
1472
 
1501
- ContextInfo* context_info_copy = ALLOC(ContextInfo);
1502
- context_info_copy->isolate_info = context_info->isolate_info;
1503
- context_info_copy->context = context_info->context;
1504
-
1505
1473
  if (isolate_info && isolate_info->refs() > 1) {
1506
1474
  pthread_t free_context_thread;
1475
+ ContextInfo* context_info_copy = ALLOC(ContextInfo);
1476
+
1477
+ context_info_copy->isolate_info = context_info->isolate_info;
1478
+ context_info_copy->context = context_info->context;
1507
1479
  if (pthread_create(&free_context_thread, thread_attr_p,
1508
1480
  free_context_thr, (void*)context_info_copy)) {
1509
1481
  fprintf(stderr, "WARNING failed to release memory in MiniRacer, thread to release could not be created, process will leak memory\n");
1482
+ xfree(context_info_copy);
1510
1483
  }
1511
1484
  } else {
1512
- free_context_raw(context_info_copy);
1485
+ free_context_raw(context_info);
1513
1486
  }
1514
1487
 
1515
1488
  context_info->context = NULL;
@@ -1782,9 +1755,7 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
1782
1755
  }
1783
1756
 
1784
1757
  VALUE function_name = argv[0];
1785
- if (TYPE(function_name) != T_STRING) {
1786
- rb_raise(rb_eTypeError, "first argument should be a String");
1787
- }
1758
+ Check_Type(function_name, T_STRING);
1788
1759
 
1789
1760
  char *fname = RSTRING_PTR(function_name);
1790
1761
  if (!fname) {
@@ -1870,12 +1841,7 @@ static VALUE rb_context_create_isolate_value(VALUE self) {
1870
1841
  static void set_ruby_exiting(VALUE value) {
1871
1842
  (void)value;
1872
1843
 
1873
- int res = pthread_rwlock_wrlock(&exit_lock);
1874
-
1875
- ruby_exiting = true;
1876
- if (res == 0) {
1877
- pthread_rwlock_unlock(&exit_lock);
1878
- }
1844
+ ruby_exiting.store(1);
1879
1845
  }
1880
1846
 
1881
1847
  extern "C" {
@@ -1941,9 +1907,5 @@ extern "C" {
1941
1907
  thread_attr_p = &attr;
1942
1908
  }
1943
1909
  }
1944
- auto on_fork_for_child = []() {
1945
- exit_lock = PTHREAD_RWLOCK_INITIALIZER;
1946
- };
1947
- pthread_atfork(nullptr, nullptr, on_fork_for_child);
1948
1910
  }
1949
1911
  }
@@ -1,5 +1,10 @@
1
1
  require 'mkmf'
2
2
 
3
+ if RUBY_ENGINE == "truffleruby"
4
+ File.write("Makefile", dummy_makefile($srcdir).join(""))
5
+ return
6
+ end
7
+
3
8
  extension_name = 'mini_racer_loader'
4
9
  dir_config extension_name
5
10
 
@@ -0,0 +1,355 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MiniRacer
4
+
5
+ class Context
6
+
7
+ class ExternalFunction
8
+ private
9
+
10
+ def notify_v8
11
+ name = @name.encode(::Encoding::UTF_8)
12
+ wrapped = lambda do |*args|
13
+ converted = @parent.send(:convert_js_to_ruby, args)
14
+ begin
15
+ result = @callback.call(*converted)
16
+ rescue Polyglot::ForeignException => e
17
+ e = RuntimeError.new(e.message)
18
+ e.set_backtrace(e.backtrace)
19
+ @parent.instance_variable_set(:@current_exception, e)
20
+ raise e
21
+ rescue => e
22
+ @parent.instance_variable_set(:@current_exception, e)
23
+ raise e
24
+ end
25
+ @parent.send(:convert_ruby_to_js, result)
26
+ end
27
+
28
+ if @parent_object.nil?
29
+ # set global name to proc
30
+ result = @parent.eval_in_context('this')
31
+ result[name] = wrapped
32
+ else
33
+ parent_object_eval = @parent_object_eval.encode(::Encoding::UTF_8)
34
+ begin
35
+ result = @parent.eval_in_context(parent_object_eval)
36
+ rescue Polyglot::ForeignException, StandardError => e
37
+ raise ParseError, "Was expecting #{@parent_object} to be an object", e.backtrace
38
+ end
39
+ result[name] = wrapped
40
+ # set evaluated object results name to proc
41
+ end
42
+ end
43
+ end
44
+
45
+ def heap_stats
46
+ {
47
+ total_physical_size: 0,
48
+ total_heap_size_executable: 0,
49
+ total_heap_size: 0,
50
+ used_heap_size: 0,
51
+ heap_size_limit: 0,
52
+ }
53
+ end
54
+
55
+ def stop
56
+ if @entered
57
+ @context.stop
58
+ @stopped = true
59
+ stop_attached
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ @context_initialized = false
66
+ @use_strict = false
67
+
68
+ def init_unsafe(isolate, snapshot)
69
+ unless defined?(Polyglot::InnerContext)
70
+ raise "TruffleRuby #{RUBY_ENGINE_VERSION} does not have support for inner contexts, use a more recent version"
71
+ end
72
+
73
+ unless Polyglot.languages.include? "js"
74
+ raise "The language 'js' is not available, you likely need to `export TRUFFLERUBYOPT='--jvm --polyglot'`\n" \
75
+ "You also need to install the 'js' component with 'gu install js' on GraalVM 22.2+\n" \
76
+ "Note that you need TruffleRuby+GraalVM and not just the TruffleRuby standalone to use MiniRacer"
77
+ end
78
+
79
+ @context = Polyglot::InnerContext.new(on_cancelled: -> {
80
+ raise ScriptTerminatedError, 'JavaScript was terminated (either by timeout or explicitly)'
81
+ })
82
+ Context.instance_variable_set(:@context_initialized, true)
83
+ @js_object = @context.eval('js', 'Object')
84
+ @isolate_mutex = Mutex.new
85
+ @stopped = false
86
+ @entered = false
87
+ @has_entered = false
88
+ @current_exception = nil
89
+ if isolate && snapshot
90
+ isolate.instance_variable_set(:@snapshot, snapshot)
91
+ end
92
+ if snapshot
93
+ @snapshot = snapshot
94
+ elsif isolate
95
+ @snapshot = isolate.instance_variable_get(:@snapshot)
96
+ else
97
+ @snapshot = nil
98
+ end
99
+ @is_object_or_array_func, @is_time_func, @js_date_to_time_func, @is_symbol_func, @js_symbol_to_symbol_func, @js_new_date_func, @js_new_array_func = eval_in_context <<-CODE
100
+ [
101
+ (x) => { return (x instanceof Object || x instanceof Array) && !(x instanceof Date) && !(x instanceof Function) },
102
+ (x) => { return x instanceof Date },
103
+ (x) => { return x.getTime(x) },
104
+ (x) => { return typeof x === 'symbol' },
105
+ (x) => { var r = x.description; return r === undefined ? 'undefined' : r },
106
+ (x) => { return new Date(x) },
107
+ (x) => { return new Array(x) },
108
+ ]
109
+ CODE
110
+ end
111
+
112
+ def dispose_unsafe
113
+ @context.close
114
+ end
115
+
116
+ def eval_unsafe(str, filename)
117
+ @entered = true
118
+ if !@has_entered && @snapshot
119
+ snapshot_src = encode(@snapshot.instance_variable_get(:@source))
120
+ begin
121
+ eval_in_context(snapshot_src, filename)
122
+ rescue Polyglot::ForeignException => e
123
+ raise RuntimeError, e.message, e.backtrace
124
+ end
125
+ end
126
+ @has_entered = true
127
+ raise RuntimeError, "TruffleRuby does not support eval after stop" if @stopped
128
+ raise TypeError, "wrong type argument #{str.class} (should be a string)" unless str.is_a?(String)
129
+ raise TypeError, "wrong type argument #{filename.class} (should be a string)" unless filename.nil? || filename.is_a?(String)
130
+
131
+ str = encode(str)
132
+ begin
133
+ translate do
134
+ eval_in_context(str, filename)
135
+ end
136
+ rescue Polyglot::ForeignException => e
137
+ raise RuntimeError, e.message, e.backtrace
138
+ rescue ::RuntimeError => e
139
+ if @current_exception
140
+ e = @current_exception
141
+ @current_exception = nil
142
+ raise e
143
+ else
144
+ raise e, e.message
145
+ end
146
+ end
147
+ ensure
148
+ @entered = false
149
+ end
150
+
151
+ def call_unsafe(function_name, *arguments)
152
+ @entered = true
153
+ if !@has_entered && @snapshot
154
+ src = encode(@snapshot.instance_variable_get(:source))
155
+ begin
156
+ eval_in_context(src)
157
+ rescue Polyglot::ForeignException => e
158
+ raise RuntimeError, e.message, e.backtrace
159
+ end
160
+ end
161
+ @has_entered = true
162
+ raise RuntimeError, "TruffleRuby does not support call after stop" if @stopped
163
+ begin
164
+ translate do
165
+ function = eval_in_context(function_name)
166
+ function.call(*convert_ruby_to_js(arguments))
167
+ end
168
+ rescue Polyglot::ForeignException => e
169
+ raise RuntimeError, e.message, e.backtrace
170
+ end
171
+ ensure
172
+ @entered = false
173
+ end
174
+
175
+ def create_isolate_value
176
+ # Returning a dummy object since TruffleRuby does not have a 1-1 concept with isolate.
177
+ # However, code and ASTs are shared between contexts.
178
+ Isolate.new
179
+ end
180
+
181
+ def isolate_mutex
182
+ @isolate_mutex
183
+ end
184
+
185
+ def translate
186
+ convert_js_to_ruby yield
187
+ rescue Object => e
188
+ message = e.message
189
+ if @current_exception
190
+ raise @current_exception
191
+ elsif e.message && e.message.start_with?('SyntaxError:')
192
+ error_class = MiniRacer::ParseError
193
+ elsif e.is_a?(MiniRacer::ScriptTerminatedError)
194
+ error_class = MiniRacer::ScriptTerminatedError
195
+ else
196
+ error_class = MiniRacer::RuntimeError
197
+ end
198
+
199
+ if error_class == MiniRacer::RuntimeError
200
+ bls = e.backtrace_locations&.select { |bl| bl&.source_location&.language == 'js' }
201
+ if bls && !bls.empty?
202
+ if '(eval)' != bls[0].path
203
+ message = "#{e.message}\n at #{bls[0]}\n" + bls[1..].map(&:to_s).join("\n")
204
+ else
205
+ message = "#{e.message}\n" + bls.map(&:to_s).join("\n")
206
+ end
207
+ end
208
+ raise error_class, message
209
+ else
210
+ raise error_class, message, e.backtrace
211
+ end
212
+ end
213
+
214
+ def convert_js_to_ruby(value)
215
+ case value
216
+ when true, false, Integer, Float
217
+ value
218
+ else
219
+ if value.nil?
220
+ nil
221
+ elsif value.respond_to?(:call)
222
+ MiniRacer::JavaScriptFunction.new
223
+ elsif value.respond_to?(:to_str)
224
+ value.to_str.dup
225
+ elsif value.respond_to?(:to_ary)
226
+ value.to_ary.map do |e|
227
+ if e.respond_to?(:call)
228
+ nil
229
+ else
230
+ convert_js_to_ruby(e)
231
+ end
232
+ end
233
+ elsif time?(value)
234
+ js_date_to_time(value)
235
+ elsif symbol?(value)
236
+ js_symbol_to_symbol(value)
237
+ else
238
+ object = value
239
+ h = {}
240
+ object.instance_variables.each do |member|
241
+ v = object[member]
242
+ unless v.respond_to?(:call)
243
+ h[member.to_s] = convert_js_to_ruby(v)
244
+ end
245
+ end
246
+ h
247
+ end
248
+ end
249
+ end
250
+
251
+ def object_or_array?(val)
252
+ @is_object_or_array_func.call(val)
253
+ end
254
+
255
+ def time?(value)
256
+ @is_time_func.call(value)
257
+ end
258
+
259
+ def js_date_to_time(value)
260
+ millis = @js_date_to_time_func.call(value)
261
+ Time.at(Rational(millis, 1000))
262
+ end
263
+
264
+ def symbol?(value)
265
+ @is_symbol_func.call(value)
266
+ end
267
+
268
+ def js_symbol_to_symbol(value)
269
+ @js_symbol_to_symbol_func.call(value).to_s.to_sym
270
+ end
271
+
272
+ def js_new_date(value)
273
+ @js_new_date_func.call(value)
274
+ end
275
+
276
+ def js_new_array(size)
277
+ @js_new_array_func.call(size)
278
+ end
279
+
280
+ def convert_ruby_to_js(value)
281
+ case value
282
+ when nil, true, false, Integer, Float
283
+ value
284
+ when Array
285
+ ary = js_new_array(value.size)
286
+ value.each_with_index do |v, i|
287
+ ary[i] = convert_ruby_to_js(v)
288
+ end
289
+ ary
290
+ when Hash
291
+ h = @js_object.new
292
+ value.each_pair do |k, v|
293
+ h[convert_ruby_to_js(k.to_s)] = convert_ruby_to_js(v)
294
+ end
295
+ h
296
+ when String, Symbol
297
+ Truffle::Interop.as_truffle_string value
298
+ when Time
299
+ js_new_date(value.to_f * 1000)
300
+ when DateTime
301
+ js_new_date(value.to_time.to_f * 1000)
302
+ else
303
+ "Undefined Conversion"
304
+ end
305
+ end
306
+
307
+ def encode(string)
308
+ raise ArgumentError unless string
309
+ string.encode(::Encoding::UTF_8)
310
+ end
311
+
312
+ class_eval <<-'RUBY', "(mini_racer)", 1
313
+ def eval_in_context(code, file = nil); code = ('"use strict";' + code) if Context.instance_variable_get(:@use_strict); @context.eval('js', code, file || '(mini_racer)'); end
314
+ RUBY
315
+
316
+ end
317
+
318
+ class Isolate
319
+ def init_with_snapshot(snapshot)
320
+ # TruffleRuby does not have a 1-1 concept with isolate.
321
+ # However, isolate can hold a snapshot, and code and ASTs are shared between contexts.
322
+ @snapshot = snapshot
323
+ end
324
+
325
+ def low_memory_notification
326
+ GC.start
327
+ end
328
+
329
+ def idle_notification(idle_time)
330
+ true
331
+ end
332
+ end
333
+
334
+ class Platform
335
+ def self.set_flag_as_str!(flag)
336
+ raise TypeError, "wrong type argument #{flag.class} (should be a string)" unless flag.is_a?(String)
337
+ raise MiniRacer::PlatformAlreadyInitialized, "The platform is already initialized." if Context.instance_variable_get(:@context_initialized)
338
+ Context.instance_variable_set(:@use_strict, true) if "--use_strict" == flag
339
+ end
340
+ end
341
+
342
+ class Snapshot
343
+ def load(str)
344
+ raise TypeError, "wrong type argument #{str.class} (should be a string)" unless str.is_a?(String)
345
+ # Intentionally noop since TruffleRuby mocks the snapshot API
346
+ end
347
+
348
+ def warmup_unsafe!(src)
349
+ raise TypeError, "wrong type argument #{src.class} (should be a string)" unless src.is_a?(String)
350
+ # Intentionally noop since TruffleRuby mocks the snapshot API
351
+ # by replaying snapshot source before the first eval/call
352
+ self
353
+ end
354
+ end
355
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MiniRacer
4
- VERSION = "0.6.2"
5
- LIBV8_NODE_VERSION = "~> 16.10.0.0"
4
+ VERSION = "0.6.4"
5
+ LIBV8_NODE_VERSION = "~> 16.19.0.0"
6
6
  end
data/lib/mini_racer.rb CHANGED
@@ -1,17 +1,22 @@
1
1
  require "mini_racer/version"
2
- require "mini_racer_loader"
3
2
  require "pathname"
4
3
 
5
- 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)
4
+ if RUBY_ENGINE == "truffleruby"
5
+ require "mini_racer/truffleruby"
6
+ else
7
+ require "mini_racer_loader"
8
+ ext_filename = "mini_racer_extension.#{RbConfig::CONFIG['DLEXT']}"
9
+ ext_path = Gem.loaded_specs['mini_racer'].require_paths
10
+ .map { |p| (p = Pathname.new(p)).absolute? ? p : Pathname.new(__dir__).parent + p }
11
+ ext_found = ext_path.map { |p| p + ext_filename }.find { |p| p.file? }
12
+
13
+ raise LoadError, "Could not find #{ext_filename} in #{ext_path.map(&:to_s)}" unless ext_found
14
+ MiniRacer::Loader.load(ext_found.to_s)
15
+ end
12
16
 
13
17
  require "thread"
14
18
  require "json"
19
+ require "io/wait"
15
20
 
16
21
  module MiniRacer
17
22
 
@@ -202,7 +207,7 @@ module MiniRacer
202
207
  end
203
208
 
204
209
  if !(File === f)
205
- raise ArgumentError("file_or_io")
210
+ raise ArgumentError, "file_or_io"
206
211
  end
207
212
 
208
213
  write_heap_snapshot_unsafe(f)
@@ -349,7 +354,7 @@ module MiniRacer
349
354
 
350
355
  t = Thread.new do
351
356
  begin
352
- result = IO.select([rp],[],[],(@timeout/1000.0))
357
+ result = rp.wait_readable(@timeout/1000.0)
353
358
  if !result
354
359
  mutex.synchronize do
355
360
  stop unless done
@@ -366,7 +371,7 @@ module MiniRacer
366
371
  done = true
367
372
  end
368
373
 
369
- wp.write("done")
374
+ wp.close
370
375
 
371
376
  # ensure we do not leak a thread in state
372
377
  t.join
@@ -375,12 +380,9 @@ module MiniRacer
375
380
  rval
376
381
  ensure
377
382
  # exceptions need to be handled
378
- if t && wp
379
- wp.write("done")
380
- t.join
381
- end
382
- wp.close if wp
383
- rp.close if rp
383
+ wp&.close
384
+ t&.join
385
+ rp&.close
384
386
  end
385
387
 
386
388
  def check_init_options!(isolate:, snapshot:, max_memory:, marshal_stack_depth:, ensure_gc_after_idle:, timeout:)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mini_racer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.2
4
+ version: 0.6.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Saffron
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-01-16 00:00:00.000000000 Z
11
+ date: 2023-05-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -86,14 +86,14 @@ dependencies:
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: 16.10.0.0
89
+ version: 16.19.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: 16.10.0.0
96
+ version: 16.19.0.0
97
97
  description: Minimal embedded v8 engine for Ruby
98
98
  email:
99
99
  - sam.saffron@gmail.com
@@ -106,7 +106,6 @@ files:
106
106
  - ".dockerignore"
107
107
  - ".github/workflows/ci.yml"
108
108
  - ".gitignore"
109
- - ".travis.yml"
110
109
  - CHANGELOG
111
110
  - CODE_OF_CONDUCT.md
112
111
  - Dockerfile
@@ -121,6 +120,7 @@ files:
121
120
  - ext/mini_racer_loader/extconf.rb
122
121
  - ext/mini_racer_loader/mini_racer_loader.c
123
122
  - lib/mini_racer.rb
123
+ - lib/mini_racer/truffleruby.rb
124
124
  - lib/mini_racer/version.rb
125
125
  - mini_racer.gemspec
126
126
  homepage: https://github.com/discourse/mini_racer
@@ -128,9 +128,9 @@ licenses:
128
128
  - MIT
129
129
  metadata:
130
130
  bug_tracker_uri: https://github.com/discourse/mini_racer/issues
131
- changelog_uri: https://github.com/discourse/mini_racer/blob/v0.6.2/CHANGELOG
132
- documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.6.2
133
- source_code_uri: https://github.com/discourse/mini_racer/tree/v0.6.2
131
+ changelog_uri: https://github.com/discourse/mini_racer/blob/v0.6.4/CHANGELOG
132
+ documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.6.4
133
+ source_code_uri: https://github.com/discourse/mini_racer/tree/v0.6.4
134
134
  post_install_message:
135
135
  rdoc_options: []
136
136
  require_paths:
@@ -147,7 +147,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
147
147
  - !ruby/object:Gem::Version
148
148
  version: '0'
149
149
  requirements: []
150
- rubygems_version: 3.1.6
150
+ rubygems_version: 3.4.6
151
151
  signing_key:
152
152
  specification_version: 4
153
153
  summary: Minimal embedded v8 for Ruby
data/.travis.yml DELETED
@@ -1,23 +0,0 @@
1
- language: ruby
2
- os: linux
3
- rvm:
4
- - 2.6
5
- - 2.7
6
- - 3.0
7
- - ruby-head
8
- arch:
9
- - amd64
10
- - arm64
11
- jobs:
12
- include:
13
- - rvm: 2.6
14
- os: osx
15
- osx_image: xcode11.3
16
- - rvm: 2.6
17
- os: osx
18
- osx_image: xcode12.2
19
- - rvm: 2.7
20
- os: osx
21
- osx_image: xcode12.2
22
- dist: xenial
23
- cache: bundler