mini_racer_rs 1.0.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.
Files changed (97) hide show
  1. checksums.yaml +7 -0
  2. data/AI-DISCLOSURE.md +13 -0
  3. data/CHANGELOG.md +45 -0
  4. data/COMPATIBILITY.md +235 -0
  5. data/Cargo.lock +1263 -0
  6. data/Cargo.toml +62 -0
  7. data/LICENSE +21 -0
  8. data/README.md +254 -0
  9. data/THIRD_PARTY_NOTICES.md +4501 -0
  10. data/crates/mini_racer_engine/Cargo.toml +25 -0
  11. data/crates/mini_racer_engine/build.rs +13 -0
  12. data/crates/mini_racer_engine/src/context.rs +802 -0
  13. data/crates/mini_racer_engine/src/control.rs +239 -0
  14. data/crates/mini_racer_engine/src/error.rs +88 -0
  15. data/crates/mini_racer_engine/src/fast_calls.rs +681 -0
  16. data/crates/mini_racer_engine/src/lib.rs +20 -0
  17. data/crates/mini_racer_engine/src/notify.rs +107 -0
  18. data/crates/mini_racer_engine/src/process/code_cache.rs +412 -0
  19. data/crates/mini_racer_engine/src/process/fork.rs +261 -0
  20. data/crates/mini_racer_engine/src/process/platform.rs +194 -0
  21. data/crates/mini_racer_engine/src/process/tasks.rs +96 -0
  22. data/crates/mini_racer_engine/src/process/watchdog.rs +185 -0
  23. data/crates/mini_racer_engine/src/process/workers.rs +165 -0
  24. data/crates/mini_racer_engine/src/process.rs +69 -0
  25. data/crates/mini_racer_engine/src/runner.rs +1560 -0
  26. data/crates/mini_racer_engine/src/serialize.rs +253 -0
  27. data/crates/mini_racer_engine/src/snapshot.rs +221 -0
  28. data/crates/mini_racer_engine/src/spin.rs +23 -0
  29. data/crates/mini_racer_engine/src/sys.rs +566 -0
  30. data/crates/mini_racer_sync/Cargo.toml +18 -0
  31. data/crates/mini_racer_sync/src/channel.rs +621 -0
  32. data/crates/mini_racer_sync/src/fork.rs +116 -0
  33. data/crates/mini_racer_sync/src/lib.rs +20 -0
  34. data/crates/mini_racer_sync/src/lock.rs +49 -0
  35. data/crates/mini_racer_sync/src/parker.rs +44 -0
  36. data/crates/mini_racer_sync/src/primitives.rs +10 -0
  37. data/crates/mini_racer_sync/src/tasks.rs +345 -0
  38. data/crates/mini_racer_sync/src/termination.rs +1234 -0
  39. data/crates/mini_racer_sync/src/workers.rs +283 -0
  40. data/crates/mini_racer_wire/Cargo.toml +18 -0
  41. data/crates/mini_racer_wire/src/decode.rs +926 -0
  42. data/crates/mini_racer_wire/src/lib.rs +67 -0
  43. data/crates/mini_racer_wire/src/marker.rs +43 -0
  44. data/crates/mini_racer_wire/src/text.rs +106 -0
  45. data/crates/mini_racer_wire/src/write.rs +187 -0
  46. data/ext/mini_racer_rs/Cargo.toml +26 -0
  47. data/ext/mini_racer_rs/build.rs +16 -0
  48. data/ext/mini_racer_rs/extconf.rb +78 -0
  49. data/ext/mini_racer_rs/src/context.rs +485 -0
  50. data/ext/mini_racer_rs/src/decode.rs +450 -0
  51. data/ext/mini_racer_rs/src/encode.rs +333 -0
  52. data/ext/mini_racer_rs/src/errors.rs +156 -0
  53. data/ext/mini_racer_rs/src/fork.rs +31 -0
  54. data/ext/mini_racer_rs/src/gc_root.rs +117 -0
  55. data/ext/mini_racer_rs/src/guard.rs +7 -0
  56. data/ext/mini_racer_rs/src/host.rs +339 -0
  57. data/ext/mini_racer_rs/src/lib.rs +44 -0
  58. data/ext/mini_racer_rs/src/options.rs +269 -0
  59. data/ext/mini_racer_rs/src/platform.rs +42 -0
  60. data/ext/mini_racer_rs/src/rb_raw.rs +368 -0
  61. data/ext/mini_racer_rs/src/snapshot.rs +149 -0
  62. data/ext/mini_racer_rs/v8-archives.sha256 +8 -0
  63. data/lib/mini_racer/shared.rb +420 -0
  64. data/lib/mini_racer/truffleruby.rb +439 -0
  65. data/lib/mini_racer/version.rb +6 -0
  66. data/lib/mini_racer.rb +3 -0
  67. data/lib/mini_racer_rs/internal_error.rb +7 -0
  68. data/lib/mini_racer_rs/version.rb +5 -0
  69. data/lib/mini_racer_rs/vocabulary.rb +105 -0
  70. data/lib/mini_racer_rs.rb +33 -0
  71. data/licenses/mini_racer.txt +21 -0
  72. data/licenses/rust/SOURCES.tsv +6 -0
  73. data/licenses/rust/std/COPYRIGHT +34 -0
  74. data/licenses/rust/std/COPYRIGHT-library.html +29357 -0
  75. data/licenses/rust/std/LICENSE-APACHE +176 -0
  76. data/licenses/rust/std/LICENSE-MIT +25 -0
  77. data/licenses/v8/SOURCES.tsv +20 -0
  78. data/licenses/v8/abseil-cpp/LICENSE +203 -0
  79. data/licenses/v8/chromium-build/LICENSE +27 -0
  80. data/licenses/v8/components.tsv +29 -0
  81. data/licenses/v8/dragonbox/LICENSE-Boost +23 -0
  82. data/licenses/v8/fast_float/LICENSE-MIT +27 -0
  83. data/licenses/v8/highway/LICENSE +371 -0
  84. data/licenses/v8/icu/LICENSE +568 -0
  85. data/licenses/v8/inspector_protocol/LICENSE +27 -0
  86. data/licenses/v8/libc++/LICENSE.TXT +311 -0
  87. data/licenses/v8/libc++abi/LICENSE.TXT +311 -0
  88. data/licenses/v8/llvm-libc/LICENSE.TXT +278 -0
  89. data/licenses/v8/rapidhash/LICENSE +34 -0
  90. data/licenses/v8/simdutf/LICENSE +18 -0
  91. data/licenses/v8/siphash/LICENSE +116 -0
  92. data/licenses/v8/utf8-decoder/LICENSE +19 -0
  93. data/licenses/v8/v8/LICENSE +61 -0
  94. data/licenses/v8/v8/LICENSE.fdlibm +6 -0
  95. data/licenses/v8/v8/LICENSE.strongtalk +29 -0
  96. data/licenses/v8/v8/LICENSE.v8 +26 -0
  97. metadata +156 -0
@@ -0,0 +1,420 @@
1
+ # This code used to be shared in lib/mini_racer.rb
2
+ # but was moved to the extension with https://github.com/rubyjs/mini_racer/pull/325.
3
+ # So now this is effectively duplicate logic with C/C++ code.
4
+ # Maybe one day it can be actually shared again between both backends.
5
+
6
+ module MiniRacer
7
+ MARSHAL_STACKDEPTH_DEFAULT = (2**9) - 2
8
+ MARSHAL_STACKDEPTH_MAX_VALUE = (2**10) - 2
9
+
10
+ class FailedV8Conversion
11
+ attr_reader :info
12
+ def initialize(info)
13
+ @info = info
14
+ end
15
+ end
16
+
17
+ # helper class returned when we have a JavaScript function
18
+ class JavaScriptFunction
19
+ def to_s
20
+ "JavaScript Function"
21
+ end
22
+ end
23
+
24
+ class Isolate
25
+ def initialize(snapshot = nil)
26
+ unless snapshot.nil? || snapshot.is_a?(Snapshot)
27
+ raise ArgumentError,
28
+ "snapshot must be a Snapshot object, passed a #{snapshot.inspect}"
29
+ end
30
+
31
+ # defined in the C class
32
+ init_with_snapshot(snapshot)
33
+ end
34
+ end
35
+
36
+ class Platform
37
+ class << self
38
+ def set_flags!(*args, **kwargs)
39
+ flags_to_strings([args, kwargs]).each do |flag|
40
+ # defined in the C class
41
+ set_flag_as_str!(flag)
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def flags_to_strings(flags)
48
+ flags.flatten.map { |flag| flag_to_string(flag) }.flatten
49
+ end
50
+
51
+ # normalize flags to strings, and adds leading dashes if needed
52
+ def flag_to_string(flag)
53
+ if flag.is_a?(Hash)
54
+ flag.map { |key, value| "#{flag_to_string(key)} #{value}" }
55
+ else
56
+ str = flag.to_s
57
+ str = "--#{str}" unless str.start_with?("--")
58
+ str
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ # eval is defined in the C class
65
+ class Context
66
+ class ExternalFunction
67
+ def initialize(name, callback, parent)
68
+ unless String === name
69
+ raise ArgumentError, "parent_object must be a String"
70
+ end
71
+ parent_object, _, @name = name.rpartition(".")
72
+ @callback = callback
73
+ @parent = parent
74
+ @parent_object_eval = nil
75
+ @parent_object = nil
76
+
77
+ unless parent_object.empty?
78
+ @parent_object = parent_object
79
+
80
+ @parent_object_eval = ""
81
+ prev = ""
82
+ first = true
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
94
+ end
95
+ @parent_object_eval << "#{parent_object};"
96
+ end
97
+ notify_v8
98
+ end
99
+ end
100
+
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
+ )
117
+
118
+ @functions = {}
119
+ @timeout = nil
120
+ @max_memory = nil
121
+ @current_exception = nil
122
+ @timeout = timeout
123
+ @max_memory = max_memory
124
+ @marshal_stack_depth = marshal_stack_depth
125
+
126
+ # false signals it should be fetched if requested
127
+ @isolate = isolate || false
128
+
129
+ @ensure_gc_after_idle = ensure_gc_after_idle
130
+
131
+ if @ensure_gc_after_idle
132
+ @last_eval = nil
133
+ @ensure_gc_thread = nil
134
+ @ensure_gc_mutex = Mutex.new
135
+ end
136
+
137
+ @disposed = false
138
+
139
+ @callback_mutex = Mutex.new
140
+ @callback_running = false
141
+ @thread_raise_called = false
142
+ @eval_thread = nil
143
+
144
+ # defined in the C class
145
+ init_unsafe(isolate, snapshot)
146
+ end
147
+
148
+ def isolate
149
+ return @isolate if @isolate != false
150
+ # defined in the C class
151
+ @isolate = create_isolate_value
152
+ end
153
+
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
161
+
162
+ filename = options && options[:filename].to_s
163
+
164
+ @eval_thread = Thread.current
165
+ isolate_mutex.synchronize do
166
+ @current_exception = nil
167
+ timeout { eval_unsafe(str, filename) }
168
+ end
169
+ ensure
170
+ @eval_thread = nil
171
+ ensure_gc_thread if @ensure_gc_after_idle
172
+ end
173
+
174
+ def call(function_name, *arguments)
175
+ if @disposed
176
+ raise(
177
+ ContextDisposedError,
178
+ "attempted to call function on a disposed context!"
179
+ )
180
+ end
181
+
182
+ @eval_thread = Thread.current
183
+ isolate_mutex.synchronize do
184
+ timeout { call_unsafe(function_name, *arguments) }
185
+ end
186
+ ensure
187
+ @eval_thread = nil
188
+ ensure_gc_thread if @ensure_gc_after_idle
189
+ end
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
+
199
+ def dispose
200
+ return if @disposed
201
+ isolate_mutex.synchronize do
202
+ return if @disposed
203
+ dispose_unsafe
204
+ @disposed = true
205
+ @isolate = nil # allow it to be garbage collected, if set
206
+ end
207
+ end
208
+
209
+ def attach(name, callback)
210
+ if @disposed
211
+ raise(
212
+ ContextDisposedError,
213
+ "attempted to call function on a disposed context!"
214
+ )
215
+ end
216
+
217
+ wrapped =
218
+ lambda do |*args|
219
+ begin
220
+ r = nil
221
+
222
+ begin
223
+ @callback_mutex.synchronize { @callback_running = true }
224
+ r = callback.call(*args)
225
+ ensure
226
+ @callback_mutex.synchronize { @callback_running = false }
227
+ end
228
+
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 }
233
+
234
+ r
235
+ ensure
236
+ @callback_mutex.synchronize { @thread_raise_called = false }
237
+ end
238
+ end
239
+
240
+ isolate_mutex.synchronize do
241
+ external = ExternalFunction.new(name, wrapped, self)
242
+ @functions["#{name}"] = external
243
+ end
244
+ end
245
+
246
+ private
247
+
248
+ def ensure_gc_thread
249
+ @last_eval = Process.clock_gettime(Process::CLOCK_MONOTONIC)
250
+ @ensure_gc_mutex.synchronize do
251
+ @ensure_gc_thread = nil if !@ensure_gc_thread&.alive?
252
+ return if !Thread.main.alive? # avoid "can't alloc thread" exception
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
264
+
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
274
+ end
275
+ end
276
+ end
277
+ sleep ensure_gc_after_idle_seconds if !done
278
+ end
279
+ end
280
+ end
281
+ end
282
+
283
+ def stop_attached
284
+ @callback_mutex.synchronize do
285
+ if @callback_running
286
+ @eval_thread.raise ScriptTerminatedError, "Terminated during callback"
287
+ @thread_raise_called = true
288
+ end
289
+ end
290
+ end
291
+
292
+ def timeout(&blk)
293
+ return blk.call unless @timeout
294
+
295
+ mutex = Mutex.new
296
+ done = false
297
+
298
+ rp, wp = IO.pipe
299
+
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"
308
+ end
309
+ end
310
+
311
+ rval = blk.call
312
+ mutex.synchronize { done = true }
313
+
314
+ wp.close
315
+
316
+ # ensure we do not leak a thread in state
317
+ t.join
318
+ t = nil
319
+
320
+ rval
321
+ ensure
322
+ # exceptions need to be handled
323
+ wp&.close
324
+ t&.join
325
+ rp&.close
326
+ end
327
+
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)
357
+
358
+ if isolate && snapshot
359
+ raise ArgumentError, "can only pass one of isolate and snapshot options"
360
+ end
361
+ end
362
+
363
+ def assert_numeric_or_nil(option_name, object, min_value:, max_value: nil)
364
+ if max_value && object.is_a?(Numeric) && object > max_value
365
+ raise ArgumentError,
366
+ "#{option_name} must be less than or equal to #{max_value}"
367
+ end
368
+
369
+ if object.is_a?(Numeric) && object < min_value
370
+ raise ArgumentError,
371
+ "#{option_name} must be larger than or equal to #{min_value}"
372
+ end
373
+
374
+ if !object.nil? && !object.is_a?(Numeric)
375
+ raise ArgumentError,
376
+ "#{option_name} must be a number, passed a #{object.inspect}"
377
+ end
378
+ end
379
+
380
+ def assert_option_is_nil_or_a(option_name, object, klass)
381
+ unless object.nil? || object.is_a?(klass)
382
+ raise ArgumentError,
383
+ "#{option_name} must be a #{klass} object, passed a #{object.inspect}"
384
+ end
385
+ end
386
+ end
387
+
388
+ # `size` and `warmup!` public methods are defined in the C class
389
+ class Snapshot
390
+ def initialize(str = "")
391
+ # ensure it first can load
392
+ begin
393
+ ctx = MiniRacer::Context.new
394
+ ctx.eval(str)
395
+ rescue MiniRacer::RuntimeError => e
396
+ raise MiniRacer::SnapshotError, e.message, e.backtrace
397
+ end
398
+
399
+ @source = str
400
+
401
+ # defined in the C class
402
+ load(str)
403
+ end
404
+
405
+ def warmup!(src)
406
+ # we have to do something here
407
+ # we are bloating memory a bit but it is more correct
408
+ # than hitting an exception when attempty to compile invalid source
409
+ begin
410
+ ctx = MiniRacer::Context.new
411
+ ctx.eval(@source)
412
+ ctx.eval(src)
413
+ rescue MiniRacer::RuntimeError => e
414
+ raise MiniRacer::SnapshotError, e.message, e.backtrace
415
+ end
416
+
417
+ warmup_unsafe!(src)
418
+ end
419
+ end
420
+ end