From 4b203ef7fd2896a21d13e90b7d56f51f584846b9 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Fri, 1 May 2026 13:05:40 +0200 Subject: [PATCH 1/4] Improve error messages when running on TruffleRuby --- lib/mini_racer/truffleruby.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/mini_racer/truffleruby.rb b/lib/mini_racer/truffleruby.rb index 802acfd..b077bad 100644 --- a/lib/mini_racer/truffleruby.rb +++ b/lib/mini_racer/truffleruby.rb @@ -77,9 +77,13 @@ def init_unsafe(isolate, snapshot) raise "TruffleRuby #{RUBY_ENGINE_VERSION} does not have support for inner contexts, use a more recent version" end + + if TruffleRuby.native? + raise "You need the TruffleRuby JVM Standalone for mini_racer because it is not possible to install the js component in the the Native standalone" + end + unless Polyglot.languages.include? "js" - raise "The language 'js' is not available, you likely need to `export TRUFFLERUBYOPT='--jvm --polyglot'`\n" \ - "You also need to install the 'js' component, see https://github.com/oracle/truffleruby/blob/master/doc/user/polyglot.md#installing-other-languages" + raise "The language 'js' is not available, you need to install the 'js' component.\nSee https://github.com/oracle/truffleruby/blob/master/doc/user/polyglot.md#installing-other-languages" end @context = Polyglot::InnerContext.new(on_cancelled: -> { From 292c40734530b9eac37cb192f9a0d19ba5fa4d89 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Fri, 1 May 2026 22:48:53 +0200 Subject: [PATCH 2/4] Adapt test_binary_returns_uint8array to not assume that Uint8Array are converted to String * This fixes https://github.com/rubyjs/mini_racer/issues/409. * The test was added in https://github.com/rubyjs/mini_racer/pull/406. --- test/mini_racer_test.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/mini_racer_test.rb b/test/mini_racer_test.rb index 53e563f..7725544 100644 --- a/test/mini_racer_test.rb +++ b/test/mini_racer_test.rb @@ -1226,12 +1226,12 @@ def test_large_integer def test_binary_returns_uint8array context = MiniRacer::Context.new - context.attach("add_one", ->(data) { - MiniRacer::Binary.new(data.bytes.map { _1 + 1 }.pack("C*")) + context.attach("create_uint8_array", -> { + MiniRacer::Binary.new([1, 2, 3, 4].pack("C*")) }) result = context.eval <<~JS - var output = add_one(new Uint8Array([0, 1, 2, 3])); + var output = create_uint8_array(); (output instanceof Uint8Array) && Array.from(output).join(",") === "1,2,3,4"; JS assert_equal true, result From 126d36a595947148749bb4b1ce0ed442dd80589f Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Fri, 1 May 2026 22:51:57 +0200 Subject: [PATCH 3/4] Cleanup truffleruby CI job * Add timeout to avoid a hang to take 6 hours. * A single job on macOS seems enough. * Remove no longer necessary bundler update and TRUFFLERUBYOPT. --- .github/workflows/ci.yml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2ce1a6b..b7d88dc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,18 +14,14 @@ jobs: fail-fast: false matrix: os: - - "macos-14" # arm64 - - "macos-15" # arm64 - - "macos-26" # arm64 - - "ubuntu-24.04" + - "macos-latest" + - "ubuntu-latest" ruby: - "truffleruby+graalvm" name: ${{ matrix.os }} - ${{ matrix.ruby }} runs-on: ${{ matrix.os }} - - env: - TRUFFLERUBYOPT: "--jvm --polyglot" + timeout-minutes: 10 steps: - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v4 @@ -34,7 +30,6 @@ jobs: - uses: ruby/setup-ruby@4c56a21280b36d862b5fc31348f463d60bdc55d5 # v1 with: ruby-version: ${{ matrix.ruby }} - bundler: latest # to get this fix: https://github.com/rubygems/rubygems/issues/6165 bundler-cache: true - name: Install GraalVM JS component run: truffleruby-polyglot-get js From 175691b23c56459fdfcce9fff1b5313877bc3819 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Mon, 4 May 2026 22:29:29 +0200 Subject: [PATCH 4/4] Add test for converting a JS Uint8Array to Ruby and implement it on TruffleRuby --- lib/mini_racer/truffleruby.rb | 43 ++++++++++++++++++++--------------- test/mini_racer_test.rb | 6 +++++ 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/lib/mini_racer/truffleruby.rb b/lib/mini_racer/truffleruby.rb index b077bad..8a40048 100644 --- a/lib/mini_racer/truffleruby.rb +++ b/lib/mini_racer/truffleruby.rb @@ -106,20 +106,19 @@ def init_unsafe(isolate, snapshot) else @snapshot = nil end - @is_object_or_array_func, @is_map_func, @is_map_iterator_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, @js_new_uint8array_func = eval_in_context <<-CODE - [ - (x) => { return (x instanceof Object || x instanceof Array) && !(x instanceof Date) && !(x instanceof Function) }, - (x) => { return x instanceof Map }, - (x) => { return x[Symbol.toStringTag] === 'Map Iterator' }, - (x) => { return x instanceof Date }, - (x) => { return x.getTime(x) }, - (x) => { return typeof x === 'symbol' }, - (x) => { var r = x.description; return r === undefined ? 'undefined' : r }, - (x) => { return new Date(x) }, - (x) => { return new Array(x) }, - (x) => { return new Uint8Array(x) }, - ] - CODE + + @is_object_or_array_func = eval_in_context "(x) => { return (x instanceof Object || x instanceof Array) && !(x instanceof Date) && !(x instanceof Function) }" + @is_map_func = eval_in_context "(x) => { return x instanceof Map }" + @is_map_iterator_func = eval_in_context "(x) => { return x[Symbol.toStringTag] === 'Map Iterator' }" + @is_time_func = eval_in_context "(x) => { return x instanceof Date }" + @is_symbol_func = eval_in_context "(x) => { return typeof x === 'symbol' }" + @is_uint8_array_func = eval_in_context "(x) => { return x instanceof Uint8Array }" + + @js_date_to_time_func = eval_in_context "(x) => { return x.getTime(x) }" + @js_symbol_to_symbol_func = eval_in_context "(x) => { var r = x.description; return r === undefined ? 'undefined' : r }" + @js_new_date_func = eval_in_context "(x) => { return new Date(x) }" + @js_new_array_func = eval_in_context "(x) => { return new Array(x) }" + @js_new_uint8array_func = eval_in_context "(x) => { return new Uint8Array(x) }" end def dispose_unsafe @@ -236,6 +235,10 @@ def convert_js_to_ruby(value) elsif value.respond_to?(:to_str) value.to_str.dup elsif value.respond_to?(:to_ary) + if uint8_array?(value) + return value.to_a.pack('C*') + end + value.to_ary.map do |e| if e.respond_to?(:call) nil @@ -285,15 +288,19 @@ def time?(value) @is_time_func.call(value) end + def symbol?(value) + @is_symbol_func.call(value) + end + + def uint8_array?(value) + @is_uint8_array_func.call(value) + end + def js_date_to_time(value) millis = @js_date_to_time_func.call(value) Time.at(Rational(millis, 1000)) end - def symbol?(value) - @is_symbol_func.call(value) - end - def js_symbol_to_symbol(value) @js_symbol_to_symbol_func.call(value).to_s.to_sym end diff --git a/test/mini_racer_test.rb b/test/mini_racer_test.rb index 7725544..ef8f3d8 100644 --- a/test/mini_racer_test.rb +++ b/test/mini_racer_test.rb @@ -1224,6 +1224,12 @@ def test_large_integer end end + def test_uint8array_is_converted_to_string + context = MiniRacer::Context.new + result = context.eval('new Uint8Array([0, 1, 2, 3])') + assert_equal "\x00\x01\x02\x03".b, result + end + def test_binary_returns_uint8array context = MiniRacer::Context.new context.attach("create_uint8_array", -> {