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 +5 -5
- data/{LICENSE → MIT-LICENSE} +2 -2
- data/README.md +27 -6
- data/lib/execjs/disabled_runtime.rb +3 -3
- data/lib/execjs/duktape_runtime.rb +6 -5
- data/lib/execjs/external_runtime.rb +18 -20
- data/lib/execjs/graaljs_runtime.rb +147 -0
- data/lib/execjs/mini_racer_runtime.rb +103 -0
- data/lib/execjs/module.rb +6 -6
- data/lib/execjs/ruby_rhino_runtime.rb +10 -5
- data/lib/execjs/runtime.rb +39 -14
- data/lib/execjs/runtimes.rb +33 -8
- data/lib/execjs/support/bun_runner.js +29 -0
- data/lib/execjs/support/jsc_runner.js +8 -0
- data/lib/execjs/support/node_runner.js +21 -7
- data/lib/execjs/support/v8_runner.js +25 -0
- data/lib/execjs/version.rb +1 -1
- metadata +12 -12
- data/lib/execjs/encoding.rb +0 -26
- data/lib/execjs/ruby_racer_runtime.rb +0 -114
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2cff0ab13dcd1bc38d12b1ca38d99773ba123ad75573c8b8f4789232677df308
|
4
|
+
data.tar.gz: a84d80b533fa677491bdb26d28bfedd44443deb5ff772d9eeb7db7e9e0d0ab36
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fbac63f192c113786e7df0bce663a0e40a241b54534e01f18047d356d7ccb1514e4aeb8a610a336f85271def9b5956404091caccf6f0c984a20a723f67a364ce
|
7
|
+
data.tar.gz: 2f6dfe9ff9154500ea26a0c94a829eacb8b63ac84744791eff8f0b8643182f30118b1f1ed42f08486a4cc05c4e61c0cb8d800df53dcbccdbba5dadb786027b7a
|
data/{LICENSE → MIT-LICENSE}
RENAMED
@@ -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
|
-
|
95
|
+
ExecJS is work of dozens of contributors. You're encouraged to submit pull requests, propose
|
96
|
+
features and discuss issues.
|
77
97
|
|
78
|
-
|
98
|
+
See [CONTRIBUTING](CONTRIBUTING.md).
|
79
99
|
|
80
|
-
|
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(
|
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(
|
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(
|
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.
|
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(
|
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(
|
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(
|
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
|
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',
|
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)}
|
200
|
-
io = IO.popen(command,
|
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,
|
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(
|
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(
|
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(
|
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
|
-
|
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
|
data/lib/execjs/runtime.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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 =
|
35
|
-
|
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 =
|
40
|
-
|
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.
|
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?
|
data/lib/execjs/runtimes.rb
CHANGED
@@ -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: ["
|
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:
|
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
|
-
|
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
|
4
|
-
|
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
|
-
|
22
|
+
printFinal('["ok"]');
|
10
23
|
} else {
|
11
24
|
try {
|
12
|
-
|
25
|
+
printFinal(JSON.stringify(['ok', result]));
|
13
26
|
} catch (err) {
|
14
|
-
|
27
|
+
printFinal(JSON.stringify(['err', '' + err, err.stack]));
|
15
28
|
}
|
16
29
|
}
|
17
30
|
} catch (err) {
|
18
|
-
|
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
|
+
});
|
data/lib/execjs/version.rb
CHANGED
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.
|
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:
|
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:
|
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
|
-
|
74
|
-
|
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:
|
data/lib/execjs/encoding.rb
DELETED
@@ -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
|