mirror of
https://github.com/rubyjs/therubyracer
synced 2023-03-27 23:21:42 -04:00
Merge pull request #379 from cowboyd/4.5/try-catch-throw
add support for TryCatch along with StackTrace
This commit is contained in:
commit
5a1632adba
18 changed files with 687 additions and 10 deletions
|
@ -41,6 +41,14 @@ namespace rr {
|
|||
return *this;
|
||||
}
|
||||
|
||||
ClassBuilder& ClassBuilder::defineConstMethod(const char* name, VALUE value) {
|
||||
VALUE symbol = rb_funcall(rb_str_cat2(rb_str_new2("@"), name), rb_intern("to_sym"), 0);
|
||||
VALUE singleton_class = rb_funcall(this->value, rb_intern("singleton_class"), 0);
|
||||
rb_ivar_set(this->value, SYM2ID(symbol), value);
|
||||
rb_funcall(singleton_class, rb_intern("attr_reader"), 1, ID2SYM(rb_intern(name)));
|
||||
return *this;
|
||||
}
|
||||
|
||||
ClassBuilder& ClassBuilder::defineMethod(const char* name, VALUE (*impl)(int, VALUE*, VALUE)) {
|
||||
rb_define_method(this->value, name, (VALUE (*)(...))impl, -1);
|
||||
return *this;
|
||||
|
|
|
@ -16,6 +16,7 @@ namespace rr {
|
|||
ClassBuilder(const char* name, const char* supername);
|
||||
|
||||
ClassBuilder& defineConst(const char* name, VALUE value);
|
||||
ClassBuilder& defineConstMethod(const char* name, VALUE value);
|
||||
|
||||
ClassBuilder& defineMethod(const char* name, VALUE (*impl)(int, VALUE*, VALUE));
|
||||
ClassBuilder& defineMethod(const char* name, VALUE (*impl)(VALUE));
|
||||
|
|
|
@ -23,6 +23,10 @@ namespace rr {
|
|||
* Internally, `Equiv`s are always stored as a Ruby `VALUE`, and so
|
||||
* part of the job of the subclass is to have an appropriate
|
||||
* constructor that converts the C/C++ type to a `VALUE`
|
||||
*
|
||||
* It is handy to have a class to do the conversions instead of
|
||||
* preprocessor macros like `NUM2INT`, et al. because classes can be
|
||||
* easily used in C++ templates.
|
||||
*/
|
||||
class Equiv {
|
||||
public:
|
||||
|
|
50
ext/v8/exception.h
Normal file
50
ext/v8/exception.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
// -*- mode: c++ -*-
|
||||
#ifndef RR_EXCEPTION_H
|
||||
#define RR_EXCEPTION_H
|
||||
#include "rr.h"
|
||||
|
||||
namespace rr {
|
||||
class Exception {
|
||||
public:
|
||||
static inline void Init() {
|
||||
ClassBuilder("Exception").
|
||||
defineSingletonMethod("RangeError", &RangeError).
|
||||
defineSingletonMethod("ReferenceError", &ReferenceError).
|
||||
defineSingletonMethod("SyntaxError", &SyntaxError).
|
||||
defineSingletonMethod("TypeError", &TypeError).
|
||||
defineSingletonMethod("Error", &Error);
|
||||
}
|
||||
|
||||
static VALUE RangeError(VALUE self, VALUE rb_message) {
|
||||
String message(rb_message);
|
||||
Locker lock(message);
|
||||
return Value(message, v8::Exception::RangeError(message));
|
||||
}
|
||||
|
||||
static VALUE ReferenceError(VALUE self, VALUE rb_message) {
|
||||
String message(rb_message);
|
||||
Locker lock(message);
|
||||
return Value(message, v8::Exception::ReferenceError(message));
|
||||
}
|
||||
|
||||
static VALUE SyntaxError(VALUE self, VALUE rb_message) {
|
||||
String message(rb_message);
|
||||
Locker lock(message);
|
||||
return Value(message, v8::Exception::SyntaxError(message));
|
||||
}
|
||||
|
||||
static VALUE TypeError(VALUE self, VALUE rb_message) {
|
||||
String message(rb_message);
|
||||
Locker lock(message);
|
||||
return Value(message, v8::Exception::TypeError(message));
|
||||
}
|
||||
|
||||
static VALUE Error(VALUE self, VALUE rb_message) {
|
||||
String message(rb_message);
|
||||
Locker lock(message);
|
||||
return Value(message, v8::Exception::Error(message));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* RR_EXCEPTION_H */
|
|
@ -43,16 +43,15 @@ extern "C" {
|
|||
ObjectTemplate::Init();
|
||||
FunctionTemplate::Init();
|
||||
Signature::Init();
|
||||
StackFrame::Init();
|
||||
StackTrace::Init();
|
||||
Message::Init();
|
||||
Exception::Init();
|
||||
TryCatch::Init();
|
||||
|
||||
// Invocation::Init();
|
||||
// Signature::Init();
|
||||
// Date::Init();
|
||||
// Constants::Init();
|
||||
// Template::Init();
|
||||
// Stack::Init();
|
||||
// Message::Init();
|
||||
// TryCatch::Init();
|
||||
// Exception::Init();
|
||||
// ResourceConstraints::Init();
|
||||
// HeapStatistics::Init();
|
||||
}
|
||||
|
|
54
ext/v8/int.h
Normal file
54
ext/v8/int.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
// -*- mode: c++ -*-
|
||||
#ifndef RR_INT_H
|
||||
#define RR_INT_H
|
||||
|
||||
|
||||
namespace rr {
|
||||
/**
|
||||
* Converts between Ruby `Number` and the C/C++ `int`.
|
||||
*
|
||||
* This allows you to easily pass in `int` values whenever a
|
||||
* Ruby VALUE is expected (such as a method call) E.g.
|
||||
*
|
||||
* int myInt = 5;
|
||||
* rb_funcall(Uint32_t(myInt), rb_intern("to_s")); //=> <String "5">
|
||||
*
|
||||
* It also converts a Ruby `VALUE` into its corresponding
|
||||
* `int`:
|
||||
*
|
||||
* intt myInt = Int(rb_eval_string("5")); //=> 5
|
||||
*
|
||||
* Like all `Equiv`s, it stores itself internally as a Ruby `VALUE`
|
||||
*/
|
||||
class Int : public Equiv {
|
||||
public:
|
||||
/**
|
||||
* Use to convert methods that return Maybe<int> to a Ruby
|
||||
* VALUE
|
||||
*
|
||||
* return Int::Maybe(stack_trace->GetStartColumn(context));
|
||||
*/
|
||||
typedef Equiv::Maybe<int, Int> Maybe;
|
||||
|
||||
/**
|
||||
* Construct an Int from a Ruby `VALUE`
|
||||
*/
|
||||
Int(VALUE val) : Equiv(val) {}
|
||||
|
||||
/**
|
||||
* Construct an Int from a `int` by converting it into its
|
||||
* corresponding `VALUE`.
|
||||
*/
|
||||
Int(int i) : Equiv(INT2FIX(i)) {}
|
||||
|
||||
/**
|
||||
* Coerce the Ruby `VALUE` into an `int`.
|
||||
*/
|
||||
inline operator int() {
|
||||
return RTEST(value) ? NUM2UINT(value) : 0;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,6 +1,5 @@
|
|||
// -*- mode: c++ -*-
|
||||
#include "rr.h"
|
||||
#include "isolate.h"
|
||||
|
||||
namespace rr {
|
||||
|
||||
|
@ -10,7 +9,8 @@ namespace rr {
|
|||
rb_eval_string("require 'v8/retained_objects'");
|
||||
ClassBuilder("Isolate").
|
||||
defineSingletonMethod("New", &New).
|
||||
|
||||
defineMethod("ThrowException", &ThrowException).
|
||||
defineMethod("SetCaptureStackTraceForUncaughtExceptions", &SetCaptureStackTraceForUncaughtExceptions).
|
||||
defineMethod("IdleNotificationDeadline", &IdleNotificationDeadline).
|
||||
|
||||
store(&Class);
|
||||
|
@ -33,6 +33,23 @@ namespace rr {
|
|||
return Isolate(isolate);
|
||||
}
|
||||
|
||||
VALUE Isolate::ThrowException(VALUE self, VALUE error) {
|
||||
Isolate isolate(self);
|
||||
Locker lock(isolate);
|
||||
return Value(isolate, isolate->ThrowException(Value(error)));
|
||||
}
|
||||
|
||||
VALUE Isolate::SetCaptureStackTraceForUncaughtExceptions(VALUE self, VALUE capture, VALUE stack_limit, VALUE options) {
|
||||
Isolate isolate(self);
|
||||
Locker lock(isolate);
|
||||
|
||||
isolate->SetCaptureStackTraceForUncaughtExceptions(
|
||||
Bool(capture),
|
||||
RTEST(stack_limit) ? NUM2INT(stack_limit) : 10,
|
||||
Enum<v8::StackTrace::StackTraceOptions>(options, v8::StackTrace::kOverview));
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
|
||||
VALUE Isolate::IdleNotificationDeadline(VALUE self, VALUE deadline_in_seconds) {
|
||||
Isolate isolate(self);
|
||||
|
|
|
@ -30,6 +30,8 @@ namespace rr {
|
|||
static void Init();
|
||||
|
||||
static VALUE New(VALUE self);
|
||||
static VALUE SetCaptureStackTraceForUncaughtExceptions(VALUE self, VALUE capture, VALUE stack_limit, VALUE options);
|
||||
static VALUE ThrowException(VALUE self, VALUE error);
|
||||
|
||||
inline Isolate(IsolateData* data_) : data(data_) {}
|
||||
inline Isolate(v8::Isolate* isolate) :
|
||||
|
|
95
ext/v8/message.h
Normal file
95
ext/v8/message.h
Normal file
|
@ -0,0 +1,95 @@
|
|||
// -*- mode: c++ -*-
|
||||
|
||||
#ifndef RR_MESSAGE_H
|
||||
#define RR_MESSAGE_H
|
||||
#include "rr.h"
|
||||
|
||||
namespace rr {
|
||||
class Message : public Ref<v8::Message> {
|
||||
public:
|
||||
Message(VALUE self) : Ref<v8::Message>(self) {}
|
||||
Message(v8::Isolate* isolate, v8::Local<v8::Message> msg) :
|
||||
Ref<v8::Message>(isolate, msg) {}
|
||||
|
||||
static VALUE Get(VALUE self) {
|
||||
Message message(self);
|
||||
Locker lock(message);
|
||||
return String(message, message->Get());
|
||||
}
|
||||
static VALUE GetSourceLine(VALUE self, VALUE cxt) {
|
||||
Message message(self);
|
||||
Locker lock(message);
|
||||
return String::Maybe(message, message->GetSourceLine(Context(cxt)));
|
||||
}
|
||||
static VALUE GetScriptOrigin(VALUE self) {
|
||||
Message message(self);
|
||||
Locker lock(message);
|
||||
return ScriptOrigin(message, message->GetScriptOrigin());
|
||||
}
|
||||
static VALUE GetScriptResourceName(VALUE self) {
|
||||
Message message(self);
|
||||
Locker lock(message);
|
||||
return Value(message, message->GetScriptResourceName());
|
||||
}
|
||||
static VALUE GetStackTrace(VALUE self) {
|
||||
Message message(self);
|
||||
Locker lock(message);
|
||||
return StackTrace(message, message->GetStackTrace());
|
||||
}
|
||||
static VALUE GetLineNumber(VALUE self, VALUE cxt) {
|
||||
Message message(self);
|
||||
Locker lock(message);
|
||||
return Int::Maybe(message->GetLineNumber(Context(cxt)));
|
||||
}
|
||||
static VALUE GetStartPosition(VALUE self) {
|
||||
Message message(self);
|
||||
Locker lock(message);
|
||||
return Int(message->GetStartPosition());
|
||||
}
|
||||
static VALUE GetEndPosition(VALUE self) {
|
||||
Message message(self);
|
||||
Locker lock(message);
|
||||
return Int(message->GetEndPosition());
|
||||
}
|
||||
static VALUE GetStartColumn(VALUE self, VALUE cxt) {
|
||||
Message message(self);
|
||||
Locker lock(message);
|
||||
return Int::Maybe(message->GetStartColumn(Context(cxt)));
|
||||
}
|
||||
static VALUE GetEndColumn(VALUE self, VALUE cxt) {
|
||||
Message message(self);
|
||||
Locker lock(message);
|
||||
return Int::Maybe(message->GetEndColumn(Context(cxt)));
|
||||
}
|
||||
static VALUE IsOpaque(VALUE self) {
|
||||
Message message(self);
|
||||
Locker lock(message);
|
||||
return Bool(message->IsOpaque());
|
||||
}
|
||||
static VALUE IsSharedCrossOrigin(VALUE self) {
|
||||
Message message(self);
|
||||
Locker lock(message);
|
||||
return Bool(message->IsSharedCrossOrigin());
|
||||
}
|
||||
|
||||
static inline void Init() {
|
||||
ClassBuilder("Message").
|
||||
defineMethod("Get", &Get).
|
||||
defineMethod("GetSourceLine", &GetSourceLine).
|
||||
defineMethod("GetScriptOrigin", &GetScriptOrigin).
|
||||
defineMethod("GetScriptResourceName", &GetScriptResourceName).
|
||||
defineMethod("GetStackTrace", &GetStackTrace).
|
||||
defineMethod("GetLineNumber", &GetLineNumber).
|
||||
defineMethod("GetStartPosition", &GetStartPosition).
|
||||
defineMethod("GetEndPosition", &GetEndPosition).
|
||||
defineMethod("GetStartColumn", &GetStartColumn).
|
||||
defineMethod("GetEndColumn", &GetEndColumn).
|
||||
defineMethod("IsSharedCrossOrigin", &IsSharedCrossOrigin).
|
||||
defineMethod("IsOpaque", &IsOpaque).
|
||||
store(&Class);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif /* RR_MESSAGE_H */
|
|
@ -23,6 +23,7 @@ inline VALUE not_implemented(const char* message) {
|
|||
|
||||
#include "equiv.h"
|
||||
#include "bool.h"
|
||||
#include "int.h"
|
||||
#include "uint32_t.h"
|
||||
#include "pointer.h"
|
||||
#include "wrapper.h"
|
||||
|
@ -68,4 +69,10 @@ inline VALUE not_implemented(const char* message) {
|
|||
#include "function-template.h"
|
||||
#include "object-template.h"
|
||||
|
||||
#include "stack-frame.h"
|
||||
#include "stack-trace.h"
|
||||
#include "message.h"
|
||||
#include "exception.h"
|
||||
#include "try-catch.h"
|
||||
|
||||
#endif
|
||||
|
|
70
ext/v8/stack-frame.h
Normal file
70
ext/v8/stack-frame.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
// -*- mode: c++ -*-
|
||||
|
||||
#ifndef RR_STACK_FRAME_H
|
||||
#define RR_STACK_FRAME_H
|
||||
#include "rr.h"
|
||||
|
||||
namespace rr {
|
||||
class StackFrame : public Ref<v8::StackFrame> {
|
||||
public:
|
||||
StackFrame(VALUE self) : Ref<v8::StackFrame>(self) {}
|
||||
StackFrame(v8::Isolate* isolate, v8::Local<v8::StackFrame> frame) :
|
||||
Ref<v8::StackFrame>(isolate, frame) {}
|
||||
|
||||
static VALUE GetLineNumber(VALUE self) {
|
||||
StackFrame frame(self);
|
||||
Locker lock(frame);
|
||||
return INT2FIX(frame->GetLineNumber());
|
||||
}
|
||||
static VALUE GetColumn(VALUE self) {
|
||||
StackFrame frame(self);
|
||||
Locker lock(frame);
|
||||
return INT2FIX(frame->GetColumn());
|
||||
}
|
||||
static VALUE GetScriptId(VALUE self) {
|
||||
StackFrame frame(self);
|
||||
Locker lock(frame);
|
||||
return INT2FIX(frame->GetScriptId());
|
||||
}
|
||||
static VALUE GetScriptName(VALUE self) {
|
||||
StackFrame frame(self);
|
||||
Locker lock(frame);
|
||||
return String(frame, frame->GetScriptName());
|
||||
}
|
||||
static VALUE GetScriptNameOrSourceURL(VALUE self) {
|
||||
StackFrame frame(self);
|
||||
Locker lock(frame);
|
||||
return String(frame, frame->GetScriptNameOrSourceURL());
|
||||
}
|
||||
static VALUE GetFunctionName(VALUE self) {
|
||||
StackFrame frame(self);
|
||||
Locker lock(frame);
|
||||
return String(frame, frame->GetFunctionName());
|
||||
}
|
||||
static VALUE IsEval(VALUE self) {
|
||||
StackFrame frame(self);
|
||||
Locker lock(frame);
|
||||
return Bool(frame->IsEval());
|
||||
}
|
||||
static VALUE IsConstructor(VALUE self) {
|
||||
StackFrame frame(self);
|
||||
Locker lock(frame);
|
||||
return Bool(frame->IsConstructor());
|
||||
}
|
||||
|
||||
static inline void Init() {
|
||||
ClassBuilder("StackFrame").
|
||||
defineMethod("GetLineNumber", &GetLineNumber).
|
||||
defineMethod("GetColumn", &GetColumn).
|
||||
defineMethod("GetScriptId", &GetScriptId).
|
||||
defineMethod("GetScriptName", &GetScriptName).
|
||||
defineMethod("GetScriptNameOrSourceURL", &GetScriptNameOrSourceURL).
|
||||
defineMethod("GetFunctionName", &GetFunctionName).
|
||||
defineMethod("IsEval", &IsEval).
|
||||
defineMethod("IsConstructor", &IsConstructor).
|
||||
store(&Class);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* RR_STACK_FRAME_H */
|
70
ext/v8/stack-trace.h
Normal file
70
ext/v8/stack-trace.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
// -*- mode: c++ -*-
|
||||
#ifndef RR_STACK_TRACE_H
|
||||
#define RR_STACK_TRACE_H
|
||||
#include "rr.h"
|
||||
|
||||
namespace rr {
|
||||
class StackTrace : public Ref<v8::StackTrace> {
|
||||
public:
|
||||
StackTrace(VALUE self) : Ref<v8::StackTrace>(self) {}
|
||||
StackTrace(v8::Isolate* isolate, v8::Local<v8::StackTrace> trace) :
|
||||
Ref<v8::StackTrace>(isolate, trace) {}
|
||||
|
||||
static VALUE GetFrame(VALUE self, VALUE offset) {
|
||||
StackTrace stack(self);
|
||||
Locker lock(stack);
|
||||
|
||||
return StackFrame(stack, stack->GetFrame(NUM2INT(offset)));
|
||||
}
|
||||
|
||||
static VALUE GetFrameCount(VALUE self) {
|
||||
StackTrace stack(self);
|
||||
Locker lock(stack);
|
||||
|
||||
return INT2FIX(stack->GetFrameCount());
|
||||
}
|
||||
|
||||
static VALUE AsArray(VALUE self) {
|
||||
StackTrace stack(self);
|
||||
Locker lock(stack);
|
||||
|
||||
return Array(stack, stack->AsArray());
|
||||
}
|
||||
|
||||
static VALUE CurrentStackTrace(VALUE self, VALUE rb_isolate, VALUE frame_limit, VALUE options) {
|
||||
Isolate isolate(rb_isolate);
|
||||
Locker lock(isolate);
|
||||
|
||||
return StackTrace(
|
||||
isolate,
|
||||
v8::StackTrace::CurrentStackTrace(
|
||||
isolate,
|
||||
NUM2UINT(frame_limit),
|
||||
Enum<v8::StackTrace::StackTraceOptions>(options, v8::StackTrace::kOverview)));
|
||||
}
|
||||
|
||||
inline static void Init() {
|
||||
ClassBuilder("StackTrace").
|
||||
defineMethod("GetFrame", &GetFrame).
|
||||
defineMethod("GetFrameCount", &GetFrameCount).
|
||||
defineMethod("AsArray", &AsArray).
|
||||
defineSingletonMethod("CurrentStackTrace", &CurrentStackTrace).
|
||||
|
||||
defineConstMethod("kLineNumber", INT2FIX(v8::StackTrace::kLineNumber)).
|
||||
defineConstMethod("kColumnOffset", INT2FIX(v8::StackTrace::kColumnOffset)).
|
||||
defineConstMethod("kScriptName", INT2FIX(v8::StackTrace::kScriptName)).
|
||||
defineConstMethod("kFunctionName", INT2FIX(v8::StackTrace::kFunctionName)).
|
||||
defineConstMethod("kIsEval", INT2FIX(v8::StackTrace::kIsEval)).
|
||||
defineConstMethod("kIsConstructor", INT2FIX(v8::StackTrace::kIsConstructor)).
|
||||
defineConstMethod("kScriptNameOrSourceURL", INT2FIX(v8::StackTrace::kScriptNameOrSourceURL)).
|
||||
defineConstMethod("kScriptId", INT2FIX(v8::StackTrace::kScriptId)).
|
||||
defineConstMethod("kExposeFramesAcrossSecurityOrigins", INT2FIX(v8::StackTrace::kExposeFramesAcrossSecurityOrigins)).
|
||||
defineConstMethod("kOverview", INT2FIX(v8::StackTrace::kOverview)).
|
||||
defineConstMethod("kDetailed", INT2FIX(v8::StackTrace::kDetailed)).
|
||||
store(&Class);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* RR_STACK_TRACE_H */
|
5
ext/v8/try-catch.cc
Normal file
5
ext/v8/try-catch.cc
Normal file
|
@ -0,0 +1,5 @@
|
|||
#include "rr.h"
|
||||
|
||||
namespace rr {
|
||||
VALUE TryCatch::Class;
|
||||
}
|
192
ext/v8/try-catch.h
Normal file
192
ext/v8/try-catch.h
Normal file
|
@ -0,0 +1,192 @@
|
|||
// -*- mode: c++ -*-
|
||||
|
||||
#ifndef RR_TRY_CATCH_H
|
||||
#define RR_TRY_CATCH_H
|
||||
#include "rr.h"
|
||||
|
||||
namespace rr {
|
||||
/**
|
||||
* V8::C::TryCatch is both a class and a method.
|
||||
*
|
||||
* Use the method to evaluate a block of Ruby code from within a
|
||||
* v8::TryCatch. The block will receive an instance of the
|
||||
* `V8::C::TryCatch` class as its argument E.g.
|
||||
*
|
||||
* V8::C::TryCatch(isolate) do |trycatch|
|
||||
* trycatch.class #=> V8::C::TryCatch
|
||||
* cxt.eval('throw new Error()')
|
||||
* trycatch.HasCaught() #=> true
|
||||
* end
|
||||
*
|
||||
* Note that the `trycatch` value yielded to the block must never be
|
||||
* used outside the block since it references the `v8::TryCatch` C++
|
||||
* object which is allocated on the stack. See `doTryCatch` and
|
||||
* `call` for the implementation of the block parameters.
|
||||
*
|
||||
* Note: Ideally, we'd like to make the `Wrapper` class as a super
|
||||
* class to handle the wrapping and unwrapping of the TryCatch, but
|
||||
* that would require introducing pass-by-reference semantics to the
|
||||
* wrapper template (since v8::TryCatch disables the copy
|
||||
* constructor). Satisfying the compiler proved too challenging for
|
||||
* this poor implementer.
|
||||
*/
|
||||
class TryCatch {
|
||||
public:
|
||||
TryCatch(VALUE self) : container(Container::unwrap(self)) {}
|
||||
TryCatch(v8::Isolate* isolate, v8::TryCatch& trycatch) :
|
||||
container(new Container(isolate, trycatch)) {}
|
||||
|
||||
static VALUE HasCaught(VALUE self) {
|
||||
TryCatch tc(self);
|
||||
Locker lock(tc);
|
||||
return Bool(tc->HasCaught());
|
||||
}
|
||||
static VALUE CanContinue(VALUE self) {
|
||||
TryCatch tc(self);
|
||||
Locker lock(tc);
|
||||
return Bool(tc->CanContinue());
|
||||
}
|
||||
static VALUE HasTerminated(VALUE self) {
|
||||
TryCatch tc(self);
|
||||
Locker lock(tc);
|
||||
return Bool(tc->HasTerminated());
|
||||
}
|
||||
static VALUE ReThrow(VALUE self) {
|
||||
TryCatch tc(self);
|
||||
Locker lock(tc);
|
||||
return Value(tc, tc->ReThrow());
|
||||
}
|
||||
static VALUE Exception(VALUE self) {
|
||||
TryCatch tc(self);
|
||||
Locker lock(tc);
|
||||
return Value(tc, tc->Exception());
|
||||
}
|
||||
static VALUE StackTrace(VALUE self, VALUE context) {
|
||||
TryCatch tc(self);
|
||||
Locker lock(tc);
|
||||
return Value::Maybe(tc, tc->StackTrace(Context(context)));
|
||||
}
|
||||
static VALUE Message(VALUE self) {
|
||||
TryCatch tc(self);
|
||||
Locker lock(tc);
|
||||
return rr::Message(tc, tc->Message());
|
||||
}
|
||||
static VALUE Reset(VALUE self) {
|
||||
TryCatch tc(self);
|
||||
Locker lock(tc);
|
||||
tc->Reset();
|
||||
return Qnil;
|
||||
}
|
||||
static VALUE SetVerbose(VALUE self, VALUE value) {
|
||||
TryCatch tc(self);
|
||||
Locker lock(tc);
|
||||
tc->SetVerbose(Bool(value));
|
||||
return Qnil;
|
||||
}
|
||||
static VALUE SetCaptureMessage(VALUE self, VALUE value) {
|
||||
TryCatch tc(self);
|
||||
Locker lock(tc);
|
||||
tc->SetCaptureMessage(Bool(value));
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the V8::C::TryCatch(isolate) method. This yields the
|
||||
* trycatch to the passed ruby code.
|
||||
*/
|
||||
static VALUE doTryCatch(int argc, VALUE argv[], VALUE self) {
|
||||
if (!rb_block_given_p()) {
|
||||
return Qnil;
|
||||
}
|
||||
int state;
|
||||
VALUE isolate, code, result;
|
||||
rb_scan_args(argc, argv, "10&", &isolate, &code);
|
||||
|
||||
// allocate the trycatch within its own scope, so that its
|
||||
// destructor is called, even if we're forced to `rb_jmp_tag`
|
||||
// out of this function because of a ruby exception.
|
||||
// the actual `V8::C::TryCatch` object is stored on the code in
|
||||
// the `_v8_trycatch` variable, because rb_protect only accepts
|
||||
// a single argument.
|
||||
{
|
||||
v8::TryCatch trycatch;
|
||||
rb_iv_set(code, "_v8_trycatch", TryCatch(Isolate(isolate), trycatch));
|
||||
result = rb_protect(&call, code, &state);
|
||||
rb_iv_set(code, "_v8_trycatch", Qnil);
|
||||
}
|
||||
if (state != 0) {
|
||||
// ruby exception. bail!
|
||||
rb_jump_tag(state);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Actually invokes the passed ruby block. The instance of
|
||||
* `V8::C::TryCatch` is pulled off of the `_v8_trycatch` instance variable.
|
||||
*/
|
||||
static VALUE call(VALUE code) {
|
||||
return rb_funcall(code, rb_intern("call"), 1, rb_iv_get(code, "_v8_trycatch"));
|
||||
}
|
||||
|
||||
|
||||
// What follows is a copy of the `Wrapper` template, that uses
|
||||
// pass-by-reference semantics so that the actual `v8::TryCatch`
|
||||
// object that is allocated on the C++ stack is the same object
|
||||
// used everywhere. Ideally, we'd like to share the
|
||||
// implementation, but I couldn't figure out how.
|
||||
static VALUE Class;
|
||||
|
||||
struct Container {
|
||||
Container(v8::Isolate* isolate_, v8::TryCatch& tc) : isolate(isolate_), trycatch(tc) {}
|
||||
|
||||
inline VALUE wrap() {
|
||||
return Data_Wrap_Struct(Class, 0, &destroy, this);
|
||||
}
|
||||
|
||||
static inline Container* unwrap(VALUE object) {
|
||||
Container* container;
|
||||
Data_Get_Struct(object, struct Container, container);
|
||||
return container;
|
||||
}
|
||||
|
||||
static void destroy(Container* container) {
|
||||
delete container;
|
||||
}
|
||||
v8::Isolate* isolate;
|
||||
v8::TryCatch& trycatch;
|
||||
};
|
||||
|
||||
inline v8::TryCatch* operator ->() {
|
||||
return &container->trycatch;
|
||||
}
|
||||
|
||||
inline operator v8::Isolate*() {
|
||||
return container->isolate;
|
||||
}
|
||||
|
||||
inline operator VALUE() {
|
||||
return container->wrap();
|
||||
}
|
||||
static inline void Init() {
|
||||
ClassBuilder("TryCatch").
|
||||
defineMethod("HasCaught", &HasCaught).
|
||||
defineMethod("CanContinue", &CanContinue).
|
||||
defineMethod("HasTerminated", &HasTerminated).
|
||||
defineMethod("ReThrow", &ReThrow).
|
||||
defineMethod("Exception", &Exception).
|
||||
defineMethod("StackTrace", &StackTrace).
|
||||
defineMethod("Message", &Message).
|
||||
defineMethod("Reset", &Reset).
|
||||
defineMethod("SetVerbose", &SetVerbose).
|
||||
defineMethod("SetCaptureMessage", &SetCaptureMessage).
|
||||
|
||||
store(&Class);
|
||||
rb_define_singleton_method(rb_eval_string("V8::C"), "TryCatch", (VALUE (*)(...))&doTryCatch, -1);
|
||||
}
|
||||
|
||||
Container* container;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif /* RR_TRY_CATCH_H */
|
|
@ -21,7 +21,7 @@ namespace rr {
|
|||
// defineMethod("IsBooleanObject", &IsBooleanObject).
|
||||
// defineMethod("IsNumberObject", &IsNumberObject).
|
||||
// defineMethod("IsStringObject", &IsStringObject).
|
||||
// defineMethod("IsNativeError", &IsNativeError).
|
||||
defineMethod("IsNativeError", &IsNativeError).
|
||||
// defineMethod("IsRegExp", &IsRegExp).
|
||||
defineMethod("ToString", &ToString).
|
||||
// defineMethod("ToDetailString", &ToDetailString).
|
||||
|
@ -120,6 +120,13 @@ namespace rr {
|
|||
return Bool(value->IsUint32());
|
||||
}
|
||||
|
||||
VALUE Value::IsNativeError(VALUE self) {
|
||||
Value value(self);
|
||||
Locker lock(value);
|
||||
|
||||
return Bool(value->IsNativeError());
|
||||
}
|
||||
|
||||
VALUE Value::ToString(VALUE self) {
|
||||
Value value(self);
|
||||
Locker lock(value.getIsolate());
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace rr {
|
|||
// static VALUE IsBooleanObject(VALUE self);
|
||||
// static VALUE IsNumberObject(VALUE self);
|
||||
// static VALUE IsStringObject(VALUE self);
|
||||
// static VALUE IsNativeError(VALUE self);
|
||||
static VALUE IsNativeError(VALUE self);
|
||||
// static VALUE IsRegExp(VALUE self);
|
||||
static VALUE ToString(VALUE self);
|
||||
// static VALUE ToDetailString(VALUE self);
|
||||
|
|
18
spec/c/stack_trace_spec.rb
Normal file
18
spec/c/stack_trace_spec.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
require 'c_spec_helper'
|
||||
|
||||
describe V8::C::StackTrace do
|
||||
let(:const) { V8::C::StackTrace }
|
||||
it "defines the stack trace description constants" do
|
||||
expect(const::kLineNumber).to eql 1
|
||||
expect(const::kColumnOffset).to eql 3
|
||||
expect(const::kScriptName).to eql 4
|
||||
expect(const::kFunctionName).to eql 8
|
||||
expect(const::kIsEval).to eql 16
|
||||
expect(const::kIsConstructor).to eql 32
|
||||
expect(const::kScriptNameOrSourceURL).to eql 64
|
||||
expect(const::kScriptId).to eql 128
|
||||
expect(const::kExposeFramesAcrossSecurityOrigins).to eql 256
|
||||
expect(const::kOverview).to eql 15
|
||||
expect(const::kDetailed).to eql 127
|
||||
end
|
||||
end
|
78
spec/c/try_catch_spec.rb
Normal file
78
spec/c/try_catch_spec.rb
Normal file
|
@ -0,0 +1,78 @@
|
|||
require 'c_spec_helper'
|
||||
|
||||
describe V8::C::TryCatch do
|
||||
requires_v8_context
|
||||
|
||||
before do
|
||||
@isolate.SetCaptureStackTraceForUncaughtExceptions(true, 99, V8::C::StackTrace::kDetailed)
|
||||
end
|
||||
|
||||
it "can catch javascript exceptions" do
|
||||
V8::C::TryCatch(@isolate) do |trycatch|
|
||||
source = V8::C::String::NewFromUtf8(@isolate, <<-JS)
|
||||
function one() {
|
||||
two()
|
||||
}
|
||||
function two() {
|
||||
three()
|
||||
}
|
||||
function three() {
|
||||
boom()
|
||||
}
|
||||
function boom() {
|
||||
throw new Error('boom!')
|
||||
}
|
||||
eval('one()')
|
||||
JS
|
||||
|
||||
|
||||
filename = V8::C::String::NewFromUtf8(@isolate, "<eval>")
|
||||
origin = V8::C::ScriptOrigin.new(filename)
|
||||
script = V8::C::Script::Compile(@ctx, source, origin)
|
||||
expect(script).to be_successful
|
||||
result = script.FromJust().Run(@ctx)
|
||||
trycatch.HasCaught().should be_truthy
|
||||
trycatch.CanContinue().should be_truthy
|
||||
exception = trycatch.Exception()
|
||||
exception.should_not be_nil
|
||||
exception.IsNativeError().should be_truthy
|
||||
expect(trycatch.StackTrace(@ctx)).to be_successful
|
||||
trace = trycatch.StackTrace(@ctx).FromJust()
|
||||
trace.Utf8Value().should match /boom.*three.*two.*one/m
|
||||
message = trycatch.Message();
|
||||
message.should_not be_nil
|
||||
message.Get().Utf8Value().should eql "Uncaught Error: boom!"
|
||||
expect(message.GetSourceLine(@cxt)).to be_successful
|
||||
message.GetSourceLine(@cxt).FromJust().Utf8Value().should eql " throw new Error('boom!')"
|
||||
message.GetScriptResourceName().Utf8Value().should eql "<eval>"
|
||||
expect(message.GetLineNumber(@cxt)).to be_successful
|
||||
expect(message.GetLineNumber(@cxt)).to eq_just 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 15
|
||||
frame.GetScriptName().Utf8Value().should eql "<eval>"
|
||||
frame.IsEval().should be_falsey
|
||||
frame.IsConstructor().should be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
it "sees JavaScript exceptions thrown from Ruby" do
|
||||
V8::C::TryCatch(@isolate) do |trycatch|
|
||||
message = V8::C::String::NewFromUtf8(@isolate, "boom!")
|
||||
@isolate.ThrowException(V8::C::Exception::Error(message))
|
||||
expect(trycatch.HasCaught).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
it "won't die on a ruby exception" do
|
||||
expect {
|
||||
V8::C::TryCatch(@isolate) do |trycatch|
|
||||
fail "boom!"
|
||||
end
|
||||
}.to raise_error RuntimeError, "boom!"
|
||||
end
|
||||
end
|
Loading…
Add table
Reference in a new issue