mirror of
https://github.com/rubyjs/therubyracer
synced 2023-03-27 23:21:42 -04:00
support objects with messages as error values
This commit is contained in:
parent
cc62436f3f
commit
f34cc5247a
4 changed files with 236 additions and 15 deletions
|
@ -3,6 +3,7 @@ require "v8/version"
|
|||
require 'v8/weak'
|
||||
require 'v8/init'
|
||||
require 'v8/error'
|
||||
require 'v8/stack'
|
||||
require 'v8/conversion/fundamental'
|
||||
require 'v8/conversion/indentity'
|
||||
require 'v8/conversion/reference'
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
module V8
|
||||
class Error < StandardError
|
||||
|
||||
# capture 99 stack frames on exception with normal details.
|
||||
# You can adjust these values for performance or turn of stack capture entirely
|
||||
V8::C::V8::SetCaptureStackTraceForUncaughtExceptions(true, 99, V8::C::StackTrace::kOverview)
|
||||
|
||||
attr_reader :value
|
||||
def initialize(message, value)
|
||||
super(message)
|
||||
|
@ -51,6 +56,8 @@ module V8
|
|||
else
|
||||
raise V8::Error.new(exception.Get("message").to_ruby, value)
|
||||
end
|
||||
elsif exception.IsObject()
|
||||
raise V8::Error.new(value['message'] || value.to_s, value)
|
||||
else
|
||||
raise V8::Error.new(exception.ToString().to_ruby, value)
|
||||
end
|
||||
|
|
66
lib/v8/stack.rb
Normal file
66
lib/v8/stack.rb
Normal file
|
@ -0,0 +1,66 @@
|
|||
|
||||
module V8
|
||||
|
||||
class StackTrace
|
||||
include Enumerable
|
||||
|
||||
def initialize(native)
|
||||
@native = native
|
||||
end
|
||||
|
||||
def length
|
||||
@native ? @native.GetFrameCount() : 0
|
||||
end
|
||||
|
||||
def each
|
||||
return unless @native
|
||||
for i in 0..length - 1
|
||||
yield V8::StackFrame.new(@native.GetFrame(i))
|
||||
end
|
||||
end
|
||||
|
||||
def to_s
|
||||
@native ? map(&:to_s).join("\n") : ""
|
||||
end
|
||||
end
|
||||
|
||||
class StackFrame
|
||||
|
||||
def initialize(native)
|
||||
@context = V8::Context.current
|
||||
@native = native
|
||||
end
|
||||
|
||||
def script_name
|
||||
@context.to_ruby(@native.GetScriptName())
|
||||
end
|
||||
|
||||
def function_name
|
||||
@context.to_ruby(@native.GetFunctionName())
|
||||
end
|
||||
|
||||
def line_number
|
||||
@native.GetLineNumber()
|
||||
end
|
||||
|
||||
def column
|
||||
@native.GetColumn()
|
||||
end
|
||||
|
||||
def eval?
|
||||
@native.IsEval()
|
||||
end
|
||||
|
||||
def constructor?
|
||||
@native.IsConstructor()
|
||||
end
|
||||
|
||||
def to_s
|
||||
"at " + if !function_name.empty?
|
||||
"#{function_name} (#{script_name}:#{line_number}:#{column})"
|
||||
else
|
||||
"#{script_name}:#{line_number}:#{column}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,21 +1,168 @@
|
|||
require 'spec_helper'
|
||||
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
||||
|
||||
describe V8::Error do
|
||||
it "uses the same ruby exception through multiple language boundaries" do
|
||||
V8::Context.new do |cxt|
|
||||
error = StandardError.new('potato')
|
||||
cxt['one'] = lambda do
|
||||
cxt.eval('two()', 'one.js')
|
||||
end
|
||||
cxt['two'] = lambda do
|
||||
cxt.eval('three()', 'two.js')
|
||||
end
|
||||
cxt['three'] = lambda do
|
||||
raise error
|
||||
end
|
||||
lambda {
|
||||
cxt.eval('one()')
|
||||
}.should raise_error {|e| e.should be error}
|
||||
|
||||
before do
|
||||
@cxt = V8::Context.new
|
||||
@cxt['one'] = lambda do
|
||||
@cxt.eval('two()', 'one.js')
|
||||
end
|
||||
@cxt['two'] = lambda do
|
||||
@cxt.eval('three()', 'two.js')
|
||||
end
|
||||
end
|
||||
|
||||
it "captures a message without over nesting when the error is an error" do
|
||||
throw! do |e|
|
||||
e.message.should == "BOOM!"
|
||||
end
|
||||
end
|
||||
|
||||
it "captures the js message without over nesting when the error is a normal object" do
|
||||
throw!('{foo: "bar"}') do |e|
|
||||
e.message.should == "[object Object]"
|
||||
end
|
||||
throw!('{message: "bar"}') do |e|
|
||||
e.message.should == "bar"
|
||||
end
|
||||
end
|
||||
|
||||
it "captures a thrown value as the message" do
|
||||
throw!('"BOOM!"') do |e|
|
||||
e.message.should == "BOOM!"
|
||||
end
|
||||
throw!('6') do |e|
|
||||
e.message.should == '6'
|
||||
end
|
||||
end
|
||||
|
||||
it "has a reference to the root javascript cause" do
|
||||
pending
|
||||
throw!('"I am a String"') do |e|
|
||||
e.should_not be_in_ruby
|
||||
e.should be_in_javascript
|
||||
e.value.should == "I am a String"
|
||||
end
|
||||
end
|
||||
|
||||
it "has a reference to the root ruby cause if one exists" do
|
||||
pending
|
||||
StandardError.new("BOOM!").tap do |bomb|
|
||||
@cxt['boom'] = lambda do
|
||||
raise bomb
|
||||
end
|
||||
lambda {
|
||||
@cxt.eval('boom()', 'boom.js')
|
||||
}.should(raise_error do |raised|
|
||||
raised.should be_in_ruby
|
||||
raised.should_not be_in_javascript
|
||||
raised.value.should be(bomb)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
describe "backtrace" do
|
||||
before {pending}
|
||||
it "is mixed with ruby and javascript" do
|
||||
throw! do |e|
|
||||
e.backtrace.first.should == "at three.js:1:7"
|
||||
e.backtrace[1].should =~ /error_spec.rb/
|
||||
e.backtrace[2].should == "at two.js:1:1"
|
||||
e.backtrace[3].should =~ /error_spec.rb/
|
||||
e.backtrace[4].should == "at one.js:1:1"
|
||||
end
|
||||
end
|
||||
|
||||
it "can be set to show only ruby frames" do
|
||||
throw! do |e|
|
||||
e.backtrace(:ruby).each do |frame|
|
||||
frame.should =~ /(\.rb|):\d+/
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "can be set to show only javascript frames" do
|
||||
throw! do |e|
|
||||
e.backtrace(:javascript).each do |frame|
|
||||
frame.should =~ /\.js:\d:\d/
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "includes a mystery marker when the original frame is unavailable because what got thrown wasn't an error" do
|
||||
throw!("6") do |e|
|
||||
e.backtrace.first.should == 'at three.js:1:1'
|
||||
end
|
||||
end
|
||||
|
||||
it "has a source name and line number when there is a javascript SyntaxError" do
|
||||
lambda do
|
||||
@cxt.eval(<<-INVALID, 'source.js')
|
||||
"this line is okay";
|
||||
"this line has a syntax error because it ends with a colon":
|
||||
"this line is also okay";
|
||||
"how do I find out that line 2 has the syntax error?";
|
||||
INVALID
|
||||
end.should raise_error(V8::JSError) {|error|
|
||||
error.backtrace.first.should == 'at source.js:2:60'
|
||||
}
|
||||
end
|
||||
|
||||
it "can start with ruby at the bottom" do
|
||||
@cxt['boom'] = lambda do
|
||||
raise StandardError, "Bif!"
|
||||
end
|
||||
lambda {
|
||||
@cxt.eval('boom()', "boom.js")
|
||||
}.should(raise_error {|e|
|
||||
e.backtrace.first.should =~ /error_spec\.rb/
|
||||
e.backtrace[1].should =~ /boom.js/
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def throw!(js = "new Error('BOOM!')", &block)
|
||||
@cxt['three'] = lambda do
|
||||
@cxt.eval("throw #{js}", 'three.js')
|
||||
end
|
||||
lambda do
|
||||
@cxt['one'].call()
|
||||
end.should(raise_error(V8::JSError, &block))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# describe V8::Error do
|
||||
# describe "A ruby exception thrown inside JavaScript" do
|
||||
# before do
|
||||
# @error = StandardError.new('potato')
|
||||
# begin
|
||||
# V8::Context.new do |cxt|
|
||||
# cxt['one'] = lambda do
|
||||
# cxt.eval('two()', 'one.js')
|
||||
# end
|
||||
# cxt['two'] = lambda do
|
||||
# cxt.eval('three()', 'two.js')
|
||||
# end
|
||||
# cxt['three'] = lambda do
|
||||
# raise @error
|
||||
# end
|
||||
# cxt.eval('one()')
|
||||
# end
|
||||
# rescue StandardError => e
|
||||
# @thrown = e
|
||||
# end
|
||||
# end
|
||||
# it "is raised up through the call stack" do
|
||||
# @thrown.should be(@error)
|
||||
# end
|
||||
#
|
||||
# it "shows both the javascript and the ruby callframes" do
|
||||
# puts @error.backtrace.join('<br/>')
|
||||
# end
|
||||
#
|
||||
# end
|
||||
# end
|
||||
|
|
Loading…
Add table
Reference in a new issue