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:
parent
744baef3a3
commit
03e360dc79
3 changed files with 83 additions and 29 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue