1
0
Fork 0
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:
Charles Lowell 2015-08-13 13:14:29 +03:00
commit 5a1632adba
18 changed files with 687 additions and 10 deletions

View file

@ -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;

View file

@ -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));

View file

@ -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
View 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 */

View file

@ -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
View 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

View file

@ -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);

View file

@ -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
View 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 */

View file

@ -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
View 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
View 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
View file

@ -0,0 +1,5 @@
#include "rr.h"
namespace rr {
VALUE TryCatch::Class;
}

192
ext/v8/try-catch.h Normal file
View 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 */

View file

@ -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());

View file

@ -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);

View 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
View 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