1
0
Fork 0
mirror of https://github.com/rails/execjs synced 2023-03-27 23:21:20 -04:00

Merge pull request #107 from eregon/graaljs-runtime-ready

Add runtime using GraalJS on TruffleRuby
This commit is contained in:
Jean Boussier 2021-10-19 21:48:25 +02:00 committed by GitHub
commit 39118e25f9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 154 additions and 4 deletions

View file

@ -7,7 +7,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
ruby: [ '3.0', '2.7', '2.6', '2.5', 'jruby', 'truffleruby' ] ruby: [ '3.0', '2.7', '2.6', '2.5', 'jruby', 'truffleruby', 'truffleruby+graalvm' ]
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
@ -18,12 +18,17 @@ jobs:
uses: ruby/setup-ruby@v1 uses: ruby/setup-ruby@v1
with: with:
ruby-version: ${{ matrix.ruby }} ruby-version: ${{ matrix.ruby }}
- name: Update Rubygems - name: Update Rubygems
run: gem update --system run: gem update --system
- name: Install bundler - name: Install bundler
run: gem install bundler -v '2.2.16' run: gem install bundler -v '2.2.16'
- name: Install dependencies - name: Install dependencies
run: bundle install run: bundle install
- name: Set TRUFFLERUBYOPT
run: echo "TRUFFLERUBYOPT=--jvm --polyglot" >> $GITHUB_ENV
if: startsWith(matrix.ruby, 'truffleruby+graalvm')
- name: Run test - name: Run test
run: rake run: rake
- name: Install gem - name: Install gem

View file

@ -19,7 +19,7 @@ module ExecJS
end end
else else
def encode(string) def encode(string)
string.encode('UTF-8') string.encode(::Encoding::UTF_8)
end end
end end
end end

View file

@ -0,0 +1,142 @@
require "execjs/runtime"
module ExecJS
class GraalJSRuntime < Runtime
class Context < Runtime::Context
def initialize(runtime, source = "", options = {})
@context = Polyglot::InnerContext.new
@context.eval('js', 'delete this.console')
@js_object = @context.eval('js', 'Object')
source = encode(source)
unless source.empty?
translate do
eval_in_context(source)
end
end
end
def exec(source, options = {})
source = encode(source)
source = "(function(){#{source}})()" if /\S/.match?(source)
translate do
eval_in_context(source)
end
end
def eval(source, options = {})
source = encode(source)
source = "(#{source})" if /\S/.match?(source)
translate do
eval_in_context(source)
end
end
def call(source, *args)
source = encode(source)
source = "(#{source})" if /\S/.match?(source)
translate do
function = eval_in_context(source)
function.call(*convert_ruby_to_js(args))
end
end
private
def translate
convert_js_to_ruby yield
rescue ::RuntimeError => e
if e.message.start_with?('SyntaxError:')
error_class = ExecJS::RuntimeError
else
error_class = ExecJS::ProgramError
end
backtrace = e.backtrace.map { |line| line.sub('(eval)', '(execjs)') }
raise error_class, e.message, backtrace
end
def convert_js_to_ruby(value)
case value
when true, false, Integer, Float
value
else
if value.nil?
nil
elsif value.respond_to?(:call)
nil
elsif value.respond_to?(:to_str)
value.to_str
elsif value.respond_to?(:to_ary)
value.to_ary.map do |e|
if e.respond_to?(:call)
nil
else
convert_js_to_ruby(e)
end
end
else
object = value
h = {}
object.instance_variables.each do |member|
v = object[member]
unless v.respond_to?(:call)
h[member.to_s] = convert_js_to_ruby(v)
end
end
h
end
end
end
def convert_ruby_to_js(value)
case value
when nil, true, false, Integer, Float, String
value
when Array
value.map { |e| convert_ruby_to_js(e) }
when Hash
h = @js_object.new
value.each_pair do |k,v|
h[convert_ruby_to_js(k)] = convert_ruby_to_js(v)
end
h
else
raise TypeError, "Unknown how to convert to JS: #{value.inspect}"
end
end
class_eval <<-'RUBY', "(execjs)", 1
def eval_in_context(code); @context.eval('js', code); end
RUBY
end
def name
"GraalVM (Graal.js)"
end
def available?
return @available if defined?(@available)
unless RUBY_ENGINE == "truffleruby"
return @available = false
end
unless defined?(Polyglot::InnerContext)
warn "TruffleRuby #{RUBY_ENGINE_VERSION} does not have support for inner contexts, use a more recent version", uplevel: 0 if $VERBOSE
return @available = false
end
unless Polyglot.languages.include? "js"
warn "The language 'js' is not available, you likely need to `export TRUFFLERUBYOPT='--jvm --polyglot'`", uplevel: 0 if $VERBOSE
warn "Note that you need TruffleRuby+GraalVM and not just the TruffleRuby standalone to use #{self.class}", uplevel: 0 if $VERBOSE
return @available = false
end
@available = true
end
end
end

View file

@ -4,6 +4,7 @@ require "execjs/duktape_runtime"
require "execjs/external_runtime" require "execjs/external_runtime"
require "execjs/ruby_rhino_runtime" require "execjs/ruby_rhino_runtime"
require "execjs/mini_racer_runtime" require "execjs/mini_racer_runtime"
require "execjs/graaljs_runtime"
module ExecJS module ExecJS
module Runtimes module Runtimes
@ -13,6 +14,8 @@ module ExecJS
RubyRhino = RubyRhinoRuntime.new RubyRhino = RubyRhinoRuntime.new
GraalJS = GraalJSRuntime.new
MiniRacer = MiniRacerRuntime.new MiniRacer = MiniRacerRuntime.new
Node = ExternalRuntime.new( Node = ExternalRuntime.new(
@ -82,6 +85,7 @@ module ExecJS
def self.runtimes def self.runtimes
@runtimes ||= [ @runtimes ||= [
RubyRhino, RubyRhino,
GraalJS,
Duktape, Duktape,
MiniRacer, MiniRacer,
Node, Node,

View file

@ -2,7 +2,6 @@
}, function(program) { }, function(program) {
var output; var output;
try { try {
delete this.console;
delete this.console; delete this.console;
delete this.setTimeout; delete this.setTimeout;
delete this.setInterval; delete this.setInterval;

View file

@ -156,7 +156,7 @@ class TestExecJS < Test
assert_output value, ExecJS.eval("#{json_value}") assert_output value, ExecJS.eval("#{json_value}")
end end
define_method("test_strinigfy_value_#{index}") do define_method("test_stringify_value_#{index}") do
context = ExecJS.compile("function json(obj) { return JSON.stringify(obj); }") context = ExecJS.compile("function json(obj) { return JSON.stringify(obj); }")
assert_output json_value, context.call("json", value) assert_output json_value, context.call("json", value)
end end