mini_racer 0.5.0 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|