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:
parent
ff548a79ee
commit
bfb9e9e33a
7 changed files with 136 additions and 15 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -13,4 +13,5 @@ module V8
|
|||
require 'v8/tap'
|
||||
require 'v8/access'
|
||||
require 'v8/error'
|
||||
require 'v8/stack'
|
||||
end
|
|
@ -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
|
||||
|
|
|
@ -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
66
lib/v8/stack.rb
Normal 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
|
|
@ -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
|
Loading…
Reference in a new issue