mini_racer 0.5.0 → 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- $CPPFLAGS += " -fvisibility=hidden "
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
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MiniRacer
4
- VERSION = "0.5.0"
4
+ VERSION = "0.6.3"
5
5
  LIBV8_NODE_VERSION = "~> 16.10.0.0"
6
6
  end
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
- ext_filename = "mini_racer_extension.#{RbConfig::CONFIG['DLEXT']}"
6
- ext_path = Gem.loaded_specs['mini_racer'].require_paths
7
- .map { |p| (p = Pathname.new(p)).absolute? ? p : Pathname.new(__dir__).parent + p }
8
- ext_found = ext_path.map { |p| p + ext_filename }.find { |p| p.file? }
9
-
10
- raise LoadError, "Could not find #{ext_filename} in #{ext_path.map(&:to_s)}" unless ext_found
11
- MiniRacer::Loader.load(ext_found.to_s)
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("file_or_io")
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 = IO.select([rp],[],[],(@timeout/1000.0))
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.write("done")
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
- if t && wp
379
- wp.write("done")
380
- t.join
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.5.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: 2021-11-11 00:00:00.000000000 Z
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.5.0/CHANGELOG
132
- documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.5.0
133
- source_code_uri: https://github.com/discourse/mini_racer/tree/v0.5.0
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.1.6
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