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

add support for retrieving the JavaScript stack trace from Ruby via Context#stack.

This commit is contained in:
Charles Lowell 2011-03-10 06:25:54 -06:00
parent ff548a79ee
commit bfb9e9e33a
7 changed files with 136 additions and 15 deletions

View file

@ -69,21 +69,27 @@ namespace {
return V8_Ref_Get<StackFrame>(value);
}
VALUE GetLineNumber(VALUE self) {
HandleScope scope;
return rr_v82rb(frame(self)->GetLineNumber());
}
VALUE GetColumn(VALUE self) {
HandleScope scope;
return rr_v82rb(frame(self)->GetColumn());
}
VALUE GetScriptName(VALUE self) {
HandleScope scope;
return rr_v82rb(frame(self)->GetScriptName());
}
VALUE GetFunctionName(VALUE self) {
HandleScope scope;
return rr_v82rb(frame(self)->GetFunctionName());
}
VALUE IsEval(VALUE self) {
HandleScope scope;
return rr_v82rb(frame(self)->IsEval());
}
VALUE IsConstructor(VALUE self) {
HandleScope scope;
return rr_v82rb(frame(self)->IsConstructor());
}
}
@ -102,7 +108,7 @@ void rr_init_v8_exception() {
rr_define_singleton_method(ExceptionClass, "Error", Error, 1);
StackTraceClass = rr_define_class("StackTrace");
rr_define_singleton_method(StackTraceClass, "CurrentStackTrace", Trace::CurrentStackTrace, 0);
rr_define_singleton_method(StackTraceClass, "CurrentStackTrace", Trace::CurrentStackTrace, 1);
rr_define_method(StackTraceClass, "GetFrame", Trace::GetFrame, 1);
rr_define_method(StackTraceClass, "GetFrameCount", Trace::GetFrameCount, 0);
rr_define_method(StackTraceClass, "AsArray", Trace::AsArray, 0);
@ -123,6 +129,6 @@ void rr_init_v8_exception() {
VALUE rr_reflect_v8_stacktrace(Handle<StackTrace> value) {
return rr_v8_ref_create(StackTraceClass, value);
}
VALUE rr_reflect_v8_stackframe(Handle<StackTrace> value) {
VALUE rr_reflect_v8_stackframe(Handle<StackFrame> value) {
return rr_v8_ref_create(StackFrameClass, value);
}

View file

@ -13,4 +13,5 @@ module V8
require 'v8/tap'
require 'v8/access'
require 'v8/error'
require 'v8/stack'
end

View file

@ -1,8 +1,9 @@
require 'stringio'
module V8
class Context
class Context
attr_reader :native, :scope, :access
def initialize(opts = {})
@access = Access.new
@to = Portal.new(self, @access)
@ -11,10 +12,11 @@ module V8
@global = @native.Global()
@scope = @to.rb(@global)
@global.SetHiddenValue(C::String::New("TheRubyRacer::RubyObject"), C::External::New(opts[:with])) if opts[:with]
@global.SetHiddenValue(C::String::NewSymbol("TheRubyRacer::RubyContext"), C::External::New(self))
end
yield(self) if block_given?
end
def eval(javascript, filename = "<eval>", line = 1)
if IO === javascript || StringIO === javascript
javascript = javascript.read()
@ -43,16 +45,26 @@ module V8
def load(filename)
File.open(filename) do |file|
evaluate file, filename, 1
end
end
end
def [](key)
@scope[key]
end
def []=(key, value)
@scope[key] = value
end
def self.stack(limit = 99)
if native = C::Context::GetEntered()
global = native.Global().instance_eval {@native}
cxt = global.GetHiddenValue(C::String::NewSymbol("TheRubyRacer::RubyContext")).Value()
cxt.instance_eval {@to.rb(C::StackTrace::CurrentStackTrace(limit))}
else
[]
end
end
end
module C

View file

@ -78,12 +78,13 @@ module V8
def rb(value)
case value
when V8::C::Function then peer(value) {V8::Function}
when V8::C::Array then peer(value) {V8::Array}
when V8::C::Object then peer(value) {V8::Object}
when V8::C::String then value.Utf8Value.tap {|s| return s.respond_to?(:force_encoding) ? s.force_encoding("UTF-8") : s}
when V8::C::Date then Time.at(value.NumberValue() / 1000)
when V8::C::Value then nil if value.IsEmpty()
when V8::C::Function then peer(value) {V8::Function}
when V8::C::Array then peer(value) {V8::Array}
when V8::C::Object then peer(value) {V8::Object}
when V8::C::String then value.Utf8Value.tap {|s| return s.respond_to?(:force_encoding) ? s.force_encoding("UTF-8") : s}
when V8::C::Date then Time.at(value.NumberValue() / 1000)
when V8::C::StackTrace then V8::StackTrace.new(self, value)
when V8::C::Value then nil if value.IsEmpty()
else
value
end

66
lib/v8/stack.rb Normal file
View file

@ -0,0 +1,66 @@
module V8
class StackTrace
include Enumerable
def initialize(to, native)
@to = to
@native = native
end
def length
@native.GetFrameCount()
end
def each
for i in 0..length - 1
yield V8::StackFrame.new(@to, @native.GetFrame(i))
end
end
def to_s
map {|f|"at #{f}"}.join("\n")
end
end
class StackFrame
def initialize(portal, native)
@to = portal
@native = native
end
def script_name
@to.rb(@native.GetScriptName())
end
def function_name
@to.rb(@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
if @native.GetFunctionName()
"#{function_name} (#{script_name}:#{line_number}:#{column})"
else
"#{script_name}:#{line_number}:#{column}"
end
end
end
end

View file

@ -4,9 +4,44 @@ require "#{File.dirname(__FILE__)}/../spec_helper.rb"
include V8
describe C::Context do
it "should not have a current context if no context is open" do
C::Context::GetEntered().should be_nil
end
it "can get the current javascript execution stack" do
V8::Context.new do |cxt|
cxt['getTrace'] = lambda do
V8::Context.stack
end
trace = cxt.eval(<<-JS, 'trace.js')
function one() {
return two();
}
function two() {
return three();
}
function three() {
return getTrace()
}
one();
JS
trace.length.should be(4)
trace.first.tap do |frame|
frame.line_number.should == 10
frame.column.should == 16
frame.script_name.should == 'trace.js'
frame.function_name.should == 'three'
frame.should_not be_eval
frame.should_not be_constructor
end
end
end
it "has an empty stack if there is no enterned context" do
V8::Context.stack.should be_empty
end
end

@ -1 +1 @@
Subproject commit 222bf1b12fe6b58293668d8d8bed9a0dd968a9e8
Subproject commit 7914da03e60cde3cde9d0aba83d36ddd49fe8b28