-
Notifications
You must be signed in to change notification settings - Fork 282
/
graaljs_runtime.rb
146 lines (125 loc) · 3.92 KB
/
graaljs_runtime.rb
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 = 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
ForeignException = defined?(Polyglot::ForeignException) ? Polyglot::ForeignException : ::RuntimeError
def translate
convert_js_to_ruby yield
rescue ForeignException => 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 Symbol
value.to_s
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