1
0
Fork 0
mirror of https://github.com/rails/execjs synced 2023-03-27 23:21:20 -04:00

Merge branch 'master' into duktape

This commit is contained in:
Joshua Peek 2015-03-09 16:35:36 -07:00
commit 2280734851
14 changed files with 46331 additions and 64 deletions

View file

@ -1,6 +1,6 @@
sudo: false
language: ruby language: ruby
rvm: rvm:
- 1.9.3
- 2.0.0 - 2.0.0
- 2.1 - 2.1
- 2.2 - 2.2

View file

@ -64,7 +64,7 @@ older stock runtimes like JSC on OSX and JScript on Windows may not. You should
only count on ES3 features being available. Prefer feature checking these APIs only count on ES3 features being available. Prefer feature checking these APIs
rather than hard coding support for specific runtimes. rather than hard coding support for specific runtimes.
**Can I ExecJS be used to sandbox scripts?** **Can ExecJS be used to sandbox scripts?**
No, ExecJS shouldn't be used for any security related sandboxing. Since runtimes No, ExecJS shouldn't be used for any security related sandboxing. Since runtimes
are automatically detected, each runtime has different sandboxing properties. are automatically detected, each runtime has different sandboxing properties.

View file

@ -24,12 +24,12 @@ module ExecJS
def exec(source, options = {}) def exec(source, options = {})
source = encode(source) source = encode(source)
source = "#{@source}\n#{source}" if @source source = "#{@source}\n#{source}" if @source != ""
source = @runtime.compile_source(source) source = @runtime.compile_source(source)
tmpfile = write_to_tempfile(source) tmpfile = write_to_tempfile(source)
begin begin
extract_result(@runtime.exec_runtime(tmpfile.path)) extract_result(@runtime.exec_runtime(tmpfile.path), tmpfile.path)
ensure ensure
File.unlink(tmpfile) File.unlink(tmpfile)
end end
@ -57,14 +57,25 @@ module ExecJS
tmpfile tmpfile
end end
def extract_result(output) def extract_result(output, filename)
status, value = output.empty? ? [] : ::JSON.parse(output, create_additions: false) status, value, stack = output.empty? ? [] : ::JSON.parse(output, create_additions: false)
if status == "ok" if status == "ok"
value value
elsif value =~ /SyntaxError:/
raise RuntimeError, value
else else
raise ProgramError, value stack ||= ""
real_filename = File.realpath(filename)
stack = stack.split("\n").map do |line|
line.sub(" at ", "")
.sub(real_filename, "(execjs)")
.sub(filename, "(execjs)")
.strip
end
stack.reject! { |line| ["eval code", "eval@[native code]"].include?(line) }
stack.shift unless stack[0].to_s.include?("(execjs)")
error_class = value =~ /SyntaxError:/ ? RuntimeError : ProgramError
error = error_class.new(value)
error.set_backtrace(stack + caller)
raise error
end end
end end
end end
@ -158,7 +169,7 @@ module ExecJS
if $?.success? if $?.success?
output output
else else
raise RuntimeError, output raise exec_runtime_error(output)
end end
end end
@ -181,7 +192,7 @@ module ExecJS
if $?.success? if $?.success?
output output
else else
raise RuntimeError, output raise exec_runtime_error(output)
end end
end end
else else
@ -193,13 +204,22 @@ module ExecJS
if $?.success? if $?.success?
output output
else else
raise RuntimeError, output raise exec_runtime_error(output)
end end
end end
end end
# Internally exposed for Context. # Internally exposed for Context.
public :exec_runtime public :exec_runtime
def exec_runtime_error(output)
error = RuntimeError.new(output)
lines = output.split("\n")
lineno = lines[0][/:(\d+)$/, 1] if lines[0]
lineno ||= 1
error.set_backtrace(["(execjs):#{lineno}"] + caller)
error
end
def which(command) def which(command)
Array(command).find do |name| Array(command).find do |name|
name, args = name.split(/\s+/, 2) name, args = name.split(/\s+/, 2)

View file

@ -12,11 +12,7 @@ module ExecJS
begin begin
@v8_context.eval(source) @v8_context.eval(source)
rescue ::V8::JSError => e rescue ::V8::JSError => e
if e.value["name"] == "SyntaxError" raise wrap_error(e)
raise RuntimeError, e.value.to_s
else
raise ProgramError, e.value.to_s
end
end end
end end
end end
@ -37,11 +33,7 @@ module ExecJS
begin begin
unbox @v8_context.eval("(#{source})") unbox @v8_context.eval("(#{source})")
rescue ::V8::JSError => e rescue ::V8::JSError => e
if e.value["name"] == "SyntaxError" raise wrap_error(e)
raise RuntimeError, e.value.to_s
else
raise ProgramError, e.value.to_s
end
end end
end end
end end
@ -52,11 +44,7 @@ module ExecJS
begin begin
unbox @v8_context.eval(properties).call(*args) unbox @v8_context.eval(properties).call(*args)
rescue ::V8::JSError => e rescue ::V8::JSError => e
if e.value["name"] == "SyntaxError" raise wrap_error(e)
raise RuntimeError, e.value.to_s
else
raise ProgramError, e.value.to_s
end
end end
end end
end end
@ -96,6 +84,20 @@ module ExecJS
result result
end end
end end
def wrap_error(e)
error_class = e.value["name"] == "SyntaxError" ? RuntimeError : ProgramError
stack = e.value["stack"] || ""
stack = stack.split("\n")
stack.shift
stack = [e.message[/<eval>:\d+:\d+/, 0]].compact if stack.empty?
stack = stack.map { |line| line.sub(" at ", "").sub("<eval>", "(execjs)").strip }
error = error_class.new(e.value.to_s)
error.set_backtrace(stack + caller)
error
end
end end
def name def name

View file

@ -10,7 +10,7 @@ module ExecJS
fix_memory_limit! @rhino_context fix_memory_limit! @rhino_context
@rhino_context.eval(source) @rhino_context.eval(source)
rescue Exception => e rescue Exception => e
reraise_error(e) raise wrap_error(e)
end end
def exec(source, options = {}) def exec(source, options = {})
@ -28,13 +28,13 @@ module ExecJS
unbox @rhino_context.eval("(#{source})") unbox @rhino_context.eval("(#{source})")
end end
rescue Exception => e rescue Exception => e
reraise_error(e) raise wrap_error(e)
end end
def call(properties, *args) def call(properties, *args)
unbox @rhino_context.eval(properties).call(*args) unbox @rhino_context.eval(properties).call(*args)
rescue Exception => e rescue Exception => e
reraise_error(e) raise wrap_error(e)
end end
def unbox(value) def unbox(value)
@ -58,17 +58,18 @@ module ExecJS
end end
end end
def reraise_error(e) def wrap_error(e)
case e return e unless e.is_a?(::Rhino::JSError)
when ::Rhino::JSError
if e.message == "syntax error" error_class = e.message == "syntax error" ? RuntimeError : ProgramError
raise RuntimeError, e.message
else stack = e.backtrace
raise ProgramError, e.message stack = stack.map { |line| line.sub(" at ", "").sub("<eval>", "(execjs)").strip }
end stack.unshift("(execjs):1") if e.javascript_backtrace.empty?
else
raise e error = error_class.new(e.value.to_s)
end error.set_backtrace(stack)
error
end end
private private

View file

@ -9,10 +9,10 @@
try { try {
print(JSON.stringify(['ok', result])); print(JSON.stringify(['ok', result]));
} catch (err) { } catch (err) {
print('["err"]'); print(JSON.stringify(['err', '' + err, err.stack]));
} }
} }
} catch (err) { } catch (err) {
print(JSON.stringify(['err', '' + err])); print(JSON.stringify(['err', '' + err, err.stack]));
} }
}); });

View file

@ -13,10 +13,10 @@
try { try {
print(JSON.stringify(['ok', result])); print(JSON.stringify(['ok', result]));
} catch (err) { } catch (err) {
print('["err"]'); print(JSON.stringify(['err', err.name + ': ' + err.message, err.stack]));
} }
} }
} catch (err) { } catch (err) {
print(JSON.stringify(['err', err.name + ': ' + err.message])); print(JSON.stringify(['err', err.name + ': ' + err.message, err.stack]));
} }
}); });

View file

@ -11,10 +11,10 @@
try { try {
print(JSON.stringify(['ok', result])); print(JSON.stringify(['ok', result]));
} catch (err) { } catch (err) {
print('["err"]'); print(JSON.stringify(['err', '' + err, err.stack]));
} }
} }
} catch (err) { } catch (err) {
print(JSON.stringify(['err', '' + err])); print(JSON.stringify(['err', '' + err, err.stack]));
} }
}); });

View file

@ -9,10 +9,10 @@
try { try {
print(JSON.stringify(['ok', result])); print(JSON.stringify(['ok', result]));
} catch (err) { } catch (err) {
print('["err"]'); print(JSON.stringify(['err', '' + err, err.stack]));
} }
} }
} catch (err) { } catch (err) {
print(JSON.stringify(['err', '' + err])); print(JSON.stringify(['err', '' + err, err.stack]));
} }
}); });

View file

@ -1,3 +1,3 @@
module ExecJS module ExecJS
VERSION = "2.3.0" VERSION = "2.4.0"
end end

43557
test/fixtures/babel.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2613
test/fixtures/uglify.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -253,41 +253,98 @@ class TestExecJS < Test
end end
def test_exec_syntax_error def test_exec_syntax_error
assert_raises ExecJS::RuntimeError do begin
ExecJS.exec(")") ExecJS.exec(")")
flunk
rescue ExecJS::RuntimeError => e
assert e
assert e.backtrace[0].include?("(execjs):1"), e.backtrace.join("\n")
end end
end end
def test_eval_syntax_error def test_eval_syntax_error
assert_raises ExecJS::RuntimeError do begin
ExecJS.eval(")") ExecJS.eval(")")
flunk
rescue ExecJS::RuntimeError => e
assert e
assert e.backtrace[0].include?("(execjs):1"), e.backtrace.join("\n")
end end
end end
def test_compile_syntax_error def test_compile_syntax_error
assert_raises ExecJS::RuntimeError do begin
ExecJS.compile(")") ExecJS.compile(")")
flunk
rescue ExecJS::RuntimeError => e
assert e
assert e.backtrace[0].include?("(execjs):1"), e.backtrace.join("\n")
end end
end end
def test_exec_thrown_exception def test_exec_thrown_error
begin
ExecJS.exec("throw new Error('hello')")
flunk
rescue ExecJS::ProgramError => e
assert e
assert e.backtrace[0].include?("(execjs):1"), e.backtrace.join("\n")
end
end
def test_eval_thrown_error
begin
ExecJS.eval("(function(){ throw new Error('hello') })()")
flunk
rescue ExecJS::ProgramError => e
assert e
assert e.backtrace[0].include?("(execjs):1"), e.backtrace.join("\n")
end
end
def test_compile_thrown_error
begin
ExecJS.compile("throw new Error('hello')")
flunk
rescue ExecJS::ProgramError => e
assert e
assert e.backtrace[0].include?("(execjs):1"), e.backtrace.join("\n")
end
end
def test_exec_thrown_string
assert_raises ExecJS::ProgramError do assert_raises ExecJS::ProgramError do
ExecJS.exec("throw 'hello'") ExecJS.exec("throw 'hello'")
end end
end end
def test_eval_thrown_exception def test_eval_thrown_string
assert_raises ExecJS::ProgramError do assert_raises ExecJS::ProgramError do
ExecJS.exec("throw 'hello'") ExecJS.eval("(function(){ throw 'hello' })()")
end end
end end
def test_compile_thrown_exception def test_compile_thrown_string
assert_raises ExecJS::ProgramError do assert_raises ExecJS::ProgramError do
ExecJS.exec("throw 'hello'") ExecJS.compile("throw 'hello'")
end end
end end
def test_babel
skip if ExecJS.runtime.is_a?(ExecJS::RubyRhinoRuntime)
assert source = File.read(File.expand_path("../fixtures/babel.js", __FILE__))
source = <<-JS
var self = this;
#{source}
babel.eval = function(code) {
return eval(babel.transform(code)["code"]);
}
JS
context = ExecJS.compile(source)
assert_equal 64, context.call("babel.eval", "((x) => x * x)(8)")
end
def test_coffeescript def test_coffeescript
# Skip coffeescript on Duktape for now # Skip coffeescript on Duktape for now
skip if ExecJS.runtime.is_a?(ExecJS::DuktapeRuntime) skip if ExecJS.runtime.is_a?(ExecJS::DuktapeRuntime)
@ -296,4 +353,21 @@ class TestExecJS < Test
context = ExecJS.compile(source) context = ExecJS.compile(source)
assert_equal 64, context.call("CoffeeScript.eval", "((x) -> x * x)(8)") assert_equal 64, context.call("CoffeeScript.eval", "((x) -> x * x)(8)")
end end
def test_uglify
assert source = File.read(File.expand_path("../fixtures/uglify.js", __FILE__))
source = <<-JS
#{source}
function uglify(source) {
var ast = UglifyJS.parse(source);
var stream = UglifyJS.OutputStream();
ast.print(stream);
return stream.toString();
}
JS
context = ExecJS.compile(source)
assert_equal "function foo(bar){return bar}",
context.call("uglify", "function foo(bar) {\n return bar;\n}")
end
end end