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;
|
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)) {
|
ClassBuilder& ClassBuilder::defineMethod(const char* name, VALUE (*impl)(int, VALUE*, VALUE)) {
|
||||||
rb_define_method(this->value, name, (VALUE (*)(...))impl, -1);
|
rb_define_method(this->value, name, (VALUE (*)(...))impl, -1);
|
||||||
return *this;
|
return *this;
|
||||||
|
|
|
@ -16,6 +16,7 @@ namespace rr {
|
||||||
ClassBuilder(const char* name, const char* supername);
|
ClassBuilder(const char* name, const char* supername);
|
||||||
|
|
||||||
ClassBuilder& defineConst(const char* name, VALUE value);
|
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)(int, VALUE*, VALUE));
|
||||||
ClassBuilder& defineMethod(const char* name, VALUE (*impl)(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
|
* Internally, `Equiv`s are always stored as a Ruby `VALUE`, and so
|
||||||
* part of the job of the subclass is to have an appropriate
|
* part of the job of the subclass is to have an appropriate
|
||||||
* constructor that converts the C/C++ type to a `VALUE`
|
* 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 {
|
class Equiv {
|
||||||
public:
|
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();
|
ObjectTemplate::Init();
|
||||||
FunctionTemplate::Init();
|
FunctionTemplate::Init();
|
||||||
Signature::Init();
|
Signature::Init();
|
||||||
|
StackFrame::Init();
|
||||||
|
StackTrace::Init();
|
||||||
|
Message::Init();
|
||||||
|
Exception::Init();
|
||||||
|
TryCatch::Init();
|
||||||
|
|
||||||
// Invocation::Init();
|
// Invocation::Init();
|
||||||
// Signature::Init();
|
|
||||||
// Date::Init();
|
|
||||||
// Constants::Init();
|
// Constants::Init();
|
||||||
// Template::Init();
|
// Template::Init();
|
||||||
// Stack::Init();
|
|
||||||
// Message::Init();
|
|
||||||
// TryCatch::Init();
|
|
||||||
// Exception::Init();
|
|
||||||
// ResourceConstraints::Init();
|
// ResourceConstraints::Init();
|
||||||
// HeapStatistics::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++ -*-
|
// -*- mode: c++ -*-
|
||||||
#include "rr.h"
|
#include "rr.h"
|
||||||
#include "isolate.h"
|
|
||||||
|
|
||||||
namespace rr {
|
namespace rr {
|
||||||
|
|
||||||
|
@ -10,7 +9,8 @@ namespace rr {
|
||||||
rb_eval_string("require 'v8/retained_objects'");
|
rb_eval_string("require 'v8/retained_objects'");
|
||||||
ClassBuilder("Isolate").
|
ClassBuilder("Isolate").
|
||||||
defineSingletonMethod("New", &New).
|
defineSingletonMethod("New", &New).
|
||||||
|
defineMethod("ThrowException", &ThrowException).
|
||||||
|
defineMethod("SetCaptureStackTraceForUncaughtExceptions", &SetCaptureStackTraceForUncaughtExceptions).
|
||||||
defineMethod("IdleNotificationDeadline", &IdleNotificationDeadline).
|
defineMethod("IdleNotificationDeadline", &IdleNotificationDeadline).
|
||||||
|
|
||||||
store(&Class);
|
store(&Class);
|
||||||
|
@ -33,6 +33,23 @@ namespace rr {
|
||||||
return Isolate(isolate);
|
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) {
|
VALUE Isolate::IdleNotificationDeadline(VALUE self, VALUE deadline_in_seconds) {
|
||||||
Isolate isolate(self);
|
Isolate isolate(self);
|
||||||
|
|
|
@ -30,6 +30,8 @@ namespace rr {
|
||||||
static void Init();
|
static void Init();
|
||||||
|
|
||||||
static VALUE New(VALUE self);
|
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(IsolateData* data_) : data(data_) {}
|
||||||
inline Isolate(v8::Isolate* isolate) :
|
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 "equiv.h"
|
||||||
#include "bool.h"
|
#include "bool.h"
|
||||||
|
#include "int.h"
|
||||||
#include "uint32_t.h"
|
#include "uint32_t.h"
|
||||||
#include "pointer.h"
|
#include "pointer.h"
|
||||||
#include "wrapper.h"
|
#include "wrapper.h"
|
||||||
|
@ -68,4 +69,10 @@ inline VALUE not_implemented(const char* message) {
|
||||||
#include "function-template.h"
|
#include "function-template.h"
|
||||||
#include "object-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
|
#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("IsBooleanObject", &IsBooleanObject).
|
||||||
// defineMethod("IsNumberObject", &IsNumberObject).
|
// defineMethod("IsNumberObject", &IsNumberObject).
|
||||||
// defineMethod("IsStringObject", &IsStringObject).
|
// defineMethod("IsStringObject", &IsStringObject).
|
||||||
// defineMethod("IsNativeError", &IsNativeError).
|
defineMethod("IsNativeError", &IsNativeError).
|
||||||
// defineMethod("IsRegExp", &IsRegExp).
|
// defineMethod("IsRegExp", &IsRegExp).
|
||||||
defineMethod("ToString", &ToString).
|
defineMethod("ToString", &ToString).
|
||||||
// defineMethod("ToDetailString", &ToDetailString).
|
// defineMethod("ToDetailString", &ToDetailString).
|
||||||
|
@ -120,6 +120,13 @@ namespace rr {
|
||||||
return Bool(value->IsUint32());
|
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::ToString(VALUE self) {
|
||||||
Value value(self);
|
Value value(self);
|
||||||
Locker lock(value.getIsolate());
|
Locker lock(value.getIsolate());
|
||||||
|
|
|
@ -27,7 +27,7 @@ namespace rr {
|
||||||
// static VALUE IsBooleanObject(VALUE self);
|
// static VALUE IsBooleanObject(VALUE self);
|
||||||
// static VALUE IsNumberObject(VALUE self);
|
// static VALUE IsNumberObject(VALUE self);
|
||||||
// static VALUE IsStringObject(VALUE self);
|
// static VALUE IsStringObject(VALUE self);
|
||||||
// static VALUE IsNativeError(VALUE self);
|
static VALUE IsNativeError(VALUE self);
|
||||||
// static VALUE IsRegExp(VALUE self);
|
// static VALUE IsRegExp(VALUE self);
|
||||||
static VALUE ToString(VALUE self);
|
static VALUE ToString(VALUE self);
|
||||||
// static VALUE ToDetailString(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