execjs 2.6.0 → 2.9.1

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