mini_racer 0.17.0.pre5 → 0.17.0.pre6

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.
@@ -331,10 +331,37 @@ module MiniRacer
331
331
  end
332
332
 
333
333
  class Platform
334
- def self.set_flag_as_str!(flag)
335
- raise TypeError, "wrong type argument #{flag.class} (should be a string)" unless flag.is_a?(String)
336
- raise MiniRacer::PlatformAlreadyInitialized, "The platform is already initialized." if Context.instance_variable_get(:@context_initialized)
337
- Context.instance_variable_set(:@use_strict, true) if "--use_strict" == flag
334
+ class << self
335
+ def set_flags!(*args, **kwargs)
336
+ flags_to_strings([args, kwargs]).each do |flag|
337
+ set_flag_as_str!(flag)
338
+ end
339
+ end
340
+
341
+ private
342
+
343
+ def flags_to_strings(flags)
344
+ flags.flatten.map { |flag| flag_to_string(flag) }.flatten
345
+ end
346
+
347
+ # normalize flags to strings, and adds leading dashes if needed
348
+ def flag_to_string(flag)
349
+ if flag.is_a?(Hash)
350
+ flag.map do |key, value|
351
+ "#{flag_to_string(key)} #{value}"
352
+ end
353
+ else
354
+ str = flag.to_s
355
+ str = "--#{str}" unless str.start_with?('--')
356
+ str
357
+ end
358
+ end
359
+
360
+ def set_flag_as_str!(flag)
361
+ raise TypeError, "wrong type argument #{flag.class} (should be a string)" unless flag.is_a?(String)
362
+ raise MiniRacer::PlatformAlreadyInitialized, "The platform is already initialized." if Context.instance_variable_get(:@context_initialized)
363
+ Context.instance_variable_set(:@use_strict, true) if "--use_strict" == flag
364
+ end
338
365
  end
339
366
  end
340
367
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MiniRacer
4
- VERSION = "0.17.0.pre5"
4
+ VERSION = "0.17.0.pre6"
5
5
  LIBV8_NODE_VERSION = "~> 22.7.0.4"
6
6
  end
data/lib/mini_racer.rb CHANGED
@@ -28,14 +28,9 @@ require "json"
28
28
  require "io/wait"
29
29
 
30
30
  module MiniRacer
31
-
32
- MARSHAL_STACKDEPTH_DEFAULT = 2**9-2
33
- MARSHAL_STACKDEPTH_MAX_VALUE = 2**10-2
34
-
35
31
  class Error < ::StandardError; end
36
32
 
37
33
  class ContextDisposedError < Error; end
38
- class SnapshotError < Error; end
39
34
  class PlatformAlreadyInitialized < Error; end
40
35
 
41
36
  class EvalError < Error; end
@@ -43,163 +38,34 @@ module MiniRacer
43
38
  class ScriptTerminatedError < EvalError; end
44
39
  class V8OutOfMemoryError < EvalError; end
45
40
 
46
- class FailedV8Conversion
47
- attr_reader :info
48
- def initialize(info)
49
- @info = info
50
- end
51
- end
52
-
53
41
  class RuntimeError < EvalError
54
42
  def initialize(message)
55
- message, js_backtrace = message.split("\n", 2)
56
- if js_backtrace && !js_backtrace.empty?
57
- @js_backtrace = js_backtrace.split("\n")
58
- @js_backtrace.map!{|f| "JavaScript #{f.strip}"}
59
- else
60
- @js_backtrace = nil
61
- end
43
+ message, *@frames = message.split("\n")
44
+ @frames.map! { "JavaScript #{_1.strip}" }
62
45
  super(message)
63
46
  end
64
47
 
65
48
  def backtrace
66
- val = super
67
- return unless val
68
- if @js_backtrace
69
- @js_backtrace + val
70
- else
71
- val
72
- end
73
- end
74
- end
75
-
76
- # helper class returned when we have a JavaScript function
77
- class JavaScriptFunction
78
- def to_s
79
- "JavaScript Function"
49
+ frames = super
50
+ @frames + frames unless frames.nil?
80
51
  end
81
52
  end
82
53
 
83
- class Isolate
84
- def initialize(snapshot = nil)
85
- unless snapshot.nil? || snapshot.is_a?(Snapshot)
86
- raise ArgumentError, "snapshot must be a Snapshot object, passed a #{snapshot.inspect}"
87
- end
88
-
89
- # defined in the C class
90
- init_with_snapshot(snapshot)
54
+ class SnapshotError < Error
55
+ def initialize(message)
56
+ message, *@frames = message.split("\n")
57
+ @frames.map! { "JavaScript #{_1.strip}" }
58
+ super(message)
91
59
  end
92
- end
93
-
94
- class Platform
95
- class << self
96
- def set_flags!(*args, **kwargs)
97
- flags_to_strings([args, kwargs]).each do |flag|
98
- # defined in the C class
99
- set_flag_as_str!(flag)
100
- end
101
- end
102
-
103
- private
104
-
105
- def flags_to_strings(flags)
106
- flags.flatten.map { |flag| flag_to_string(flag) }.flatten
107
- end
108
60
 
109
- # normalize flags to strings, and adds leading dashes if needed
110
- def flag_to_string(flag)
111
- if flag.is_a?(Hash)
112
- flag.map do |key, value|
113
- "#{flag_to_string(key)} #{value}"
114
- end
115
- else
116
- str = flag.to_s
117
- str = "--#{str}" unless str.start_with?('--')
118
- str
119
- end
120
- end
61
+ def backtrace
62
+ frames = super
63
+ @frames + frames unless frames.nil?
121
64
  end
122
65
  end
123
66
 
124
- # eval is defined in the C class
125
67
  class Context
126
-
127
- class ExternalFunction
128
- def initialize(name, callback, parent)
129
- unless String === name
130
- raise ArgumentError, "parent_object must be a String"
131
- end
132
- parent_object, _ , @name = name.rpartition(".")
133
- @callback = callback
134
- @parent = parent
135
- @parent_object_eval = nil
136
- @parent_object = nil
137
-
138
- unless parent_object.empty?
139
- @parent_object = parent_object
140
-
141
- @parent_object_eval = ""
142
- prev = ""
143
- first = true
144
- parent_object.split(".").each do |obj|
145
- prev << obj
146
- if first
147
- @parent_object_eval << "if (typeof #{prev} === 'undefined') { #{prev} = {} };\n"
148
- else
149
- @parent_object_eval << "#{prev} = #{prev} || {};\n"
150
- end
151
- prev << "."
152
- first = false
153
- end
154
- @parent_object_eval << "#{parent_object};"
155
- end
156
- notify_v8
157
- end
158
- end
159
-
160
- def initialize(max_memory: nil, timeout: nil, isolate: nil, ensure_gc_after_idle: nil, snapshot: nil, marshal_stack_depth: nil)
161
- options ||= {}
162
-
163
- check_init_options!(isolate: isolate, snapshot: snapshot, max_memory: max_memory, marshal_stack_depth: marshal_stack_depth, ensure_gc_after_idle: ensure_gc_after_idle, timeout: timeout)
164
-
165
- @functions = {}
166
- @timeout = nil
167
- @max_memory = nil
168
- @current_exception = nil
169
- @timeout = timeout
170
- @max_memory = max_memory
171
- @marshal_stack_depth = marshal_stack_depth
172
-
173
- # false signals it should be fetched if requested
174
- @isolate = isolate || false
175
-
176
- @ensure_gc_after_idle = ensure_gc_after_idle
177
-
178
- if @ensure_gc_after_idle
179
- @last_eval = nil
180
- @ensure_gc_thread = nil
181
- @ensure_gc_mutex = Mutex.new
182
- end
183
-
184
- @disposed = false
185
-
186
- @callback_mutex = Mutex.new
187
- @callback_running = false
188
- @thread_raise_called = false
189
- @eval_thread = nil
190
-
191
- # defined in the C class
192
- init_unsafe(isolate, snapshot)
193
- end
194
-
195
- def isolate
196
- return @isolate if @isolate != false
197
- # defined in the C class
198
- @isolate = create_isolate_value
199
- end
200
-
201
68
  def load(filename)
202
- # TODO do this native cause no need to allocate VALUE here
203
69
  eval(File.read(filename))
204
70
  end
205
71
 
@@ -207,10 +73,9 @@ module MiniRacer
207
73
  f = nil
208
74
  implicit = false
209
75
 
210
-
211
76
  if String === file_or_io
212
77
  f = File.open(file_or_io, "w")
213
- implicit = true
78
+ implicit = true
214
79
  else
215
80
  f = file_or_io
216
81
  end
@@ -219,247 +84,9 @@ module MiniRacer
219
84
  raise ArgumentError, "file_or_io"
220
85
  end
221
86
 
222
- write_heap_snapshot_unsafe(f)
223
-
87
+ f.write(heap_snapshot())
224
88
  ensure
225
89
  f.close if implicit
226
90
  end
227
-
228
- def eval(str, options=nil)
229
- raise(ContextDisposedError, 'attempted to call eval on a disposed context!') if @disposed
230
-
231
- filename = options && options[:filename].to_s
232
-
233
- @eval_thread = Thread.current
234
- isolate_mutex.synchronize do
235
- @current_exception = nil
236
- timeout do
237
- eval_unsafe(str, filename)
238
- end
239
- end
240
- ensure
241
- @eval_thread = nil
242
- ensure_gc_thread if @ensure_gc_after_idle
243
- end
244
-
245
- def call(function_name, *arguments)
246
- raise(ContextDisposedError, 'attempted to call function on a disposed context!') if @disposed
247
-
248
- @eval_thread = Thread.current
249
- isolate_mutex.synchronize do
250
- timeout do
251
- call_unsafe(function_name, *arguments)
252
- end
253
- end
254
- ensure
255
- @eval_thread = nil
256
- ensure_gc_thread if @ensure_gc_after_idle
257
- end
258
-
259
- def dispose
260
- return if @disposed
261
- isolate_mutex.synchronize do
262
- return if @disposed
263
- dispose_unsafe
264
- @disposed = true
265
- @isolate = nil # allow it to be garbage collected, if set
266
- end
267
- end
268
-
269
-
270
- def attach(name, callback)
271
- raise(ContextDisposedError, 'attempted to call function on a disposed context!') if @disposed
272
-
273
- wrapped = lambda do |*args|
274
- begin
275
-
276
- r = nil
277
-
278
- begin
279
- @callback_mutex.synchronize{
280
- @callback_running = true
281
- }
282
- r = callback.call(*args)
283
- ensure
284
- @callback_mutex.synchronize{
285
- @callback_running = false
286
- }
287
- end
288
-
289
- # wait up to 2 seconds for this to be interrupted
290
- # will very rarely be called cause #raise is called
291
- # in another mutex
292
- @callback_mutex.synchronize {
293
- if @thread_raise_called
294
- sleep 2
295
- end
296
- }
297
-
298
- r
299
-
300
- ensure
301
- @callback_mutex.synchronize {
302
- @thread_raise_called = false
303
- }
304
- end
305
- end
306
-
307
- isolate_mutex.synchronize do
308
- external = ExternalFunction.new(name, wrapped, self)
309
- @functions["#{name}"] = external
310
- end
311
- end
312
-
313
- private
314
-
315
- def ensure_gc_thread
316
- @last_eval = Process.clock_gettime(Process::CLOCK_MONOTONIC)
317
- @ensure_gc_mutex.synchronize do
318
- @ensure_gc_thread = nil if !@ensure_gc_thread&.alive?
319
- return if !Thread.main.alive? # avoid "can't alloc thread" exception
320
- @ensure_gc_thread ||= Thread.new do
321
- ensure_gc_after_idle_seconds = @ensure_gc_after_idle / 1000.0
322
- done = false
323
- while !done
324
- now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
325
-
326
- if @disposed
327
- @ensure_gc_thread = nil
328
- break
329
- end
330
-
331
- if !@eval_thread && ensure_gc_after_idle_seconds < now - @last_eval
332
- @ensure_gc_mutex.synchronize do
333
- isolate_mutex.synchronize do
334
- if !@eval_thread
335
- isolate.low_memory_notification if !@disposed
336
- @ensure_gc_thread = nil
337
- done = true
338
- end
339
- end
340
- end
341
- end
342
- sleep ensure_gc_after_idle_seconds if !done
343
- end
344
- end
345
- end
346
- end
347
-
348
- def stop_attached
349
- @callback_mutex.synchronize{
350
- if @callback_running
351
- @eval_thread.raise ScriptTerminatedError, "Terminated during callback"
352
- @thread_raise_called = true
353
- end
354
- }
355
- end
356
-
357
- def timeout(&blk)
358
- return blk.call unless @timeout
359
-
360
- mutex = Mutex.new
361
- done = false
362
-
363
- rp,wp = IO.pipe
364
-
365
- t = Thread.new do
366
- begin
367
- result = rp.wait_readable(@timeout/1000.0)
368
- if !result
369
- mutex.synchronize do
370
- stop unless done
371
- end
372
- end
373
- rescue => e
374
- STDERR.puts e
375
- STDERR.puts "FAILED TO TERMINATE DUE TO TIMEOUT"
376
- end
377
- end
378
-
379
- rval = blk.call
380
- mutex.synchronize do
381
- done = true
382
- end
383
-
384
- wp.close
385
-
386
- # ensure we do not leak a thread in state
387
- t.join
388
- t = nil
389
-
390
- rval
391
- ensure
392
- # exceptions need to be handled
393
- wp&.close
394
- t&.join
395
- rp&.close
396
- end
397
-
398
- def check_init_options!(isolate:, snapshot:, max_memory:, marshal_stack_depth:, ensure_gc_after_idle:, timeout:)
399
- assert_option_is_nil_or_a('isolate', isolate, Isolate)
400
- assert_option_is_nil_or_a('snapshot', snapshot, Snapshot)
401
-
402
- assert_numeric_or_nil('max_memory', max_memory, min_value: 10_000, max_value: 2**32-1)
403
- assert_numeric_or_nil('marshal_stack_depth', marshal_stack_depth, min_value: 1, max_value: MARSHAL_STACKDEPTH_MAX_VALUE)
404
- assert_numeric_or_nil('ensure_gc_after_idle', ensure_gc_after_idle, min_value: 1)
405
- assert_numeric_or_nil('timeout', timeout, min_value: 1)
406
-
407
- if isolate && snapshot
408
- raise ArgumentError, 'can only pass one of isolate and snapshot options'
409
- end
410
- end
411
-
412
- def assert_numeric_or_nil(option_name, object, min_value:, max_value: nil)
413
- if max_value && object.is_a?(Numeric) && object > max_value
414
- raise ArgumentError, "#{option_name} must be less than or equal to #{max_value}"
415
- end
416
-
417
- if object.is_a?(Numeric) && object < min_value
418
- raise ArgumentError, "#{option_name} must be larger than or equal to #{min_value}"
419
- end
420
-
421
- if !object.nil? && !object.is_a?(Numeric)
422
- raise ArgumentError, "#{option_name} must be a number, passed a #{object.inspect}"
423
- end
424
- end
425
-
426
- def assert_option_is_nil_or_a(option_name, object, klass)
427
- unless object.nil? || object.is_a?(klass)
428
- raise ArgumentError, "#{option_name} must be a #{klass} object, passed a #{object.inspect}"
429
- end
430
- end
431
- end
432
-
433
- # `size` and `warmup!` public methods are defined in the C class
434
- class Snapshot
435
- def initialize(str = '')
436
- # ensure it first can load
437
- begin
438
- ctx = MiniRacer::Context.new
439
- ctx.eval(str)
440
- rescue MiniRacer::RuntimeError => e
441
- raise MiniRacer::SnapshotError.new, e.message, e.backtrace
442
- end
443
-
444
- @source = str
445
-
446
- # defined in the C class
447
- load(str)
448
- end
449
-
450
- def warmup!(src)
451
- # we have to do something here
452
- # we are bloating memory a bit but it is more correct
453
- # than hitting an exception when attempty to compile invalid source
454
- begin
455
- ctx = MiniRacer::Context.new
456
- ctx.eval(@source)
457
- ctx.eval(src)
458
- rescue MiniRacer::RuntimeError => e
459
- raise MiniRacer::SnapshotError.new, e.message, e.backtrace
460
- end
461
-
462
- warmup_unsafe!(src)
463
- end
464
91
  end
465
92
  end
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.17.0.pre5
4
+ version: 0.17.0.pre6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Saffron
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-09-29 00:00:00.000000000 Z
11
+ date: 2025-01-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -108,7 +108,10 @@ files:
108
108
  - LICENSE.txt
109
109
  - README.md
110
110
  - ext/mini_racer_extension/extconf.rb
111
- - ext/mini_racer_extension/mini_racer_extension.cc
111
+ - ext/mini_racer_extension/mini_racer_extension.c
112
+ - ext/mini_racer_extension/mini_racer_v8.cc
113
+ - ext/mini_racer_extension/mini_racer_v8.h
114
+ - ext/mini_racer_extension/serde.c
112
115
  - ext/mini_racer_loader/extconf.rb
113
116
  - ext/mini_racer_loader/mini_racer_loader.c
114
117
  - lib/mini_racer.rb
@@ -119,9 +122,9 @@ licenses:
119
122
  - MIT
120
123
  metadata:
121
124
  bug_tracker_uri: https://github.com/discourse/mini_racer/issues
122
- changelog_uri: https://github.com/discourse/mini_racer/blob/v0.17.0.pre5/CHANGELOG
123
- documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.17.0.pre5
124
- source_code_uri: https://github.com/discourse/mini_racer/tree/v0.17.0.pre5
125
+ changelog_uri: https://github.com/discourse/mini_racer/blob/v0.17.0.pre6/CHANGELOG
126
+ documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.17.0.pre6
127
+ source_code_uri: https://github.com/discourse/mini_racer/tree/v0.17.0.pre6
125
128
  post_install_message:
126
129
  rdoc_options: []
127
130
  require_paths:
@@ -138,7 +141,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
138
141
  - !ruby/object:Gem::Version
139
142
  version: '0'
140
143
  requirements: []
141
- rubygems_version: 3.5.11
144
+ rubygems_version: 3.5.22
142
145
  signing_key:
143
146
  specification_version: 4
144
147
  summary: Minimal embedded v8 for Ruby