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:
commit
39118e25f9
6 changed files with 154 additions and 4 deletions
7
.github/workflows/ci.yml
vendored
7
.github/workflows/ci.yml
vendored
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
142
lib/execjs/graaljs_runtime.rb
Normal file
142
lib/execjs/graaljs_runtime.rb
Normal 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
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue