support catching javascript exceptions

This commit is contained in:
Charles Lowell 2012-05-25 15:53:13 -05:00
parent e1a21538a4
commit a5812812bc
8 changed files with 543 additions and 1 deletions

View File

@ -24,5 +24,8 @@ extern "C" {
External::Init();
Script::Init();
Template::Init();
Stack::Init();
Message::Init();
TryCatch::Init();
}
}

51
ext/v8/message.cc Normal file
View File

@ -0,0 +1,51 @@
#include "rr.h"
namespace rr {
void Message::Init() {
ClassBuilder("Message").
defineMethod("Get", &Get).
defineMethod("GetSourceLine", &GetSourceLine).
defineMethod("GetScriptResourceName", &GetScriptResourceName).
defineMethod("GetScriptData", &GetScriptData).
defineMethod("GetStackTrace", &GetStackTrace).
defineMethod("GetLineNumber", &GetLineNumber).
defineMethod("GetStartPosition", &GetStartPosition).
defineMethod("GetEndPosition", &GetEndPosition).
defineMethod("GetStartColumn", &GetEndColumn).
defineSingletonMethod("kNoLineNumberInfo", &kNoLineNumberInfo).
defineSingletonMethod("kNoColumnInfo", &kNoColumnInfo).
store(&Class);
}
VALUE Message::Get(VALUE self) {
return String(Message(self)->Get());
}
VALUE Message::GetSourceLine(VALUE self) {
return String(Message(self)->GetSourceLine());
}
VALUE Message::GetScriptResourceName(VALUE self) {
return Value(Message(self)->GetScriptResourceName());
}
VALUE Message::GetScriptData(VALUE self) {
return Value(Message(self)->GetScriptData());
}
VALUE Message::GetStackTrace(VALUE self) {
return Stack::Trace(Message(self)->GetStackTrace());
}
VALUE Message::GetLineNumber(VALUE self) {
return INT2FIX(Message(self)->GetLineNumber());
}
VALUE Message::GetStartPosition(VALUE self) {
return INT2FIX(Message(self)->GetStartPosition());
}
VALUE Message::GetEndPosition(VALUE self) {
return INT2FIX(Message(self)->GetEndPosition());
}
VALUE Message::GetStartColumn(VALUE self) {
return INT2FIX(Message(self)->GetStartColumn());
}
VALUE Message::GetEndColumn(VALUE self) {
return INT2FIX(Message(self)->GetEndColumn());
}
}

View File

@ -209,6 +209,40 @@ public:
class Value : public Ref<v8::Value> {
public:
static void Init();
static VALUE IsUndefined(VALUE self);
static VALUE IsNull(VALUE self);
static VALUE IsTrue(VALUE self);
static VALUE IsFalse(VALUE self);
static VALUE IsString(VALUE self);
static VALUE IsFunction(VALUE self);
static VALUE IsArray(VALUE self);
static VALUE IsObject(VALUE self);
static VALUE IsBoolean(VALUE self);
static VALUE IsNumber(VALUE self);
static VALUE IsExternal(VALUE self);
static VALUE IsInt32(VALUE self);
static VALUE IsUint32(VALUE self);
static VALUE IsDate(VALUE self);
static VALUE IsBooleanObject(VALUE self);
static VALUE IsNumberObject(VALUE self);
static VALUE IsStringObject(VALUE self);
static VALUE IsNativeError(VALUE self);
static VALUE IsRegExp(VALUE self);
// VALUE ToBoolean(VALUE self);
// VALUE ToNumber(VALUE self);
static VALUE ToString(VALUE self);
static VALUE ToDetailString(VALUE self);
static VALUE ToObject(VALUE self);
// static VALUE ToInteger(VALUE self);
// static VALUE ToUint32(VALUE self);
// static VALUE ToInt32(VALUE self);
// static VALUE ToArrayIndex(VALUE self);
static VALUE BooleanValue(VALUE self);
static VALUE NumberValue(VALUE self);
static VALUE IntegerValue(VALUE self);
static VALUE Uint32Value(VALUE self);
static VALUE Int32Value(VALUE self);
static VALUE Equals(VALUE self, VALUE other);
static VALUE StrictEquals(VALUE self, VALUE other);
inline Value(VALUE value) : Ref<v8::Value>(value) {}
@ -480,10 +514,99 @@ public:
inline FunctionTemplate(v8::Handle<v8::FunctionTemplate> t) : Ref<v8::FunctionTemplate>(t) {}
};
class Message : public Ref<v8::Message> {
public:
static void Init();
inline Message(v8::Handle<v8::Message> message) : Ref<v8::Message>(message) {}
inline Message(VALUE value) : Ref<v8::Message>(value) {}
static VALUE Get(VALUE self);
static VALUE GetSourceLine(VALUE self);
static VALUE GetScriptResourceName(VALUE self);
static VALUE GetScriptData(VALUE self);
static VALUE GetStackTrace(VALUE self);
static VALUE GetLineNumber(VALUE self);
static VALUE GetStartPosition(VALUE self);
static VALUE GetEndPosition(VALUE self);
static VALUE GetStartColumn(VALUE self);
static VALUE GetEndColumn(VALUE self);
static inline VALUE kNoLineNumberInfo(VALUE self) {return INT2FIX(v8::Message::kNoLineNumberInfo);}
static inline VALUE kNoColumnInfo(VALUE self) {return INT2FIX(v8::Message::kNoColumnInfo);}
};
class Stack {
public:
static void Init();
class Trace : public Ref<v8::StackTrace> {
public:
class StackTraceOptions : public Enum<v8::StackTrace::StackTraceOptions> {
public:
inline StackTraceOptions(VALUE value) : Enum<v8::StackTrace::StackTraceOptions>(value, v8::StackTrace::kOverview) {}
};
public:
inline Trace(v8::Handle<v8::StackTrace> trace) : Ref<v8::StackTrace>(trace) {}
inline Trace(VALUE value) : Ref<v8::StackTrace>(value) {}
static inline VALUE kLineNumber(VALUE self) {return INT2FIX(v8::StackTrace::kLineNumber);}
static inline VALUE kColumnOffset(VALUE self) {return INT2FIX(v8::StackTrace::kColumnOffset);}
static inline VALUE kScriptName(VALUE self) {return INT2FIX(v8::StackTrace::kScriptName);}
static inline VALUE kFunctionName(VALUE self) {return INT2FIX(v8::StackTrace::kFunctionName);}
static inline VALUE kIsEval(VALUE self) {return INT2FIX(v8::StackTrace::kIsEval);}
static inline VALUE kIsConstructor(VALUE self) {return INT2FIX(v8::StackTrace::kIsConstructor);}
static inline VALUE kScriptNameOrSourceURL(VALUE self) {return INT2FIX(v8::StackTrace::kScriptNameOrSourceURL);}
static inline VALUE kOverview(VALUE self) {return INT2FIX(v8::StackTrace::kOverview);}
static inline VALUE kDetailed(VALUE self) {return INT2FIX(v8::StackTrace::kDetailed);}
static VALUE GetFrame(VALUE self, VALUE index);
static VALUE GetFrameCount(VALUE self);
static VALUE AsArray(VALUE self);
static VALUE CurrentStackTrace(int argc, VALUE argv[], VALUE self);
};
class Frame : public Ref<v8::StackFrame> {
public:
inline Frame(v8::Handle<v8::StackFrame> frame) : Ref<v8::StackFrame>(frame) {}
inline Frame(VALUE value) : Ref<v8::StackFrame>(value) {}
static VALUE GetLineNumber(VALUE self);
static VALUE GetColumn(VALUE self);
static VALUE GetScriptName(VALUE self);
static VALUE GetScriptNameOrSourceURL(VALUE self);
static VALUE GetFunctionName(VALUE self);
static VALUE IsEval(VALUE self);
static VALUE IsConstructor(VALUE self);
};
};
class TryCatch {
public:
static void Init();
TryCatch();
TryCatch(VALUE value);
~TryCatch();
operator VALUE();
inline v8::TryCatch* operator->() {return this->impl;}
static VALUE HasCaught(VALUE self);
static VALUE CanContinue(VALUE self);
static VALUE ReThrow(VALUE self);
static VALUE Exception(VALUE self);
static VALUE StackTrace(VALUE self);
static VALUE Message(VALUE self);
static VALUE Reset(VALUE self);
static VALUE SetVerbose(VALUE self, VALUE value);
static VALUE SetCaptureMessage(VALUE self, VALUE value);
private:
static VALUE doTryCatch(int argc, VALUE argv[], VALUE self);
static VALUE setupAndCall(int* state, VALUE code);
static VALUE doCall(VALUE code);
static VALUE Class;
v8::TryCatch* impl;
bool allocated;
};
class V8 {
public:
static void Init();
static VALUE IdleNotification(VALUE self);
static VALUE SetCaptureStackTraceForUncaughtExceptions(int argc, VALUE argv[], VALUE self);
};
class ClassBuilder {

75
ext/v8/stack.cc Normal file
View File

@ -0,0 +1,75 @@
#include "rr.h"
namespace rr {
void Stack::Init() {
ClassBuilder("StackTrace").
defineSingletonMethod("kLineNumber", &Trace::kLineNumber).
defineSingletonMethod("kColumnOffset", &Trace::kColumnOffset).
defineSingletonMethod("kScriptName", &Trace::kScriptName).
defineSingletonMethod("kFunctionName", &Trace::kFunctionName).
defineSingletonMethod("kIsEval", &Trace::kIsEval).
defineSingletonMethod("kIsConstructor", &Trace::kIsConstructor).
defineSingletonMethod("kScriptNameOrSourceURL", &Trace::kScriptNameOrSourceURL).
defineSingletonMethod("kOverview", &Trace::kOverview).
defineSingletonMethod("kDetailed", &Trace::kDetailed).
defineSingletonMethod("CurrentStackTrace", &Trace::CurrentStackTrace).
defineMethod("GetFrame", &Trace::GetFrame).
defineMethod("GetFrameCount", &Trace::GetFrameCount).
defineMethod("AsArray", &Trace::AsArray).
store(&Trace::Class);
ClassBuilder("StackFrame").
defineMethod("GetLineNumber", &Frame::GetLineNumber).
defineMethod("GetColumn", &Frame::GetColumn).
defineMethod("GetScriptName", &Frame::GetScriptName).
defineMethod("GetScriptNameOrSourceURL", &Frame::GetScriptNameOrSourceURL).
defineMethod("IsEval", &Frame::IsEval).
defineMethod("IsConstructor", &Frame::IsConstructor).
store(&Frame::Class);
}
VALUE Stack::Trace::GetFrame(VALUE self, VALUE index) {
return Frame(Trace(self)->GetFrame(NUM2UINT(index)));
}
VALUE Stack::Trace::GetFrameCount(VALUE self) {
return INT2FIX(Trace(self)->GetFrameCount());
}
VALUE Stack::Trace::AsArray(VALUE self) {
return Array(Trace(self)->AsArray());
}
VALUE Stack::Trace::CurrentStackTrace(int argc, VALUE argv[], VALUE self) {
VALUE frame_limit; VALUE options;
rb_scan_args(argc, argv, "11", &frame_limit, &options);
return Trace(v8::StackTrace::CurrentStackTrace(NUM2INT(frame_limit), StackTraceOptions(options)));
}
VALUE Stack::Frame::GetLineNumber(VALUE self) {
return INT2FIX(Frame(self)->GetLineNumber());
}
VALUE Stack::Frame::GetColumn(VALUE self) {
return INT2FIX(Frame(self)->GetColumn());
}
VALUE Stack::Frame::GetScriptName(VALUE self) {
return String(Frame(self)->GetScriptName());
}
VALUE Stack::Frame::GetScriptNameOrSourceURL(VALUE self) {
return String(Frame(self)->GetScriptNameOrSourceURL());
}
VALUE Stack::Frame::GetFunctionName(VALUE self) {
return String(Frame(self)->GetFunctionName());
}
VALUE Stack::Frame::IsEval(VALUE self) {
return Bool(Frame(self)->IsEval());
}
VALUE Stack::Frame::IsConstructor(VALUE self) {
return Bool(Frame(self)->IsConstructor());
}
}

86
ext/v8/trycatch.cc Normal file
View File

@ -0,0 +1,86 @@
#include "rr.h"
namespace rr {
VALUE TryCatch::Class;
void TryCatch::Init() {
ClassBuilder("TryCatch").
defineMethod("HasCaught", &HasCaught).
defineMethod("CanContinue", &CanContinue).
defineMethod("ReThrow", &ReThrow).
defineMethod("Exception", &Exception).
defineMethod("StackTrace", &StackTrace).
defineMethod("Message", &Message).
defineMethod("Reset", &Reset).
defineMethod("SetVerbose", &SetVerbose).
defineMethod("SetCaptureMessage", &SetCaptureMessage).
store(&Class);
VALUE v8 = rb_define_module("V8");
VALUE c = rb_define_module_under(v8, "C");
rb_define_singleton_method(c, "TryCatch", (VALUE (*)(...))&doTryCatch, -1);
}
TryCatch::TryCatch() : impl(new v8::TryCatch()), allocated(true) {}
TryCatch::TryCatch(VALUE value) : allocated(false) {
Data_Get_Struct(value, class v8::TryCatch, impl);
}
TryCatch::~TryCatch() {
if (this->allocated) {
delete this->impl;
}
}
TryCatch::operator VALUE() {
return Data_Wrap_Struct(Class, 0, 0, impl);
}
VALUE TryCatch::HasCaught(VALUE self) {
return Bool(TryCatch(self)->HasCaught());
}
VALUE TryCatch::CanContinue(VALUE self) {
return Bool(TryCatch(self)->CanContinue());
}
VALUE TryCatch::ReThrow(VALUE self) {
return Value(TryCatch(self)->ReThrow());
}
VALUE TryCatch::Exception(VALUE self) {
return Value(TryCatch(self)->Exception());
}
VALUE TryCatch::StackTrace(VALUE self) {
return Value(TryCatch(self)->StackTrace());
}
VALUE TryCatch::Message(VALUE self) {
return rr::Message(TryCatch(self)->Message());
}
VALUE TryCatch::Reset(VALUE self) {
Void(TryCatch(self)->Reset());
}
VALUE TryCatch::SetVerbose(VALUE self, VALUE value) {
Void(TryCatch(self)->SetVerbose(Bool(value)));
}
VALUE TryCatch::SetCaptureMessage(VALUE self, VALUE value) {
Void(TryCatch(self)->SetCaptureMessage(Bool(value)));
}
VALUE TryCatch::doTryCatch(int argc, VALUE argv[], VALUE self) {
if (!rb_block_given_p()) {
return Qnil;
}
int state = 0;
VALUE code;
rb_scan_args(argc,argv,"00&", &code);
VALUE result = setupAndCall(&state, code);
if (state != 0) {
rb_jump_tag(state);
}
return result;
}
VALUE TryCatch::setupAndCall(int* state, VALUE code) {
return rb_protect(&doCall, code, state);
}
VALUE TryCatch::doCall(VALUE code) {
TryCatch trycatch;
return rb_funcall(code, rb_intern("call"), 1, (VALUE)trycatch);
}
}

View File

@ -4,11 +4,19 @@ namespace rr {
void V8::Init() {
ClassBuilder("V8").
defineSingletonMethod("IdleNotification", &IdleNotification);
defineSingletonMethod("IdleNotification", &IdleNotification).
defineSingletonMethod("SetCaptureStackTraceForUncaughtExceptions", &SetCaptureStackTraceForUncaughtExceptions);
}
VALUE V8::IdleNotification(VALUE self) {
return Bool(v8::V8::IdleNotification());
}
VALUE V8::SetCaptureStackTraceForUncaughtExceptions(int argc, VALUE argv[], VALUE self) {
VALUE should_capture; VALUE frame_limit; VALUE options;
rb_scan_args(argc, argv, "12", &should_capture, &frame_limit, &options);
int limit = RTEST(frame_limit) ? NUM2INT(frame_limit) : 10;
Void(v8::V8::SetCaptureStackTraceForUncaughtExceptions(Bool(should_capture), limit, Stack::Trace::StackTraceOptions(options)));
}
}

View File

@ -4,11 +4,148 @@ namespace rr {
void Value::Init() {
ClassBuilder("Value").
defineMethod("IsUndefined", &IsUndefined).
defineMethod("IsNull", &IsNull).
defineMethod("IsTrue", &IsTrue).
defineMethod("IsFalse", &IsFalse).
defineMethod("IsString", &IsString).
defineMethod("IsFunction", &IsFunction).
defineMethod("IsArray", &IsArray).
defineMethod("IsObject", &IsObject).
defineMethod("IsBoolean", &IsBoolean).
defineMethod("IsNumber", &IsNumber).
defineMethod("IsExternal", &IsExternal).
defineMethod("IsInt32", &IsInt32).
defineMethod("IsUint32", &IsUint32).
defineMethod("IsDate", &IsDate).
defineMethod("IsBooleanObject", &IsBooleanObject).
defineMethod("IsNumberObject", &IsNumberObject).
defineMethod("IsStringObject", &IsStringObject).
defineMethod("IsNativeError", &IsNativeError).
defineMethod("IsRegExp", &IsRegExp).
defineMethod("ToString", &ToString).
defineMethod("ToDetailString", &ToDetailString).
defineMethod("ToObject", &ToObject).
defineMethod("BooleanValue", &BooleanValue).
defineMethod("NumberValue", &NumberValue).
defineMethod("IntegerValue", &IntegerValue).
defineMethod("Uint32Value", &Uint32Value).
defineMethod("IntegerValue", &IntegerValue).
defineMethod("Equals", &Equals).
defineMethod("StrictEquals", &StrictEquals)
.store(&Class);
}
VALUE Value::IsUndefined(VALUE self) {
return Bool(Value(self)->IsUndefined());
}
VALUE Value::IsNull(VALUE self) {
return Bool(Value(self)->IsNull());
}
VALUE Value::IsTrue(VALUE self) {
return Bool(Value(self)->IsTrue());
}
VALUE Value::IsFalse(VALUE self) {
return Bool(Value(self)->IsFalse());
}
VALUE Value::IsString(VALUE self) {
return Bool(Value(self)->IsString());
}
VALUE Value::IsFunction(VALUE self) {
return Bool(Value(self)->IsFunction());
}
VALUE Value::IsArray(VALUE self) {
return Bool(Value(self)->IsArray());
}
VALUE Value::IsObject(VALUE self) {
return Bool(Value(self)->IsObject());
}
VALUE Value::IsBoolean(VALUE self) {
return Bool(Value(self)->IsBoolean());
}
VALUE Value::IsNumber(VALUE self) {
return Bool(Value(self)->IsNumber());
}
VALUE Value::IsExternal(VALUE self) {
return Bool(Value(self)->IsExternal());
}
VALUE Value::IsInt32(VALUE self) {
return Bool(Value(self)->IsInt32());
}
VALUE Value::IsUint32(VALUE self) {
return Bool(Value(self)->IsUint32());
}
VALUE Value::IsDate(VALUE self) {
return Bool(Value(self)->IsDate());
}
VALUE Value::IsBooleanObject(VALUE self) {
return Bool(Value(self)->IsBooleanObject());
}
VALUE Value::IsNumberObject(VALUE self) {
return Bool(Value(self)->IsNumberObject());
}
VALUE Value::IsStringObject(VALUE self) {
return Bool(Value(self)->IsStringObject());
}
VALUE Value::IsNativeError(VALUE self) {
return Bool(Value(self)->IsNativeError());
}
VALUE Value::IsRegExp(VALUE self) {
return Bool(Value(self)->IsRegExp());
}
// VALUE Value::ToBoolean(VALUE self) {
// return Boolean(Value(self)->ToBoolean());
// }
// VALUE Value::ToNumber(VALUE self) {
// return Number(Value(self)->ToNumber());
// }
VALUE Value::ToString(VALUE self) {
return String(Value(self)->ToString());
}
VALUE Value::ToDetailString(VALUE self) {
return String(Value(self)->ToDetailString());
}
VALUE Value::ToObject(VALUE self) {
return Object(Value(self)->ToObject());
}
// VALUE Value::ToInteger(VALUE self) {
// return Integer(Value(self)->ToInteger());
// }
// VALUE Value::ToUint32(VALUE self) {
// return Uint32(Value(self)->ToUint32());
// }
// VALUE Value::ToInt32(VALUE self) {
// return Int32(Value(self)->ToInt32());
// }
// VALUE Value::ToArrayIndex(VALUE self) {
// return Uint32(Value(self)->ToArrayIndex());
// }
VALUE Value::BooleanValue(VALUE self) {
return Bool(Value(self)->BooleanValue());
}
VALUE Value::NumberValue(VALUE self) {
return rb_float_new(Value(self)->NumberValue());
}
VALUE Value::IntegerValue(VALUE self) {
return INT2NUM(Value(self)->IntegerValue());
}
VALUE Value::Uint32Value(VALUE self) {
return UINT2NUM(Value(self)->Uint32Value());
}
VALUE Value::Int32Value(VALUE self) {
return INT2FIX(Value(self)->Int32Value());
}
VALUE Value::Equals(VALUE self, VALUE other) {
return Bool(Value(self)->Equals(Value(other)));
}

59
spec/c/trycatch_spec.rb Normal file
View File

@ -0,0 +1,59 @@
require 'spec_helper'
describe V8::C::External do
before do
@cxt = V8::C::Context::New()
@cxt.Enter()
end
after do
@cxt.Exit()
end
it "can catch javascript exceptions" do
V8::C::V8::SetCaptureStackTraceForUncaughtExceptions(true, 99, V8::C::StackTrace::kDetailed)
V8::C::TryCatch() do |trycatch|
V8::C::HandleScope() do
source = V8::C::String::New(<<-JS)
function one() {
two()
}
function two() {
three()
}
function three() {
boom()
}
function boom() {
throw new Error('boom!')
}
eval('one()')
JS
filename = V8::C::String::New("<eval>")
script = V8::C::Script::New(source, filename)
result = script.Run()
trycatch.HasCaught().should be_true
trycatch.CanContinue().should be_true
exception = trycatch.Exception()
exception.should_not be_nil
exception.IsNativeError().should be_true
trycatch.StackTrace().Utf8Value().should match /boom.*three.*two.*one/m
message = trycatch.Message();
message.should_not be_nil
message.Get().Utf8Value().should eql "Uncaught Error: boom!"
message.GetSourceLine().Utf8Value().should eql " throw new Error('boom!')"
message.GetScriptResourceName().Utf8Value().should eql "<eval>"
message.GetLineNumber().should eql 11
stack = message.GetStackTrace()
stack.should_not be_nil
stack.GetFrameCount().should eql 6
frame = stack.GetFrame(0)
frame.GetLineNumber().should eql 11
frame.GetColumn().should eql 17
frame.GetScriptName().Utf8Value().should eql "<eval>"
frame.GetScriptNameOrSourceURL().Utf8Value().should eql "<eval>"
frame.IsEval().should be_false
stack.GetFrame(4).IsEval().should be_true
frame.IsConstructor().should be_false
end
end
end
end