execjs 2.6.0 → 2.9.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: fcd62eb19c4e6aea5d447da64320e784832ee043
4
- data.tar.gz: 687511bf72f60e9bc628beb0ff96b5ee0b69cee1
2
+ SHA256:
3
+ metadata.gz: 2cff0ab13dcd1bc38d12b1ca38d99773ba123ad75573c8b8f4789232677df308
4
+ data.tar.gz: a84d80b533fa677491bdb26d28bfedd44443deb5ff772d9eeb7db7e9e0d0ab36
5
5
  SHA512:
6
- metadata.gz: ccc7750f0aa82f25d94eec847a4316c3a2ede069e26dd9dc312fceae32661ca44ca71e6f84f7db51e3bd0036d4d6b6d30700c115f56f2a92483506664617a929
7
- data.tar.gz: 6a8890a15c433afc9f5de23b70852e0947b0bd694b8c195c6f2e573bcd57832b628f97a5e68ecf818a4972e438969e7fec1d0d826e3ae342d3805b0c9a7a750c
6
+ metadata.gz: fbac63f192c113786e7df0bce663a0e40a241b54534e01f18047d356d7ccb1514e4aeb8a610a336f85271def9b5956404091caccf6f0c984a20a723f67a364ce
7
+ data.tar.gz: 2f6dfe9ff9154500ea26a0c94a829eacb8b63ac84744791eff8f0b8643182f30118b1f1ed42f08486a4cc05c4e61c0cb8d800df53dcbccdbba5dadb786027b7a
@@ -1,5 +1,5 @@
1
- Copyright (c) 2015 Sam Stephenson
2
- Copyright (c) 2015 Josh Peek
1
+ Copyright (c) 2015-2016 Sam Stephenson
2
+ Copyright (c) 2015-2016 Josh Peek
3
3
 
4
4
  Permission is hereby granted, free of charge, to any person obtaining
5
5
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -7,14 +7,17 @@ returns the result to you as a Ruby object.
7
7
 
8
8
  ExecJS supports these runtimes:
9
9
 
10
- * [therubyracer](https://github.com/cowboyd/therubyracer) - Google V8
11
- embedded within Ruby
12
10
  * [therubyrhino](https://github.com/cowboyd/therubyrhino) - Mozilla
13
11
  Rhino embedded within JRuby
14
12
  * [Duktape.rb](https://github.com/judofyr/duktape.rb) - Duktape JavaScript interpreter
15
13
  * [Node.js](http://nodejs.org/)
14
+ * [Bun.sh](https://bun.sh) - JavaScript runtime & toolkit designed for speed
16
15
  * Apple JavaScriptCore - Included with Mac OS X
17
16
  * [Microsoft Windows Script Host](http://msdn.microsoft.com/en-us/library/9bbdkx3k.aspx) (JScript)
17
+ * [Google V8](http://code.google.com/p/v8/)
18
+ * [mini_racer](https://github.com/rubyjs/mini_racer) - Google V8
19
+ embedded within Ruby
20
+ * [GraalVM JavaScript](https://www.graalvm.org/javascript/) - used on TruffleRuby
18
21
 
19
22
  A short example:
20
23
 
@@ -36,13 +39,28 @@ context.call("CoffeeScript.compile", "square = (x) -> x * x", bare: true)
36
39
  # => "var square;\nsquare = function(x) {\n return x * x;\n};"
37
40
  ```
38
41
 
42
+ # Forcing a specific runtime
43
+
44
+ If you'd like to use a specific runtime rather than the autodected one, you can assign `ExecJS.runtime`:
45
+
46
+ ```ruby
47
+ ExecJS.runtime = ExecJS::Runtimes::Node
48
+ ```
49
+
50
+ Alternatively, you can define it via the `EXECJS_RUNTIME` environment variable:
51
+
52
+ ```bash
53
+ EXECJS_RUNTIME=Node ruby ...
54
+ ```
55
+
56
+ You can find the list of possible runtimes in [`lib/execjs/runtimes.rb`](https://github.com/rails/execjs/blob/master/lib/execjs/runtimes.rb).
57
+
39
58
  # Installation
40
59
 
41
60
  ```
42
61
  $ gem install execjs
43
62
  ```
44
63
 
45
-
46
64
  # FAQ
47
65
 
48
66
  **Why can't I use CommonJS `require()` inside ExecJS?**
@@ -72,9 +90,12 @@ are automatically detected, each runtime has different sandboxing properties.
72
90
  You shouldn't use `ExecJS.eval` on any inputs you wouldn't feel comfortable Ruby
73
91
  `eval()`ing.
74
92
 
93
+ ## Contributing to ExecJS
75
94
 
76
- # License
95
+ ExecJS is work of dozens of contributors. You're encouraged to submit pull requests, propose
96
+ features and discuss issues.
77
97
 
78
- Copyright (c) 2015 Sam Stephenson and Josh Peek.
98
+ See [CONTRIBUTING](CONTRIBUTING.md).
79
99
 
80
- Released under the MIT license. See `LICENSE` for details.
100
+ ## License
101
+ ExecJS is released under the [MIT License](MIT-LICENSE).
@@ -6,15 +6,15 @@ module ExecJS
6
6
  "Disabled"
7
7
  end
8
8
 
9
- def exec(source)
9
+ def exec(source, options = {})
10
10
  raise Error, "ExecJS disabled"
11
11
  end
12
12
 
13
- def eval(source)
13
+ def eval(source, options = {})
14
14
  raise Error, "ExecJS disabled"
15
15
  end
16
16
 
17
- def compile(source)
17
+ def compile(source, options = {})
18
18
  raise Error, "ExecJS disabled"
19
19
  end
20
20
 
@@ -4,29 +4,30 @@ require "json"
4
4
  module ExecJS
5
5
  class DuktapeRuntime < Runtime
6
6
  class Context < Runtime::Context
7
- def initialize(runtime, source = "")
7
+ def initialize(runtime, source = "", options = {})
8
8
  @ctx = Duktape::Context.new(complex_object: nil)
9
- @ctx.exec_string(encode(source), '(execjs)')
9
+ @ctx.exec_string(source.encode(Encoding::UTF_8), '(execjs)')
10
10
  rescue Exception => e
11
11
  raise wrap_error(e)
12
12
  end
13
13
 
14
14
  def exec(source, options = {})
15
15
  return unless /\S/ =~ source
16
- @ctx.eval_string("(function(){#{encode(source)}})()", '(execjs)')
16
+ @ctx.eval_string("(function(){#{source.encode(Encoding::UTF_8)}})()", '(execjs)')
17
17
  rescue Exception => e
18
18
  raise wrap_error(e)
19
19
  end
20
20
 
21
21
  def eval(source, options = {})
22
22
  return unless /\S/ =~ source
23
- @ctx.eval_string("(#{encode(source)})", '(execjs)')
23
+ @ctx.eval_string("(#{source.encode(Encoding::UTF_8)})", '(execjs)')
24
24
  rescue Exception => e
25
25
  raise wrap_error(e)
26
26
  end
27
27
 
28
28
  def call(identifier, *args)
29
- @ctx.call_prop(identifier.split("."), *args)
29
+ @ctx.exec_string("__execjs_duktape_call = #{identifier}", '(execjs)')
30
+ @ctx.call_prop("__execjs_duktape_call", *args)
30
31
  rescue Exception => e
31
32
  raise wrap_error(e)
32
33
  end
@@ -1,11 +1,12 @@
1
- require "tmpdir"
2
1
  require "execjs/runtime"
2
+ require "tmpdir"
3
+ require "json"
3
4
 
4
5
  module ExecJS
5
6
  class ExternalRuntime < Runtime
6
7
  class Context < Runtime::Context
7
- def initialize(runtime, source = "")
8
- source = encode(source)
8
+ def initialize(runtime, source = "", options = {})
9
+ source = source.encode(Encoding::UTF_8)
9
10
 
10
11
  @runtime = runtime
11
12
  @source = source
@@ -15,7 +16,7 @@ module ExecJS
15
16
  end
16
17
 
17
18
  def eval(source, options = {})
18
- source = encode(source)
19
+ source = source.encode(Encoding::UTF_8)
19
20
 
20
21
  if /\S/ =~ source
21
22
  exec("return eval(#{::JSON.generate("(#{source})", quirks_mode: true)})")
@@ -23,7 +24,7 @@ module ExecJS
23
24
  end
24
25
 
25
26
  def exec(source, options = {})
26
- source = encode(source)
27
+ source = source.encode(Encoding::UTF_8)
27
28
  source = "#{@source}\n#{source}" if @source != ""
28
29
  source = @runtime.compile_source(source)
29
30
 
@@ -77,7 +78,7 @@ module ExecJS
77
78
  .sub(filename, "(execjs)")
78
79
  .strip
79
80
  end
80
- stack.reject! { |line| ["eval code", "eval@[native code]"].include?(line) }
81
+ stack.reject! { |line| ["eval code", "eval code@", "eval@[native code]"].include?(line) }
81
82
  stack.shift unless stack[0].to_s.include?("(execjs)")
82
83
  error_class = value =~ /SyntaxError:/ ? RuntimeError : ProgramError
83
84
  error = error_class.new(value)
@@ -102,7 +103,13 @@ module ExecJS
102
103
  @popen_options[:internal_encoding] = ::Encoding.default_internal || 'UTF-8'
103
104
 
104
105
  if @runner_path
105
- instance_eval generate_compile_method(@runner_path)
106
+ instance_eval <<~RUBY, __FILE__, __LINE__
107
+ def compile_source(source)
108
+ <<-RUNNER
109
+ #{IO.read(@runner_path)}
110
+ RUNNER
111
+ end
112
+ RUBY
106
113
  end
107
114
  end
108
115
 
@@ -142,15 +149,6 @@ module ExecJS
142
149
  end
143
150
 
144
151
  protected
145
- def generate_compile_method(path)
146
- <<-RUBY
147
- def compile_source(source)
148
- <<-RUNNER
149
- #{IO.read(path)}
150
- RUNNER
151
- end
152
- RUBY
153
- end
154
152
 
155
153
  def json2_source
156
154
  @json2_source ||= IO.read(ExecJS.root + "/support/json2.js")
@@ -173,7 +171,7 @@ module ExecJS
173
171
  begin
174
172
  command = binary.split(" ") << filename
175
173
  `#{shell_escape(*command)} 2>&1 > #{path}`
176
- output = File.open(path, 'rb', @popen_options) { |f| f.read }
174
+ output = File.open(path, 'rb', **@popen_options) { |f| f.read }
177
175
  ensure
178
176
  File.unlink(path) if path
179
177
  end
@@ -196,8 +194,8 @@ module ExecJS
196
194
  require 'shellwords'
197
195
 
198
196
  def exec_runtime(filename)
199
- command = "#{Shellwords.join(binary.split(' ') << filename)} 2>&1"
200
- io = IO.popen(command, @popen_options)
197
+ command = "#{Shellwords.join(binary.split(' ') << filename)}"
198
+ io = IO.popen(command, **@popen_options)
201
199
  output = io.read
202
200
  io.close
203
201
 
@@ -209,7 +207,7 @@ module ExecJS
209
207
  end
210
208
  else
211
209
  def exec_runtime(filename)
212
- io = IO.popen(binary.split(' ') << filename, @popen_options.merge({err: [:child, :out]}))
210
+ io = IO.popen(binary.split(' ') << filename, **@popen_options)
213
211
  output = io.read
214
212
  io.close
215
213
 
@@ -0,0 +1,147 @@
1
+ require "execjs/runtime"
2
+
3
+ module ExecJS
4
+ class GraalJSRuntime < Runtime
5
+ class Context < Runtime::Context
6
+ def initialize(runtime, source = "", options = {})
7
+ @context = Polyglot::InnerContext.new
8
+ @context.eval('js', 'delete this.console')
9
+ @js_object = @context.eval('js', 'Object')
10
+
11
+ source = source.encode(Encoding::UTF_8)
12
+ unless source.empty?
13
+ translate do
14
+ eval_in_context(source)
15
+ end
16
+ end
17
+ end
18
+
19
+ def exec(source, options = {})
20
+ source = source.encode(Encoding::UTF_8)
21
+ source = "(function(){#{source}})()" if /\S/.match?(source)
22
+
23
+ translate do
24
+ eval_in_context(source)
25
+ end
26
+ end
27
+
28
+ def eval(source, options = {})
29
+ source = source.encode(Encoding::UTF_8)
30
+ source = "(#{source})" if /\S/.match?(source)
31
+
32
+ translate do
33
+ eval_in_context(source)
34
+ end
35
+ end
36
+
37
+ def call(source, *args)
38
+ source = source.encode(Encoding::UTF_8)
39
+ source = "(#{source})" if /\S/.match?(source)
40
+
41
+ translate do
42
+ function = eval_in_context(source)
43
+ function.call(*convert_ruby_to_js(args))
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ ForeignException = defined?(Polyglot::ForeignException) ? Polyglot::ForeignException : ::RuntimeError
50
+
51
+ def translate
52
+ convert_js_to_ruby yield
53
+ rescue ForeignException => e
54
+ if e.message && e.message.start_with?('SyntaxError:')
55
+ error_class = ExecJS::RuntimeError
56
+ else
57
+ error_class = ExecJS::ProgramError
58
+ end
59
+
60
+ backtrace = (e.backtrace || []).map { |line| line.sub('(eval)', '(execjs)') }
61
+ raise error_class, e.message, backtrace
62
+ end
63
+
64
+ def convert_js_to_ruby(value)
65
+ case value
66
+ when true, false, Integer, Float
67
+ value
68
+ else
69
+ if value.nil?
70
+ nil
71
+ elsif value.respond_to?(:call)
72
+ nil
73
+ elsif value.respond_to?(:to_str)
74
+ value.to_str
75
+ elsif value.respond_to?(:to_ary)
76
+ value.to_ary.map do |e|
77
+ if e.respond_to?(:call)
78
+ nil
79
+ else
80
+ convert_js_to_ruby(e)
81
+ end
82
+ end
83
+ else
84
+ object = value
85
+ h = {}
86
+ object.instance_variables.each do |member|
87
+ v = object[member]
88
+ unless v.respond_to?(:call)
89
+ h[member.to_s] = convert_js_to_ruby(v)
90
+ end
91
+ end
92
+ h
93
+ end
94
+ end
95
+ end
96
+
97
+ def convert_ruby_to_js(value)
98
+ case value
99
+ when nil, true, false, Integer, Float
100
+ value
101
+ when String, Symbol
102
+ Truffle::Interop.as_truffle_string value
103
+ when Array
104
+ value.map { |e| convert_ruby_to_js(e) }
105
+ when Hash
106
+ h = @js_object.new
107
+ value.each_pair do |k,v|
108
+ h[convert_ruby_to_js(k)] = convert_ruby_to_js(v)
109
+ end
110
+ h
111
+ else
112
+ raise TypeError, "Unknown how to convert to JS: #{value.inspect}"
113
+ end
114
+ end
115
+
116
+ class_eval <<-'RUBY', "(execjs)", 1
117
+ def eval_in_context(code); @context.eval('js', code); end
118
+ RUBY
119
+ end
120
+
121
+ def name
122
+ "GraalVM (Graal.js)"
123
+ end
124
+
125
+ def available?
126
+ return @available if defined?(@available)
127
+
128
+ unless RUBY_ENGINE == "truffleruby"
129
+ return @available = false
130
+ end
131
+
132
+ unless defined?(Polyglot::InnerContext)
133
+ warn "TruffleRuby #{RUBY_ENGINE_VERSION} does not have support for inner contexts, use a more recent version", uplevel: 0 if $VERBOSE
134
+ return @available = false
135
+ end
136
+
137
+ unless Polyglot.languages.include? "js"
138
+ warn "The language 'js' is not available, you likely need to `export TRUFFLERUBYOPT='--jvm --polyglot'`", uplevel: 0 if $VERBOSE
139
+ warn "You also need to install the 'js' component with 'gu install js' on GraalVM 22.2+", uplevel: 0 if $VERBOSE
140
+ warn "Note that you need TruffleRuby+GraalVM and not just the TruffleRuby standalone to use #{self.class}", uplevel: 0 if $VERBOSE
141
+ return @available = false
142
+ end
143
+
144
+ @available = true
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,103 @@
1
+ require "execjs/runtime"
2
+
3
+ module ExecJS
4
+ class MiniRacerRuntime < Runtime
5
+ class Context < Runtime::Context
6
+ def initialize(runtime, source = "", options={})
7
+ source = source.encode(Encoding::UTF_8)
8
+ @context = ::MiniRacer::Context.new
9
+ @context.eval("delete this.console");
10
+ translate do
11
+ @context.eval(source)
12
+ end
13
+ end
14
+
15
+ def exec(source, options = {})
16
+ source = source.encode(Encoding::UTF_8)
17
+
18
+ if /\S/ =~ source
19
+ eval "(function(){#{source}})()"
20
+ end
21
+ end
22
+
23
+ def eval(source, options = {})
24
+ source = source.encode(Encoding::UTF_8)
25
+
26
+ if /\S/ =~ source
27
+ translate do
28
+ @context.eval("(#{source})")
29
+ end
30
+ end
31
+ end
32
+
33
+ def call(identifier, *args)
34
+ # TODO optimise generate
35
+ eval "#{identifier}.apply(this, #{::JSON.generate(args)})"
36
+ end
37
+
38
+ private
39
+
40
+ def strip_functions!(value)
41
+ if Array === value
42
+ value.map! do |v|
43
+ if MiniRacer::JavaScriptFunction === value
44
+ nil
45
+ else
46
+ strip_functions!(v)
47
+ end
48
+ end
49
+ elsif Hash === value
50
+ value.each do |k,v|
51
+ if MiniRacer::JavaScriptFunction === v
52
+ value.delete k
53
+ else
54
+ value[k] = strip_functions!(v)
55
+ end
56
+ end
57
+ value
58
+ elsif MiniRacer::JavaScriptFunction === value
59
+ nil
60
+ else
61
+ value
62
+ end
63
+ end
64
+
65
+ def translate
66
+ begin
67
+ strip_functions! yield
68
+ rescue MiniRacer::RuntimeError => e
69
+ ex = ProgramError.new e.message
70
+ if backtrace = e.backtrace
71
+ backtrace = backtrace.map { |line|
72
+ if line =~ /JavaScript at/
73
+ line.sub("JavaScript at ", "")
74
+ .sub("<anonymous>", "(execjs)")
75
+ .strip
76
+ else
77
+ line
78
+ end
79
+ }
80
+ ex.set_backtrace backtrace
81
+ end
82
+ raise ex
83
+ rescue MiniRacer::ParseError => e
84
+ ex = RuntimeError.new e.message
85
+ ex.set_backtrace(["(execjs):1"] + e.backtrace)
86
+ raise ex
87
+ end
88
+ end
89
+
90
+ end
91
+
92
+ def name
93
+ "mini_racer (V8)"
94
+ end
95
+
96
+ def available?
97
+ require "mini_racer"
98
+ true
99
+ rescue LoadError
100
+ false
101
+ end
102
+ end
103
+ end
data/lib/execjs/module.rb CHANGED
@@ -15,16 +15,16 @@ module ExecJS
15
15
  @runtime = runtime
16
16
  end
17
17
 
18
- def exec(source)
19
- runtime.exec(source)
18
+ def exec(source, options = {})
19
+ runtime.exec(source, options)
20
20
  end
21
21
 
22
- def eval(source)
23
- runtime.eval(source)
22
+ def eval(source, options = {})
23
+ runtime.eval(source, options)
24
24
  end
25
25
 
26
- def compile(source)
27
- runtime.compile(source)
26
+ def compile(source, options = {})
27
+ runtime.compile(source, options)
28
28
  end
29
29
 
30
30
  def root
@@ -1,10 +1,11 @@
1
1
  require "execjs/runtime"
2
+ require "json"
2
3
 
3
4
  module ExecJS
4
5
  class RubyRhinoRuntime < Runtime
5
6
  class Context < Runtime::Context
6
- def initialize(runtime, source = "")
7
- source = encode(source)
7
+ def initialize(runtime, source = "", options = {})
8
+ source = source.encode(Encoding::UTF_8)
8
9
 
9
10
  @rhino_context = ::Rhino::Context.new
10
11
  fix_memory_limit! @rhino_context
@@ -14,7 +15,7 @@ module ExecJS
14
15
  end
15
16
 
16
17
  def exec(source, options = {})
17
- source = encode(source)
18
+ source = source.encode(Encoding::UTF_8)
18
19
 
19
20
  if /\S/ =~ source
20
21
  eval "(function(){#{source}})()", options
@@ -22,7 +23,7 @@ module ExecJS
22
23
  end
23
24
 
24
25
  def eval(source, options = {})
25
- source = encode(source)
26
+ source = source.encode(Encoding::UTF_8)
26
27
 
27
28
  if /\S/ =~ source
28
29
  unbox @rhino_context.eval("(#{source})")
@@ -32,7 +33,11 @@ module ExecJS
32
33
  end
33
34
 
34
35
  def call(properties, *args)
35
- unbox @rhino_context.eval(properties).call(*args)
36
+ # Might no longer be necessary if therubyrhino handles Symbols directly:
37
+ # https://github.com/rubyjs/therubyrhino/issues/43
38
+ converted_args = JSON.parse(JSON.generate(args), create_additions: false)
39
+
40
+ unbox @rhino_context.eval(properties).call(*converted_args)
36
41
  rescue Exception => e
37
42
  raise wrap_error(e)
38
43
  end
@@ -1,23 +1,34 @@
1
- require "execjs/encoding"
2
-
3
1
  module ExecJS
4
2
  # Abstract base class for runtimes
5
3
  class Runtime
6
4
  class Context
7
- include Encoding
8
-
9
- def initialize(runtime, source = "")
5
+ def initialize(runtime, source = "", options = {})
10
6
  end
11
7
 
8
+ # Evaluates the +source+ in the context of a function body and returns the
9
+ # returned value.
10
+ #
11
+ # context.exec("return 1") # => 1
12
+ # context.exec("1") # => nil (nothing was returned)
12
13
  def exec(source, options = {})
13
14
  raise NotImplementedError
14
15
  end
15
16
 
17
+ # Evaluates the +source+ as an expression and returns the result.
18
+ #
19
+ # context.eval("1") # => 1
20
+ # context.eval("return 1") # => Raises SyntaxError
16
21
  def eval(source, options = {})
17
22
  raise NotImplementedError
18
23
  end
19
24
 
20
- def call(properties, *args)
25
+ # Evaluates +source+ as an expression (which should be of type
26
+ # +function+), and calls the function with the given arguments.
27
+ # The function will be evaluated with the global object as +this+.
28
+ #
29
+ # context.call("function(a, b) { return a + b }", 1, 1) # => 2
30
+ # context.call("CoffeeScript.compile", "1 + 1")
31
+ def call(source, *args)
21
32
  raise NotImplementedError
22
33
  end
23
34
  end
@@ -30,18 +41,32 @@ module ExecJS
30
41
  self.class::Context
31
42
  end
32
43
 
33
- def exec(source)
34
- context = context_class.new(self)
35
- context.exec(source)
44
+ def exec(source, options = {})
45
+ context = compile("", options)
46
+
47
+ if context.method(:exec).arity == 1
48
+ context.exec(source)
49
+ else
50
+ context.exec(source, options)
51
+ end
36
52
  end
37
53
 
38
- def eval(source)
39
- context = context_class.new(self)
40
- context.eval(source)
54
+ def eval(source, options = {})
55
+ context = compile("", options)
56
+
57
+ if context.method(:eval).arity == 1
58
+ context.eval(source)
59
+ else
60
+ context.eval(source, options)
61
+ end
41
62
  end
42
63
 
43
- def compile(source)
44
- context_class.new(self, source)
64
+ def compile(source, options = {})
65
+ if context_class.instance_method(:initialize).arity == 2
66
+ context_class.new(self, source)
67
+ else
68
+ context_class.new(self, source, options)
69
+ end
45
70
  end
46
71
 
47
72
  def deprecated?
@@ -2,8 +2,9 @@ require "execjs/module"
2
2
  require "execjs/disabled_runtime"
3
3
  require "execjs/duktape_runtime"
4
4
  require "execjs/external_runtime"
5
- require "execjs/ruby_racer_runtime"
6
5
  require "execjs/ruby_rhino_runtime"
6
+ require "execjs/mini_racer_runtime"
7
+ require "execjs/graaljs_runtime"
7
8
 
8
9
  module ExecJS
9
10
  module Runtimes
@@ -11,20 +12,32 @@ module ExecJS
11
12
 
12
13
  Duktape = DuktapeRuntime.new
13
14
 
14
- RubyRacer = RubyRacerRuntime.new
15
-
16
15
  RubyRhino = RubyRhinoRuntime.new
17
16
 
17
+ GraalJS = GraalJSRuntime.new
18
+
19
+ MiniRacer = MiniRacerRuntime.new
20
+
18
21
  Node = ExternalRuntime.new(
19
22
  name: "Node.js (V8)",
20
- command: ["nodejs", "node"],
23
+ command: ["node", "nodejs"],
21
24
  runner_path: ExecJS.root + "/support/node_runner.js",
22
25
  encoding: 'UTF-8'
23
26
  )
24
27
 
28
+ Bun = ExternalRuntime.new(
29
+ name: "Bun.sh",
30
+ command: ["bun"],
31
+ runner_path: ExecJS.root + "/support/bun_runner.js",
32
+ encoding: 'UTF-8'
33
+ )
34
+
25
35
  JavaScriptCore = ExternalRuntime.new(
26
36
  name: "JavaScriptCore",
27
- command: "/System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Resources/jsc",
37
+ command: [
38
+ "/System/Library/Frameworks/JavaScriptCore.framework/Versions/Current/Helpers/jsc",
39
+ "/System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Resources/jsc",
40
+ ],
28
41
  runner_path: ExecJS.root + "/support/jsc_runner.js"
29
42
  )
30
43
 
@@ -42,6 +55,13 @@ module ExecJS
42
55
  encoding: 'UTF-16LE' # CScript with //U returns UTF-16LE
43
56
  )
44
57
 
58
+ V8 = ExternalRuntime.new(
59
+ name: "V8",
60
+ command: "d8",
61
+ runner_path: ExecJS.root + "/support/v8_runner.js",
62
+ encoding: 'UTF-8'
63
+ )
64
+
45
65
 
46
66
  def self.autodetect
47
67
  from_environment || best_available ||
@@ -54,7 +74,9 @@ module ExecJS
54
74
  end
55
75
 
56
76
  def self.from_environment
57
- if name = ENV["EXECJS_RUNTIME"]
77
+ env = ENV["EXECJS_RUNTIME"]
78
+ if env && !env.empty?
79
+ name = env
58
80
  raise RuntimeUnavailable, "#{name} runtime is not defined" unless const_defined?(name)
59
81
  runtime = const_get(name)
60
82
 
@@ -69,13 +91,16 @@ module ExecJS
69
91
 
70
92
  def self.runtimes
71
93
  @runtimes ||= [
72
- RubyRacer,
73
94
  RubyRhino,
95
+ GraalJS,
74
96
  Duktape,
97
+ MiniRacer,
98
+ Bun,
75
99
  Node,
76
100
  JavaScriptCore,
77
101
  SpiderMonkey,
78
- JScript
102
+ JScript,
103
+ V8
79
104
  ]
80
105
  end
81
106
  end
@@ -0,0 +1,29 @@
1
+ (function(program, execJS) { (function() {execJS(program) }).call({}); })(function(self, global, process, module, exports, require, console, setTimeout, setInterval, clearTimeout, clearInterval, setImmediate, clearImmediate) { #{source}
2
+ }, function(program) {
3
+ // Force BunJS to use sloppy mode see https://github.com/oven-sh/bun/issues/4527#issuecomment-1709520894
4
+ exports.abc = function(){}
5
+ var __process__ = process;
6
+ var printFinal = function(string) {
7
+ process.stdout.write('' + string, function() {
8
+ __process__.exit(0);
9
+ });
10
+ };
11
+ try {
12
+ delete this.process;
13
+ delete this.console;
14
+ result = program();
15
+ process = __process__;
16
+ if (typeof result == 'undefined' && result !== null) {
17
+ printFinal('["ok"]');
18
+ } else {
19
+ try {
20
+ printFinal(JSON.stringify(['ok', result]));
21
+ } catch (err) {
22
+ printFinal(JSON.stringify(['err', '' + err, err.stack]));
23
+ }
24
+ }
25
+ } catch (err) {
26
+ process = __process__;
27
+ printFinal(JSON.stringify(['err', '' + err, err.stack]));
28
+ }
29
+ });
@@ -2,6 +2,14 @@
2
2
  }, function(program) {
3
3
  var output;
4
4
  try {
5
+ delete this.console;
6
+ delete this.setTimeout;
7
+ delete this.setInterval;
8
+ delete this.clearTimeout;
9
+ delete this.clearInterval;
10
+ delete this.setImmediate;
11
+ delete this.clearImmediate;
12
+
5
13
  result = program();
6
14
  if (typeof result == 'undefined' && result !== null) {
7
15
  print('["ok"]');
@@ -1,20 +1,34 @@
1
- (function(program, execJS) { execJS(program) })(function(global, module, exports, require, console, setTimeout, setInterval, clearTimeout, clearInterval, setImmediate, clearImmediate) { #{source}
1
+ (function(program, execJS) { execJS(program) })(function(global, process, module, exports, require, console, setTimeout, setInterval, clearTimeout, clearInterval, setImmediate, clearImmediate) { #{source}
2
2
  }, function(program) {
3
- var output, print = function(string) {
4
- process.stdout.write('' + string);
3
+ var __process__ = process;
4
+
5
+ var printFinal = function(string) {
6
+ process.stdout.write('' + string, function() {
7
+ __process__.exit(0);
8
+ });
5
9
  };
6
10
  try {
11
+ delete this.process;
12
+ delete this.console;
13
+ delete this.setTimeout;
14
+ delete this.setInterval;
15
+ delete this.clearTimeout;
16
+ delete this.clearInterval;
17
+ delete this.setImmediate;
18
+ delete this.clearImmediate;
7
19
  result = program();
20
+ this.process = __process__;
8
21
  if (typeof result == 'undefined' && result !== null) {
9
- print('["ok"]');
22
+ printFinal('["ok"]');
10
23
  } else {
11
24
  try {
12
- print(JSON.stringify(['ok', result]));
25
+ printFinal(JSON.stringify(['ok', result]));
13
26
  } catch (err) {
14
- print(JSON.stringify(['err', '' + err, err.stack]));
27
+ printFinal(JSON.stringify(['err', '' + err, err.stack]));
15
28
  }
16
29
  }
17
30
  } catch (err) {
18
- print(JSON.stringify(['err', '' + err, err.stack]));
31
+ this.process = __process__;
32
+ printFinal(JSON.stringify(['err', '' + err, err.stack]));
19
33
  }
20
34
  });
@@ -0,0 +1,25 @@
1
+ (function(program, execJS) { execJS(program) })(function(console, setTimeout, setInterval, clearTimeout, clearInterval, setImmediate, clearImmediate) { #{source}
2
+ }, function(program) {
3
+ var output;
4
+ try {
5
+ delete this.console;
6
+ delete this.setTimeout;
7
+ delete this.setInterval;
8
+ delete this.clearTimeout;
9
+ delete this.clearInterval;
10
+ delete this.setImmediate;
11
+ delete this.clearImmediate;
12
+ result = program();
13
+ if (typeof result == 'undefined' && result !== null) {
14
+ print('["ok"]');
15
+ } else {
16
+ try {
17
+ print(JSON.stringify(['ok', result]));
18
+ } catch (err) {
19
+ print(JSON.stringify(['err', '' + err, err.stack]));
20
+ }
21
+ }
22
+ } catch (err) {
23
+ print(JSON.stringify(['err', '' + err, err.stack]));
24
+ }
25
+ });
@@ -1,3 +1,3 @@
1
1
  module ExecJS
2
- VERSION = "2.6.0"
2
+ VERSION = "2.9.1"
3
3
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: execjs
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.6.0
4
+ version: 2.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Stephenson
8
8
  - Josh Peek
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-08-14 00:00:00.000000000 Z
12
+ date: 2023-09-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -33,29 +33,31 @@ executables: []
33
33
  extensions: []
34
34
  extra_rdoc_files: []
35
35
  files:
36
- - LICENSE
36
+ - MIT-LICENSE
37
37
  - README.md
38
38
  - lib/execjs.rb
39
39
  - lib/execjs/disabled_runtime.rb
40
40
  - lib/execjs/duktape_runtime.rb
41
- - lib/execjs/encoding.rb
42
41
  - lib/execjs/external_runtime.rb
42
+ - lib/execjs/graaljs_runtime.rb
43
+ - lib/execjs/mini_racer_runtime.rb
43
44
  - lib/execjs/module.rb
44
- - lib/execjs/ruby_racer_runtime.rb
45
45
  - lib/execjs/ruby_rhino_runtime.rb
46
46
  - lib/execjs/runtime.rb
47
47
  - lib/execjs/runtimes.rb
48
+ - lib/execjs/support/bun_runner.js
48
49
  - lib/execjs/support/jsc_runner.js
49
50
  - lib/execjs/support/jscript_runner.js
50
51
  - lib/execjs/support/json2.js
51
52
  - lib/execjs/support/node_runner.js
52
53
  - lib/execjs/support/spidermonkey_runner.js
54
+ - lib/execjs/support/v8_runner.js
53
55
  - lib/execjs/version.rb
54
56
  homepage: https://github.com/rails/execjs
55
57
  licenses:
56
58
  - MIT
57
59
  metadata: {}
58
- post_install_message:
60
+ post_install_message:
59
61
  rdoc_options: []
60
62
  require_paths:
61
63
  - lib
@@ -63,17 +65,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
63
65
  requirements:
64
66
  - - ">="
65
67
  - !ruby/object:Gem::Version
66
- version: 1.9.3
68
+ version: 2.5.0
67
69
  required_rubygems_version: !ruby/object:Gem::Requirement
68
70
  requirements:
69
71
  - - ">="
70
72
  - !ruby/object:Gem::Version
71
73
  version: '0'
72
74
  requirements: []
73
- rubyforge_project:
74
- rubygems_version: 2.4.7
75
- signing_key:
75
+ rubygems_version: 3.3.7
76
+ signing_key:
76
77
  specification_version: 4
77
78
  summary: Run JavaScript code from Ruby
78
79
  test_files: []
79
- has_rdoc:
@@ -1,26 +0,0 @@
1
- module ExecJS
2
- # Encodes strings as UTF-8
3
- module Encoding
4
- if RUBY_ENGINE == 'jruby' || RUBY_ENGINE == 'rbx'
5
- # workaround for jruby bug http://jira.codehaus.org/browse/JRUBY-6588
6
- # workaround for rbx bug https://github.com/rubinius/rubinius/issues/1729
7
- def encode(string)
8
- if string.encoding.name == 'ASCII-8BIT'
9
- data = string.dup
10
- data.force_encoding('UTF-8')
11
-
12
- unless data.valid_encoding?
13
- raise ::Encoding::UndefinedConversionError, "Could not encode ASCII-8BIT data #{string.dump} as UTF-8"
14
- end
15
- else
16
- data = string.encode('UTF-8')
17
- end
18
- data
19
- end
20
- else
21
- def encode(string)
22
- string.encode('UTF-8')
23
- end
24
- end
25
- end
26
- end
@@ -1,114 +0,0 @@
1
- require "execjs/runtime"
2
-
3
- module ExecJS
4
- class RubyRacerRuntime < Runtime
5
- class Context < Runtime::Context
6
- def initialize(runtime, source = "")
7
- source = encode(source)
8
-
9
- lock do
10
- @v8_context = ::V8::Context.new
11
-
12
- begin
13
- @v8_context.eval(source)
14
- rescue ::V8::JSError => e
15
- raise wrap_error(e)
16
- end
17
- end
18
- end
19
-
20
- def exec(source, options = {})
21
- source = encode(source)
22
-
23
- if /\S/ =~ source
24
- eval "(function(){#{source}})()", options
25
- end
26
- end
27
-
28
- def eval(source, options = {})
29
- source = encode(source)
30
-
31
- if /\S/ =~ source
32
- lock do
33
- begin
34
- unbox @v8_context.eval("(#{source})")
35
- rescue ::V8::JSError => e
36
- raise wrap_error(e)
37
- end
38
- end
39
- end
40
- end
41
-
42
- def call(properties, *args)
43
- lock do
44
- begin
45
- unbox @v8_context.eval(properties).call(*args)
46
- rescue ::V8::JSError => e
47
- raise wrap_error(e)
48
- end
49
- end
50
- end
51
-
52
- def unbox(value)
53
- case value
54
- when ::V8::Function
55
- nil
56
- when ::V8::Array
57
- value.map { |v| unbox(v) }
58
- when ::V8::Object
59
- value.inject({}) do |vs, (k, v)|
60
- vs[k] = unbox(v) unless v.is_a?(::V8::Function)
61
- vs
62
- end
63
- when String
64
- value.force_encoding('UTF-8')
65
- else
66
- value
67
- end
68
- end
69
-
70
- private
71
- def lock
72
- result, exception = nil, nil
73
- V8::C::Locker() do
74
- begin
75
- result = yield
76
- rescue Exception => e
77
- exception = e
78
- end
79
- end
80
-
81
- if exception
82
- raise exception
83
- else
84
- result
85
- end
86
- end
87
-
88
- def wrap_error(e)
89
- error_class = e.value["name"] == "SyntaxError" ? RuntimeError : ProgramError
90
-
91
- stack = e.value["stack"] || ""
92
- stack = stack.split("\n")
93
- stack.shift
94
- stack = [e.message[/<eval>:\d+:\d+/, 0]].compact if stack.empty?
95
- stack = stack.map { |line| line.sub(" at ", "").sub("<eval>", "(execjs)").strip }
96
-
97
- error = error_class.new(e.value.to_s)
98
- error.set_backtrace(stack + caller)
99
- error
100
- end
101
- end
102
-
103
- def name
104
- "therubyracer (V8)"
105
- end
106
-
107
- def available?
108
- require "v8"
109
- true
110
- rescue LoadError
111
- false
112
- end
113
- end
114
- end