diff --git a/ext/v8/v8_exception.cpp b/ext/v8/v8_exception.cpp index d166301..73b53ef 100644 --- a/ext/v8/v8_exception.cpp +++ b/ext/v8/v8_exception.cpp @@ -69,21 +69,27 @@ namespace { return V8_Ref_Get(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 value) { return rr_v8_ref_create(StackTraceClass, value); } -VALUE rr_reflect_v8_stackframe(Handle value) { +VALUE rr_reflect_v8_stackframe(Handle value) { return rr_v8_ref_create(StackFrameClass, value); } diff --git a/lib/v8.rb b/lib/v8.rb index 8087f10..da88c2e 100644 --- a/lib/v8.rb +++ b/lib/v8.rb @@ -13,4 +13,5 @@ module V8 require 'v8/tap' require 'v8/access' require 'v8/error' + require 'v8/stack' end \ No newline at end of file diff --git a/lib/v8/context.rb b/lib/v8/context.rb index 47f7b69..5bc1402 100644 --- a/lib/v8/context.rb +++ b/lib/v8/context.rb @@ -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 = "", 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 diff --git a/lib/v8/portal.rb b/lib/v8/portal.rb index d3cd30b..a3b4698 100644 --- a/lib/v8/portal.rb +++ b/lib/v8/portal.rb @@ -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 diff --git a/lib/v8/stack.rb b/lib/v8/stack.rb new file mode 100644 index 0000000..c2acd37 --- /dev/null +++ b/lib/v8/stack.rb @@ -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 \ No newline at end of file diff --git a/spec/ext/cxt_spec.rb b/spec/ext/cxt_spec.rb index 5652bf6..b795575 100644 --- a/spec/ext/cxt_spec.rb +++ b/spec/ext/cxt_spec.rb @@ -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 \ No newline at end of file diff --git a/spec/redjs b/spec/redjs index 222bf1b..7914da0 160000 --- a/spec/redjs +++ b/spec/redjs @@ -1 +1 @@ -Subproject commit 222bf1b12fe6b58293668d8d8bed9a0dd968a9e8 +Subproject commit 7914da03e60cde3cde9d0aba83d36ddd49fe8b28