1
0
Fork 0
mirror of https://github.com/rubyjs/therubyracer synced 2023-03-27 23:21:42 -04:00

support for bilingual backtraces

This commit is contained in:
Charles Lowell 2012-08-13 10:12:14 -05:00
parent 744baef3a3
commit 03e360dc79
3 changed files with 83 additions and 29 deletions

View file

@ -4,7 +4,20 @@ module V8
V8::C::V8::SetCaptureStackTraceForUncaughtExceptions(true, 99, V8::C::StackTrace::kOverview)
class Error < StandardError
include Enumerable
attr_reader :value, :cause, :javascript_backtrace
# @!attribute [r] value
# @return [Object] the JavaScript value passed to the `throw` statement
attr_reader :value
# @!attribute [r] cause
# @return [Exception] the underlying error (if any) that triggered this error to be raised
attr_reader :cause
# @!attribute [V8::StackTrace] javascript_backtrace
# @return the complete JavaScript stack at the point this error was thrown
attr_reader :javascript_backtrace
alias_method :standard_error_backtrace, :backtrace
def initialize(message, value, javascript_backtrace, cause = nil)
super(message)
@ -23,6 +36,16 @@ module V8
end
end
def backtrace(*modifiers)
return unless super()
trace_framework = modifiers.include?(:framework)
trace_ruby = modifiers.length == 0 || modifiers.include?(:ruby)
trace_javascript = modifiers.length == 0 || modifiers.include?(:javascript)
bilingual_backtrace(trace_ruby, trace_javascript).tap do |trace|
trace.reject! {|frame| frame =~ %r{lib/v8/.*\.rb}} unless modifiers.include?(:framework)
end
end
def root_cause
causes.last
end
@ -35,15 +58,20 @@ module V8
!in_javascript?
end
def multilingual_backtrace
causes.reduce(:backtrace => [], :ruby => -1, :javascript => -1) { |accumulator, cause|
ruby_frames = cause.backtrace[0..accumulator[:ruby]]
accumulator[:backtrace].unshift *ruby_frames
accumulator[:ruby] -= ruby_frames.length
if cause.respond_to?(:javascript_backtrace)
javascript_frames = cause.javascript_frames[0, accumulator[:javascript]].map(&:to_s)
accumulator[:backtrace].unshift *javascript_frames
accumulator[:javascript] -= javascript_frames.length
def bilingual_backtrace(trace_ruby = true, trace_javascript = true)
backtrace = causes.reduce(:backtrace => [], :ruby => -1, :javascript => -1) { |accumulator, cause|
accumulator.tap do
if trace_ruby
backtrace_selector = cause.respond_to?(:standard_error_backtrace) ? :standard_error_backtrace : :backtrace
ruby_frames = cause.send(backtrace_selector)[0..accumulator[:ruby]]
accumulator[:backtrace].unshift *ruby_frames
accumulator[:ruby] -= ruby_frames.length
end
if trace_javascript && cause.respond_to?(:javascript_backtrace)
javascript_frames = cause.javascript_backtrace.to_a[0..accumulator[:javascript]].map(&:to_s)
accumulator[:backtrace].unshift *javascript_frames
accumulator[:javascript] -= javascript_frames.length
end
end
}[:backtrace]
end
@ -77,19 +105,27 @@ module V8
exception = trycatch.Exception()
value = exception.to_ruby
cause = nil
javascript_backtrace = V8::StackTrace.new(trycatch.Message().GetStackTrace())
message = if !exception.kind_of?(V8::C::Value)
exception.to_s
elsif exception.IsNativeError()
if cause = exception.GetHiddenValue("rr::Cause")
cause = cause.Value()
end
exception.Get("message").to_ruby
# SyntaxErrors do not have a JavaScript stack (even if they occur during js execution).
# To caputre where the error occured, we need to put it in the message
if value['constructor'] == V8::Context.current['SyntaxError']
info = trycatch.Message()
resource_name = info.GetScriptResourceName().to_ruby
"#{value['message']} at #{resource_name}:#{info.GetLineNumber()}:#{info.GetStartColumn() + 1}"
else
exception.Get("message").to_ruby
end
elsif exception.IsObject()
value['message'] || value.to_s
else
value.to_s
end
javascript_backtrace = V8::StackTrace.new(trycatch.Message().GetStackTrace())
V8::Error.new(message, value, javascript_backtrace, cause)
end
const_set :JSError, Error

View file

@ -5,17 +5,22 @@ module V8
include Enumerable
def initialize(native)
@context = V8::Context.current
@native = native
end
def length
@native ? @native.GetFrameCount() : 0
@context.enter do
@native ? @native.GetFrameCount() : 0
end
end
def each
return unless @native
for i in 0..length - 1
yield V8::StackFrame.new(@native.GetFrame(i))
@context.enter do
for i in 0..length - 1
yield V8::StackFrame.new(@native.GetFrame(i), @context)
end
end
end
@ -26,40 +31,54 @@ module V8
class StackFrame
def initialize(native)
@context = V8::Context.current
def initialize(native, context)
@context = context
@native = native
end
def script_name
@context.to_ruby(@native.GetScriptName())
@context.enter do
@context.to_ruby(@native.GetScriptName())
end
end
def function_name
@context.to_ruby(@native.GetFunctionName())
@context.enter do
@context.to_ruby(@native.GetFunctionName())
end
end
def line_number
@native.GetLineNumber()
@context.enter do
@native.GetLineNumber()
end
end
def column
@native.GetColumn()
@context.enter do
@native.GetColumn()
end
end
def eval?
@native.IsEval()
@context.enter do
@native.IsEval()
end
end
def constructor?
@native.IsConstructor()
@context.enter do
@native.IsConstructor()
end
end
def to_s
"at " + if !function_name.empty?
"#{function_name} (#{script_name}:#{line_number}:#{column})"
else
"#{script_name}:#{line_number}:#{column}"
@context.enter do
"at " + if !function_name.empty?
"#{function_name} (#{script_name}:#{line_number}:#{column})"
else
"#{script_name}:#{line_number}:#{column}"
end
end
end
end

View file

@ -62,7 +62,6 @@ describe V8::Error do
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"
@ -104,7 +103,7 @@ describe V8::Error do
"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'
error.message.should eql 'Unexpected token : at source.js:2:61'
}
end