mini_racer 0.21.3 → 0.22.0
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/CHANGELOG +11 -0
- data/README.md +34 -0
- data/ext/mini_racer_extension/mini_racer_extension.c +232 -59
- data/ext/mini_racer_extension/mini_racer_v8.cc +188 -17
- data/ext/mini_racer_extension/mini_racer_v8.h +6 -1
- data/lib/mini_racer/shared.rb +151 -111
- data/lib/mini_racer/truffleruby.rb +89 -66
- data/lib/mini_racer/version.rb +1 -1
- data/lib/mini_racer.rb +17 -9
- metadata +33 -5
data/lib/mini_racer/shared.rb
CHANGED
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
# Maybe one day it can be actually shared again between both backends.
|
|
5
5
|
|
|
6
6
|
module MiniRacer
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
MARSHAL_STACKDEPTH_MAX_VALUE = 2**10-2
|
|
7
|
+
MARSHAL_STACKDEPTH_DEFAULT = (2**9) - 2
|
|
8
|
+
MARSHAL_STACKDEPTH_MAX_VALUE = (2**10) - 2
|
|
10
9
|
|
|
11
10
|
class FailedV8Conversion
|
|
12
11
|
attr_reader :info
|
|
@@ -25,7 +24,8 @@ module MiniRacer
|
|
|
25
24
|
class Isolate
|
|
26
25
|
def initialize(snapshot = nil)
|
|
27
26
|
unless snapshot.nil? || snapshot.is_a?(Snapshot)
|
|
28
|
-
raise ArgumentError,
|
|
27
|
+
raise ArgumentError,
|
|
28
|
+
"snapshot must be a Snapshot object, passed a #{snapshot.inspect}"
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
# defined in the C class
|
|
@@ -51,12 +51,10 @@ module MiniRacer
|
|
|
51
51
|
# normalize flags to strings, and adds leading dashes if needed
|
|
52
52
|
def flag_to_string(flag)
|
|
53
53
|
if flag.is_a?(Hash)
|
|
54
|
-
flag.map
|
|
55
|
-
"#{flag_to_string(key)} #{value}"
|
|
56
|
-
end
|
|
54
|
+
flag.map { |key, value| "#{flag_to_string(key)} #{value}" }
|
|
57
55
|
else
|
|
58
56
|
str = flag.to_s
|
|
59
|
-
str = "--#{str}" unless str.start_with?(
|
|
57
|
+
str = "--#{str}" unless str.start_with?("--")
|
|
60
58
|
str
|
|
61
59
|
end
|
|
62
60
|
end
|
|
@@ -65,13 +63,12 @@ module MiniRacer
|
|
|
65
63
|
|
|
66
64
|
# eval is defined in the C class
|
|
67
65
|
class Context
|
|
68
|
-
|
|
69
66
|
class ExternalFunction
|
|
70
67
|
def initialize(name, callback, parent)
|
|
71
68
|
unless String === name
|
|
72
69
|
raise ArgumentError, "parent_object must be a String"
|
|
73
70
|
end
|
|
74
|
-
parent_object, _
|
|
71
|
+
parent_object, _, @name = name.rpartition(".")
|
|
75
72
|
@callback = callback
|
|
76
73
|
@parent = parent
|
|
77
74
|
@parent_object_eval = nil
|
|
@@ -83,26 +80,40 @@ module MiniRacer
|
|
|
83
80
|
@parent_object_eval = ""
|
|
84
81
|
prev = ""
|
|
85
82
|
first = true
|
|
86
|
-
parent_object
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
83
|
+
parent_object
|
|
84
|
+
.split(".")
|
|
85
|
+
.each do |obj|
|
|
86
|
+
prev << obj
|
|
87
|
+
if first
|
|
88
|
+
@parent_object_eval << "if (typeof #{prev} !== 'object' || typeof #{prev} !== 'function') { #{prev} = {} };\n"
|
|
89
|
+
else
|
|
90
|
+
@parent_object_eval << "#{prev} = #{prev} || {};\n"
|
|
91
|
+
end
|
|
92
|
+
prev << "."
|
|
93
|
+
first = false
|
|
92
94
|
end
|
|
93
|
-
prev << "."
|
|
94
|
-
first = false
|
|
95
|
-
end
|
|
96
95
|
@parent_object_eval << "#{parent_object};"
|
|
97
96
|
end
|
|
98
97
|
notify_v8
|
|
99
98
|
end
|
|
100
99
|
end
|
|
101
100
|
|
|
102
|
-
def initialize(
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
101
|
+
def initialize(
|
|
102
|
+
max_memory: nil,
|
|
103
|
+
timeout: nil,
|
|
104
|
+
isolate: nil,
|
|
105
|
+
ensure_gc_after_idle: nil,
|
|
106
|
+
snapshot: nil,
|
|
107
|
+
marshal_stack_depth: nil
|
|
108
|
+
)
|
|
109
|
+
check_init_options!(
|
|
110
|
+
isolate: isolate,
|
|
111
|
+
snapshot: snapshot,
|
|
112
|
+
max_memory: max_memory,
|
|
113
|
+
marshal_stack_depth: marshal_stack_depth,
|
|
114
|
+
ensure_gc_after_idle: ensure_gc_after_idle,
|
|
115
|
+
timeout: timeout
|
|
116
|
+
)
|
|
106
117
|
|
|
107
118
|
@functions = {}
|
|
108
119
|
@timeout = nil
|
|
@@ -140,17 +151,20 @@ module MiniRacer
|
|
|
140
151
|
@isolate = create_isolate_value
|
|
141
152
|
end
|
|
142
153
|
|
|
143
|
-
def eval(str, options=nil)
|
|
144
|
-
|
|
154
|
+
def eval(str, options = nil)
|
|
155
|
+
if @disposed
|
|
156
|
+
raise(
|
|
157
|
+
ContextDisposedError,
|
|
158
|
+
"attempted to call eval on a disposed context!"
|
|
159
|
+
)
|
|
160
|
+
end
|
|
145
161
|
|
|
146
162
|
filename = options && options[:filename].to_s
|
|
147
163
|
|
|
148
164
|
@eval_thread = Thread.current
|
|
149
165
|
isolate_mutex.synchronize do
|
|
150
166
|
@current_exception = nil
|
|
151
|
-
timeout
|
|
152
|
-
eval_unsafe(str, filename)
|
|
153
|
-
end
|
|
167
|
+
timeout { eval_unsafe(str, filename) }
|
|
154
168
|
end
|
|
155
169
|
ensure
|
|
156
170
|
@eval_thread = nil
|
|
@@ -158,19 +172,30 @@ module MiniRacer
|
|
|
158
172
|
end
|
|
159
173
|
|
|
160
174
|
def call(function_name, *arguments)
|
|
161
|
-
|
|
175
|
+
if @disposed
|
|
176
|
+
raise(
|
|
177
|
+
ContextDisposedError,
|
|
178
|
+
"attempted to call function on a disposed context!"
|
|
179
|
+
)
|
|
180
|
+
end
|
|
162
181
|
|
|
163
182
|
@eval_thread = Thread.current
|
|
164
183
|
isolate_mutex.synchronize do
|
|
165
|
-
timeout
|
|
166
|
-
call_unsafe(function_name, *arguments)
|
|
167
|
-
end
|
|
184
|
+
timeout { call_unsafe(function_name, *arguments) }
|
|
168
185
|
end
|
|
169
186
|
ensure
|
|
170
187
|
@eval_thread = nil
|
|
171
188
|
ensure_gc_thread if @ensure_gc_after_idle
|
|
172
189
|
end
|
|
173
190
|
|
|
191
|
+
def eval_await(*, **)
|
|
192
|
+
raise MiniRacer::Error, "eval_await is not supported on TruffleRuby"
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def call_await(*, **)
|
|
196
|
+
raise MiniRacer::Error, "call_await is not supported on TruffleRuby"
|
|
197
|
+
end
|
|
198
|
+
|
|
174
199
|
def dispose
|
|
175
200
|
return if @disposed
|
|
176
201
|
isolate_mutex.synchronize do
|
|
@@ -181,43 +206,36 @@ module MiniRacer
|
|
|
181
206
|
end
|
|
182
207
|
end
|
|
183
208
|
|
|
184
|
-
|
|
185
209
|
def attach(name, callback)
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
210
|
+
if @disposed
|
|
211
|
+
raise(
|
|
212
|
+
ContextDisposedError,
|
|
213
|
+
"attempted to call function on a disposed context!"
|
|
214
|
+
)
|
|
215
|
+
end
|
|
192
216
|
|
|
217
|
+
wrapped =
|
|
218
|
+
lambda do |*args|
|
|
193
219
|
begin
|
|
194
|
-
|
|
195
|
-
@callback_running = true
|
|
196
|
-
}
|
|
197
|
-
r = callback.call(*args)
|
|
198
|
-
ensure
|
|
199
|
-
@callback_mutex.synchronize{
|
|
200
|
-
@callback_running = false
|
|
201
|
-
}
|
|
202
|
-
end
|
|
220
|
+
r = nil
|
|
203
221
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
sleep 2
|
|
222
|
+
begin
|
|
223
|
+
@callback_mutex.synchronize { @callback_running = true }
|
|
224
|
+
r = callback.call(*args)
|
|
225
|
+
ensure
|
|
226
|
+
@callback_mutex.synchronize { @callback_running = false }
|
|
210
227
|
end
|
|
211
|
-
}
|
|
212
228
|
|
|
213
|
-
|
|
229
|
+
# wait up to 2 seconds for this to be interrupted
|
|
230
|
+
# will very rarely be called cause #raise is called
|
|
231
|
+
# in another mutex
|
|
232
|
+
@callback_mutex.synchronize { sleep 2 if @thread_raise_called }
|
|
214
233
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
@thread_raise_called = false
|
|
218
|
-
|
|
234
|
+
r
|
|
235
|
+
ensure
|
|
236
|
+
@callback_mutex.synchronize { @thread_raise_called = false }
|
|
237
|
+
end
|
|
219
238
|
end
|
|
220
|
-
end
|
|
221
239
|
|
|
222
240
|
isolate_mutex.synchronize do
|
|
223
241
|
external = ExternalFunction.new(name, wrapped, self)
|
|
@@ -232,41 +250,43 @@ module MiniRacer
|
|
|
232
250
|
@ensure_gc_mutex.synchronize do
|
|
233
251
|
@ensure_gc_thread = nil if !@ensure_gc_thread&.alive?
|
|
234
252
|
return if !Thread.main.alive? # avoid "can't alloc thread" exception
|
|
235
|
-
@ensure_gc_thread ||=
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
@
|
|
243
|
-
|
|
244
|
-
|
|
253
|
+
@ensure_gc_thread ||=
|
|
254
|
+
Thread.new do
|
|
255
|
+
ensure_gc_after_idle_seconds = @ensure_gc_after_idle / 1000.0
|
|
256
|
+
done = false
|
|
257
|
+
while !done
|
|
258
|
+
now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
259
|
+
|
|
260
|
+
if @disposed
|
|
261
|
+
@ensure_gc_thread = nil
|
|
262
|
+
break
|
|
263
|
+
end
|
|
245
264
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
265
|
+
if !@eval_thread &&
|
|
266
|
+
ensure_gc_after_idle_seconds < now - @last_eval
|
|
267
|
+
@ensure_gc_mutex.synchronize do
|
|
268
|
+
isolate_mutex.synchronize do
|
|
269
|
+
if !@eval_thread
|
|
270
|
+
low_memory_notification if !@disposed
|
|
271
|
+
@ensure_gc_thread = nil
|
|
272
|
+
done = true
|
|
273
|
+
end
|
|
253
274
|
end
|
|
254
275
|
end
|
|
255
276
|
end
|
|
277
|
+
sleep ensure_gc_after_idle_seconds if !done
|
|
256
278
|
end
|
|
257
|
-
sleep ensure_gc_after_idle_seconds if !done
|
|
258
279
|
end
|
|
259
|
-
end
|
|
260
280
|
end
|
|
261
281
|
end
|
|
262
282
|
|
|
263
283
|
def stop_attached
|
|
264
|
-
@callback_mutex.synchronize
|
|
284
|
+
@callback_mutex.synchronize do
|
|
265
285
|
if @callback_running
|
|
266
286
|
@eval_thread.raise ScriptTerminatedError, "Terminated during callback"
|
|
267
287
|
@thread_raise_called = true
|
|
268
288
|
end
|
|
269
|
-
|
|
289
|
+
end
|
|
270
290
|
end
|
|
271
291
|
|
|
272
292
|
def timeout(&blk)
|
|
@@ -275,26 +295,21 @@ module MiniRacer
|
|
|
275
295
|
mutex = Mutex.new
|
|
276
296
|
done = false
|
|
277
297
|
|
|
278
|
-
rp,wp = IO.pipe
|
|
298
|
+
rp, wp = IO.pipe
|
|
279
299
|
|
|
280
|
-
t =
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
mutex.synchronize
|
|
285
|
-
|
|
286
|
-
|
|
300
|
+
t =
|
|
301
|
+
Thread.new do
|
|
302
|
+
begin
|
|
303
|
+
result = rp.wait_readable(@timeout / 1000.0)
|
|
304
|
+
mutex.synchronize { stop unless done } if !result
|
|
305
|
+
rescue => e
|
|
306
|
+
STDERR.puts e
|
|
307
|
+
STDERR.puts "FAILED TO TERMINATE DUE TO TIMEOUT"
|
|
287
308
|
end
|
|
288
|
-
rescue => e
|
|
289
|
-
STDERR.puts e
|
|
290
|
-
STDERR.puts "FAILED TO TERMINATE DUE TO TIMEOUT"
|
|
291
309
|
end
|
|
292
|
-
end
|
|
293
310
|
|
|
294
311
|
rval = blk.call
|
|
295
|
-
mutex.synchronize
|
|
296
|
-
done = true
|
|
297
|
-
end
|
|
312
|
+
mutex.synchronize { done = true }
|
|
298
313
|
|
|
299
314
|
wp.close
|
|
300
315
|
|
|
@@ -310,44 +325,69 @@ module MiniRacer
|
|
|
310
325
|
rp&.close
|
|
311
326
|
end
|
|
312
327
|
|
|
313
|
-
def check_init_options!(
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
328
|
+
def check_init_options!(
|
|
329
|
+
isolate:,
|
|
330
|
+
snapshot:,
|
|
331
|
+
max_memory:,
|
|
332
|
+
marshal_stack_depth:,
|
|
333
|
+
ensure_gc_after_idle:,
|
|
334
|
+
timeout:
|
|
335
|
+
)
|
|
336
|
+
assert_option_is_nil_or_a("isolate", isolate, Isolate)
|
|
337
|
+
assert_option_is_nil_or_a("snapshot", snapshot, Snapshot)
|
|
338
|
+
|
|
339
|
+
assert_numeric_or_nil(
|
|
340
|
+
"max_memory",
|
|
341
|
+
max_memory,
|
|
342
|
+
min_value: 10_000,
|
|
343
|
+
max_value: (2**32) - 1
|
|
344
|
+
)
|
|
345
|
+
assert_numeric_or_nil(
|
|
346
|
+
"marshal_stack_depth",
|
|
347
|
+
marshal_stack_depth,
|
|
348
|
+
min_value: 1,
|
|
349
|
+
max_value: MARSHAL_STACKDEPTH_MAX_VALUE
|
|
350
|
+
)
|
|
351
|
+
assert_numeric_or_nil(
|
|
352
|
+
"ensure_gc_after_idle",
|
|
353
|
+
ensure_gc_after_idle,
|
|
354
|
+
min_value: 1
|
|
355
|
+
)
|
|
356
|
+
assert_numeric_or_nil("timeout", timeout, min_value: 1)
|
|
321
357
|
|
|
322
358
|
if isolate && snapshot
|
|
323
|
-
raise ArgumentError,
|
|
359
|
+
raise ArgumentError, "can only pass one of isolate and snapshot options"
|
|
324
360
|
end
|
|
325
361
|
end
|
|
326
362
|
|
|
327
363
|
def assert_numeric_or_nil(option_name, object, min_value:, max_value: nil)
|
|
328
364
|
if max_value && object.is_a?(Numeric) && object > max_value
|
|
329
|
-
raise ArgumentError,
|
|
365
|
+
raise ArgumentError,
|
|
366
|
+
"#{option_name} must be less than or equal to #{max_value}"
|
|
330
367
|
end
|
|
331
368
|
|
|
332
369
|
if object.is_a?(Numeric) && object < min_value
|
|
333
|
-
raise ArgumentError,
|
|
370
|
+
raise ArgumentError,
|
|
371
|
+
"#{option_name} must be larger than or equal to #{min_value}"
|
|
334
372
|
end
|
|
335
373
|
|
|
336
374
|
if !object.nil? && !object.is_a?(Numeric)
|
|
337
|
-
raise ArgumentError,
|
|
375
|
+
raise ArgumentError,
|
|
376
|
+
"#{option_name} must be a number, passed a #{object.inspect}"
|
|
338
377
|
end
|
|
339
378
|
end
|
|
340
379
|
|
|
341
380
|
def assert_option_is_nil_or_a(option_name, object, klass)
|
|
342
381
|
unless object.nil? || object.is_a?(klass)
|
|
343
|
-
raise ArgumentError,
|
|
382
|
+
raise ArgumentError,
|
|
383
|
+
"#{option_name} must be a #{klass} object, passed a #{object.inspect}"
|
|
344
384
|
end
|
|
345
385
|
end
|
|
346
386
|
end
|
|
347
387
|
|
|
348
388
|
# `size` and `warmup!` public methods are defined in the C class
|
|
349
389
|
class Snapshot
|
|
350
|
-
def initialize(str =
|
|
390
|
+
def initialize(str = "")
|
|
351
391
|
# ensure it first can load
|
|
352
392
|
begin
|
|
353
393
|
ctx = MiniRacer::Context.new
|