1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
|
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 = source.encode(Encoding::UTF_8)
unless source.empty?
translate do
eval_in_context(source)
end
end
end
def exec(source, options = {})
source = source.encode(Encoding::UTF_8)
source = "(function(){#{source}})()" if /\S/.match?(source)
translate do
eval_in_context(source)
end
end
def eval(source, options = {})
source = source.encode(Encoding::UTF_8)
source = "(#{source})" if /\S/.match?(source)
translate do
eval_in_context(source)
end
end
def call(source, *args)
source = source.encode(Encoding::UTF_8)
source = "(#{source})" if /\S/.match?(source)
translate do
function = eval_in_context(source)
function.call(*convert_ruby_to_js(args))
end
end
private
ForeignException = defined?(Polyglot::ForeignException) ? Polyglot::ForeignException : ::RuntimeError
def translate
convert_js_to_ruby yield
rescue ForeignException => e
if e.message && 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
value
when String, Symbol
Truffle::Interop.as_truffle_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 "You also need to install the 'js' component, see https://github.com/oracle/truffleruby/blob/master/doc/user/polyglot.md#installing-other-languages", uplevel: 0 if $VERBOSE
return @available = false
end
@available = true
end
end
end
|