mini_racer 0.5.0 → 0.6.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +72 -26
- data/CHANGELOG +40 -0
- data/README.md +18 -16
- data/Rakefile +16 -4
- data/ext/mini_racer_extension/extconf.rb +34 -13
- data/ext/mini_racer_extension/mini_racer_extension.cc +181 -177
- data/ext/mini_racer_loader/extconf.rb +6 -1
- data/lib/mini_racer/truffleruby.rb +353 -0
- data/lib/mini_racer/version.rb +1 -1
- data/lib/mini_racer.rb +19 -17
- metadata +10 -10
- data/.travis.yml +0 -23
@@ -1,8 +1,13 @@
|
|
1
1
|
require 'mkmf'
|
2
2
|
|
3
|
+
if RUBY_ENGINE == "truffleruby"
|
4
|
+
File.write("Makefile", dummy_makefile($srcdir).join(""))
|
5
|
+
return
|
6
|
+
end
|
7
|
+
|
3
8
|
extension_name = 'mini_racer_loader'
|
4
9
|
dir_config extension_name
|
5
10
|
|
6
|
-
$
|
11
|
+
$CXXFLAGS += " -fvisibility=hidden "
|
7
12
|
|
8
13
|
create_makefile extension_name
|
@@ -0,0 +1,353 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MiniRacer
|
4
|
+
|
5
|
+
class Context
|
6
|
+
|
7
|
+
class ExternalFunction
|
8
|
+
private
|
9
|
+
|
10
|
+
def notify_v8
|
11
|
+
name = @name.encode(::Encoding::UTF_8)
|
12
|
+
wrapped = lambda do |*args|
|
13
|
+
converted = @parent.send(:convert_js_to_ruby, args)
|
14
|
+
begin
|
15
|
+
result = @callback.call(*converted)
|
16
|
+
rescue Polyglot::ForeignException => e
|
17
|
+
e = RuntimeError.new(e.message)
|
18
|
+
e.set_backtrace(e.backtrace)
|
19
|
+
@parent.instance_variable_set(:@current_exception, e)
|
20
|
+
raise e
|
21
|
+
rescue => e
|
22
|
+
@parent.instance_variable_set(:@current_exception, e)
|
23
|
+
raise e
|
24
|
+
end
|
25
|
+
@parent.send(:convert_ruby_to_js, result)
|
26
|
+
end
|
27
|
+
|
28
|
+
if @parent_object.nil?
|
29
|
+
# set global name to proc
|
30
|
+
result = @parent.eval_in_context('this')
|
31
|
+
result[name] = wrapped
|
32
|
+
else
|
33
|
+
parent_object_eval = @parent_object_eval.encode(::Encoding::UTF_8)
|
34
|
+
begin
|
35
|
+
result = @parent.eval_in_context(parent_object_eval)
|
36
|
+
rescue Polyglot::ForeignException, StandardError => e
|
37
|
+
raise ParseError, "Was expecting #{@parent_object} to be an object", e.backtrace
|
38
|
+
end
|
39
|
+
result[name] = wrapped
|
40
|
+
# set evaluated object results name to proc
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def heap_stats
|
46
|
+
{
|
47
|
+
total_physical_size: 0,
|
48
|
+
total_heap_size_executable: 0,
|
49
|
+
total_heap_size: 0,
|
50
|
+
used_heap_size: 0,
|
51
|
+
heap_size_limit: 0,
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
def stop
|
56
|
+
if @entered
|
57
|
+
@context.stop
|
58
|
+
@stopped = true
|
59
|
+
stop_attached
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
@context_initialized = false
|
66
|
+
@use_strict = false
|
67
|
+
|
68
|
+
def init_unsafe(isolate, snapshot)
|
69
|
+
unless defined?(Polyglot::InnerContext)
|
70
|
+
raise "TruffleRuby #{RUBY_ENGINE_VERSION} does not have support for inner contexts, use a more recent version"
|
71
|
+
end
|
72
|
+
|
73
|
+
unless Polyglot.languages.include? "js"
|
74
|
+
warn "You also need to install the 'js' component with 'gu install js' on GraalVM 22.2+", uplevel: 0 if $VERBOSE
|
75
|
+
end
|
76
|
+
|
77
|
+
@context = Polyglot::InnerContext.new(on_cancelled: -> {
|
78
|
+
raise ScriptTerminatedError, 'JavaScript was terminated (either by timeout or explicitly)'
|
79
|
+
})
|
80
|
+
Context.instance_variable_set(:@context_initialized, true)
|
81
|
+
@js_object = @context.eval('js', 'Object')
|
82
|
+
@isolate_mutex = Mutex.new
|
83
|
+
@stopped = false
|
84
|
+
@entered = false
|
85
|
+
@has_entered = false
|
86
|
+
@current_exception = nil
|
87
|
+
if isolate && snapshot
|
88
|
+
isolate.instance_variable_set(:@snapshot, snapshot)
|
89
|
+
end
|
90
|
+
if snapshot
|
91
|
+
@snapshot = snapshot
|
92
|
+
elsif isolate
|
93
|
+
@snapshot = isolate.instance_variable_get(:@snapshot)
|
94
|
+
else
|
95
|
+
@snapshot = nil
|
96
|
+
end
|
97
|
+
@is_object_or_array_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 = eval_in_context <<-CODE
|
98
|
+
[
|
99
|
+
(x) => { return (x instanceof Object || x instanceof Array) && !(x instanceof Date) && !(x instanceof Function) },
|
100
|
+
(x) => { return x instanceof Date },
|
101
|
+
(x) => { return x.getTime(x) },
|
102
|
+
(x) => { return typeof x === 'symbol' },
|
103
|
+
(x) => { var r = x.description; return r === undefined ? 'undefined' : r },
|
104
|
+
(x) => { return new Date(x) },
|
105
|
+
(x) => { return new Array(x) },
|
106
|
+
]
|
107
|
+
CODE
|
108
|
+
end
|
109
|
+
|
110
|
+
def dispose_unsafe
|
111
|
+
@context.close
|
112
|
+
end
|
113
|
+
|
114
|
+
def eval_unsafe(str, filename)
|
115
|
+
@entered = true
|
116
|
+
if !@has_entered && @snapshot
|
117
|
+
snapshot_src = encode(@snapshot.instance_variable_get(:@source))
|
118
|
+
begin
|
119
|
+
eval_in_context(snapshot_src, filename)
|
120
|
+
rescue Polyglot::ForeignException => e
|
121
|
+
raise RuntimeError, e.message, e.backtrace
|
122
|
+
end
|
123
|
+
end
|
124
|
+
@has_entered = true
|
125
|
+
raise RuntimeError, "TruffleRuby does not support eval after stop" if @stopped
|
126
|
+
raise TypeError, "wrong type argument #{str.class} (should be a string)" unless str.is_a?(String)
|
127
|
+
raise TypeError, "wrong type argument #{filename.class} (should be a string)" unless filename.nil? || filename.is_a?(String)
|
128
|
+
|
129
|
+
str = encode(str)
|
130
|
+
begin
|
131
|
+
translate do
|
132
|
+
eval_in_context(str, filename)
|
133
|
+
end
|
134
|
+
rescue Polyglot::ForeignException => e
|
135
|
+
raise RuntimeError, e.message, e.backtrace
|
136
|
+
rescue ::RuntimeError => e
|
137
|
+
if @current_exception
|
138
|
+
e = @current_exception
|
139
|
+
@current_exception = nil
|
140
|
+
raise e
|
141
|
+
else
|
142
|
+
raise e, e.message
|
143
|
+
end
|
144
|
+
end
|
145
|
+
ensure
|
146
|
+
@entered = false
|
147
|
+
end
|
148
|
+
|
149
|
+
def call_unsafe(function_name, *arguments)
|
150
|
+
@entered = true
|
151
|
+
if !@has_entered && @snapshot
|
152
|
+
src = encode(@snapshot.instance_variable_get(:source))
|
153
|
+
begin
|
154
|
+
eval_in_context(src)
|
155
|
+
rescue Polyglot::ForeignException => e
|
156
|
+
raise RuntimeError, e.message, e.backtrace
|
157
|
+
end
|
158
|
+
end
|
159
|
+
@has_entered = true
|
160
|
+
raise RuntimeError, "TruffleRuby does not support call after stop" if @stopped
|
161
|
+
begin
|
162
|
+
translate do
|
163
|
+
function = eval_in_context(function_name)
|
164
|
+
function.call(*convert_ruby_to_js(arguments))
|
165
|
+
end
|
166
|
+
rescue Polyglot::ForeignException => e
|
167
|
+
raise RuntimeError, e.message, e.backtrace
|
168
|
+
end
|
169
|
+
ensure
|
170
|
+
@entered = false
|
171
|
+
end
|
172
|
+
|
173
|
+
def create_isolate_value
|
174
|
+
# Returning a dummy object since TruffleRuby does not have a 1-1 concept with isolate.
|
175
|
+
# However, code and ASTs are shared between contexts.
|
176
|
+
Isolate.new
|
177
|
+
end
|
178
|
+
|
179
|
+
def isolate_mutex
|
180
|
+
@isolate_mutex
|
181
|
+
end
|
182
|
+
|
183
|
+
def translate
|
184
|
+
convert_js_to_ruby yield
|
185
|
+
rescue Object => e
|
186
|
+
message = e.message
|
187
|
+
if @current_exception
|
188
|
+
raise @current_exception
|
189
|
+
elsif e.message && e.message.start_with?('SyntaxError:')
|
190
|
+
error_class = MiniRacer::ParseError
|
191
|
+
elsif e.is_a?(MiniRacer::ScriptTerminatedError)
|
192
|
+
error_class = MiniRacer::ScriptTerminatedError
|
193
|
+
else
|
194
|
+
error_class = MiniRacer::RuntimeError
|
195
|
+
end
|
196
|
+
|
197
|
+
if error_class == MiniRacer::RuntimeError
|
198
|
+
bls = e.backtrace_locations&.select { |bl| bl&.source_location&.language == 'js' }
|
199
|
+
if bls && !bls.empty?
|
200
|
+
if '(eval)' != bls[0].path
|
201
|
+
message = "#{e.message}\n at #{bls[0]}\n" + bls[1..].map(&:to_s).join("\n")
|
202
|
+
else
|
203
|
+
message = "#{e.message}\n" + bls.map(&:to_s).join("\n")
|
204
|
+
end
|
205
|
+
end
|
206
|
+
raise error_class, message
|
207
|
+
else
|
208
|
+
raise error_class, message, e.backtrace
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def convert_js_to_ruby(value)
|
213
|
+
case value
|
214
|
+
when true, false, Integer, Float
|
215
|
+
value
|
216
|
+
else
|
217
|
+
if value.nil?
|
218
|
+
nil
|
219
|
+
elsif value.respond_to?(:call)
|
220
|
+
MiniRacer::JavaScriptFunction.new
|
221
|
+
elsif value.respond_to?(:to_str)
|
222
|
+
value.to_str.dup
|
223
|
+
elsif value.respond_to?(:to_ary)
|
224
|
+
value.to_ary.map do |e|
|
225
|
+
if e.respond_to?(:call)
|
226
|
+
nil
|
227
|
+
else
|
228
|
+
convert_js_to_ruby(e)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
elsif time?(value)
|
232
|
+
js_date_to_time(value)
|
233
|
+
elsif symbol?(value)
|
234
|
+
js_symbol_to_symbol(value)
|
235
|
+
else
|
236
|
+
object = value
|
237
|
+
h = {}
|
238
|
+
object.instance_variables.each do |member|
|
239
|
+
v = object[member]
|
240
|
+
unless v.respond_to?(:call)
|
241
|
+
h[member.to_s] = convert_js_to_ruby(v)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
h
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
def object_or_array?(val)
|
250
|
+
@is_object_or_array_func.call(val)
|
251
|
+
end
|
252
|
+
|
253
|
+
def time?(value)
|
254
|
+
@is_time_func.call(value)
|
255
|
+
end
|
256
|
+
|
257
|
+
def js_date_to_time(value)
|
258
|
+
millis = @js_date_to_time_func.call(value)
|
259
|
+
Time.at(Rational(millis, 1000))
|
260
|
+
end
|
261
|
+
|
262
|
+
def symbol?(value)
|
263
|
+
@is_symbol_func.call(value)
|
264
|
+
end
|
265
|
+
|
266
|
+
def js_symbol_to_symbol(value)
|
267
|
+
@js_symbol_to_symbol_func.call(value).to_s.to_sym
|
268
|
+
end
|
269
|
+
|
270
|
+
def js_new_date(value)
|
271
|
+
@js_new_date_func.call(value)
|
272
|
+
end
|
273
|
+
|
274
|
+
def js_new_array(size)
|
275
|
+
@js_new_array_func.call(size)
|
276
|
+
end
|
277
|
+
|
278
|
+
def convert_ruby_to_js(value)
|
279
|
+
case value
|
280
|
+
when nil, true, false, Integer, Float
|
281
|
+
value
|
282
|
+
when Array
|
283
|
+
ary = js_new_array(value.size)
|
284
|
+
value.each_with_index do |v, i|
|
285
|
+
ary[i] = convert_ruby_to_js(v)
|
286
|
+
end
|
287
|
+
ary
|
288
|
+
when Hash
|
289
|
+
h = @js_object.new
|
290
|
+
value.each_pair do |k, v|
|
291
|
+
h[convert_ruby_to_js(k.to_s)] = convert_ruby_to_js(v)
|
292
|
+
end
|
293
|
+
h
|
294
|
+
when String, Symbol
|
295
|
+
Truffle::Interop.as_truffle_string value
|
296
|
+
when Time
|
297
|
+
js_new_date(value.to_f * 1000)
|
298
|
+
when DateTime
|
299
|
+
js_new_date(value.to_time.to_f * 1000)
|
300
|
+
else
|
301
|
+
"Undefined Conversion"
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
def encode(string)
|
306
|
+
raise ArgumentError unless string
|
307
|
+
string.encode(::Encoding::UTF_8)
|
308
|
+
end
|
309
|
+
|
310
|
+
class_eval <<-'RUBY', "(mini_racer)", 1
|
311
|
+
def eval_in_context(code, file = nil); code = ('"use strict";' + code) if Context.instance_variable_get(:@use_strict); @context.eval('js', code, file || '(mini_racer)'); end
|
312
|
+
RUBY
|
313
|
+
|
314
|
+
end
|
315
|
+
|
316
|
+
class Isolate
|
317
|
+
def init_with_snapshot(snapshot)
|
318
|
+
# TruffleRuby does not have a 1-1 concept with isolate.
|
319
|
+
# However, isolate can hold a snapshot, and code and ASTs are shared between contexts.
|
320
|
+
@snapshot = snapshot
|
321
|
+
end
|
322
|
+
|
323
|
+
def low_memory_notification
|
324
|
+
GC.start
|
325
|
+
end
|
326
|
+
|
327
|
+
def idle_notification(idle_time)
|
328
|
+
true
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
class Platform
|
333
|
+
def self.set_flag_as_str!(flag)
|
334
|
+
raise TypeError, "wrong type argument #{flag.class} (should be a string)" unless flag.is_a?(String)
|
335
|
+
raise MiniRacer::PlatformAlreadyInitialized, "The platform is already initialized." if Context.instance_variable_get(:@context_initialized)
|
336
|
+
Context.instance_variable_set(:@use_strict, true) if "--use_strict" == flag
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
class Snapshot
|
341
|
+
def load(str)
|
342
|
+
raise TypeError, "wrong type argument #{str.class} (should be a string)" unless str.is_a?(String)
|
343
|
+
# Intentionally noop since TruffleRuby mocks the snapshot API
|
344
|
+
end
|
345
|
+
|
346
|
+
def warmup_unsafe!(src)
|
347
|
+
raise TypeError, "wrong type argument #{src.class} (should be a string)" unless src.is_a?(String)
|
348
|
+
# Intentionally noop since TruffleRuby mocks the snapshot API
|
349
|
+
# by replaying snapshot source before the first eval/call
|
350
|
+
self
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
data/lib/mini_racer/version.rb
CHANGED
data/lib/mini_racer.rb
CHANGED
@@ -1,17 +1,22 @@
|
|
1
1
|
require "mini_racer/version"
|
2
|
-
require "mini_racer_loader"
|
3
2
|
require "pathname"
|
4
3
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
4
|
+
if RUBY_ENGINE == "truffleruby"
|
5
|
+
require "mini_racer/truffleruby"
|
6
|
+
else
|
7
|
+
require "mini_racer_loader"
|
8
|
+
ext_filename = "mini_racer_extension.#{RbConfig::CONFIG['DLEXT']}"
|
9
|
+
ext_path = Gem.loaded_specs['mini_racer'].require_paths
|
10
|
+
.map { |p| (p = Pathname.new(p)).absolute? ? p : Pathname.new(__dir__).parent + p }
|
11
|
+
ext_found = ext_path.map { |p| p + ext_filename }.find { |p| p.file? }
|
12
|
+
|
13
|
+
raise LoadError, "Could not find #{ext_filename} in #{ext_path.map(&:to_s)}" unless ext_found
|
14
|
+
MiniRacer::Loader.load(ext_found.to_s)
|
15
|
+
end
|
12
16
|
|
13
17
|
require "thread"
|
14
18
|
require "json"
|
19
|
+
require "io/wait"
|
15
20
|
|
16
21
|
module MiniRacer
|
17
22
|
|
@@ -202,7 +207,7 @@ module MiniRacer
|
|
202
207
|
end
|
203
208
|
|
204
209
|
if !(File === f)
|
205
|
-
raise ArgumentError
|
210
|
+
raise ArgumentError, "file_or_io"
|
206
211
|
end
|
207
212
|
|
208
213
|
write_heap_snapshot_unsafe(f)
|
@@ -349,7 +354,7 @@ module MiniRacer
|
|
349
354
|
|
350
355
|
t = Thread.new do
|
351
356
|
begin
|
352
|
-
result =
|
357
|
+
result = rp.wait_readable(@timeout/1000.0)
|
353
358
|
if !result
|
354
359
|
mutex.synchronize do
|
355
360
|
stop unless done
|
@@ -366,7 +371,7 @@ module MiniRacer
|
|
366
371
|
done = true
|
367
372
|
end
|
368
373
|
|
369
|
-
wp.
|
374
|
+
wp.close
|
370
375
|
|
371
376
|
# ensure we do not leak a thread in state
|
372
377
|
t.join
|
@@ -375,12 +380,9 @@ module MiniRacer
|
|
375
380
|
rval
|
376
381
|
ensure
|
377
382
|
# exceptions need to be handled
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
end
|
382
|
-
wp.close if wp
|
383
|
-
rp.close if rp
|
383
|
+
wp&.close
|
384
|
+
t&.join
|
385
|
+
rp&.close
|
384
386
|
end
|
385
387
|
|
386
388
|
def check_init_options!(isolate:, snapshot:, max_memory:, marshal_stack_depth:, ensure_gc_after_idle:, timeout:)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mini_racer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Saffron
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-08-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -106,7 +106,6 @@ files:
|
|
106
106
|
- ".dockerignore"
|
107
107
|
- ".github/workflows/ci.yml"
|
108
108
|
- ".gitignore"
|
109
|
-
- ".travis.yml"
|
110
109
|
- CHANGELOG
|
111
110
|
- CODE_OF_CONDUCT.md
|
112
111
|
- Dockerfile
|
@@ -121,6 +120,7 @@ files:
|
|
121
120
|
- ext/mini_racer_loader/extconf.rb
|
122
121
|
- ext/mini_racer_loader/mini_racer_loader.c
|
123
122
|
- lib/mini_racer.rb
|
123
|
+
- lib/mini_racer/truffleruby.rb
|
124
124
|
- lib/mini_racer/version.rb
|
125
125
|
- mini_racer.gemspec
|
126
126
|
homepage: https://github.com/discourse/mini_racer
|
@@ -128,10 +128,10 @@ licenses:
|
|
128
128
|
- MIT
|
129
129
|
metadata:
|
130
130
|
bug_tracker_uri: https://github.com/discourse/mini_racer/issues
|
131
|
-
changelog_uri: https://github.com/discourse/mini_racer/blob/v0.
|
132
|
-
documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.
|
133
|
-
source_code_uri: https://github.com/discourse/mini_racer/tree/v0.
|
134
|
-
post_install_message:
|
131
|
+
changelog_uri: https://github.com/discourse/mini_racer/blob/v0.6.3/CHANGELOG
|
132
|
+
documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.6.3
|
133
|
+
source_code_uri: https://github.com/discourse/mini_racer/tree/v0.6.3
|
134
|
+
post_install_message:
|
135
135
|
rdoc_options: []
|
136
136
|
require_paths:
|
137
137
|
- lib
|
@@ -147,8 +147,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
147
147
|
- !ruby/object:Gem::Version
|
148
148
|
version: '0'
|
149
149
|
requirements: []
|
150
|
-
rubygems_version: 3.
|
151
|
-
signing_key:
|
150
|
+
rubygems_version: 3.3.20
|
151
|
+
signing_key:
|
152
152
|
specification_version: 4
|
153
153
|
summary: Minimal embedded v8 for Ruby
|
154
154
|
test_files: []
|
data/.travis.yml
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
language: ruby
|
2
|
-
os: linux
|
3
|
-
rvm:
|
4
|
-
- 2.6
|
5
|
-
- 2.7
|
6
|
-
- 3.0
|
7
|
-
- ruby-head
|
8
|
-
arch:
|
9
|
-
- amd64
|
10
|
-
- arm64
|
11
|
-
jobs:
|
12
|
-
include:
|
13
|
-
- rvm: 2.6
|
14
|
-
os: osx
|
15
|
-
osx_image: xcode11.3
|
16
|
-
- rvm: 2.6
|
17
|
-
os: osx
|
18
|
-
osx_image: xcode12.2
|
19
|
-
- rvm: 2.7
|
20
|
-
os: osx
|
21
|
-
osx_image: xcode12.2
|
22
|
-
dist: xenial
|
23
|
-
cache: bundler
|