mini_racer 0.17.0.pre5 → 0.17.0.pre6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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