Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 3 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Comment thread
eregon marked this conversation as resolved.

steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v4
Expand All @@ -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
Expand Down
51 changes: 31 additions & 20 deletions lib/mini_racer/truffleruby.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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: -> {
Expand All @@ -102,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
Expand Down Expand Up @@ -232,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
Expand Down Expand Up @@ -281,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
Expand Down
12 changes: 9 additions & 3 deletions test/mini_racer_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1224,14 +1224,20 @@ 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("add_one", ->(data) {
MiniRacer::Binary.new(data.bytes.map { _1 + 1 }.pack("C*"))
Comment thread
eregon marked this conversation as resolved.
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
Expand Down