mirror of
https://github.com/rubyjs/therubyracer
synced 2023-03-27 23:21:42 -04:00
Lock inside the C methods and store isolate with every ref
This commit is contained in:
parent
7f48722d72
commit
a926860150
22 changed files with 188 additions and 304 deletions
|
@ -38,12 +38,20 @@ namespace rr {
|
|||
}
|
||||
|
||||
VALUE Context::Enter(VALUE self) {
|
||||
Context(self)->Enter();
|
||||
Context context(self);
|
||||
Locker lock(context.getIsolate());
|
||||
|
||||
context->Enter();
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
VALUE Context::Exit(VALUE self) {
|
||||
Context(self)->Exit();
|
||||
Context context(self);
|
||||
Locker lock(context.getIsolate());
|
||||
|
||||
context->Exit();
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace rr {
|
|||
// static VALUE IsCodeGenerationFromStringsAllowed(VALUE self);
|
||||
|
||||
inline Context(VALUE value) : Ref<v8::Context>(value) {}
|
||||
inline Context(v8::Handle<v8::Context> cxt) : Ref<v8::Context>(cxt) {}
|
||||
inline Context(v8::Handle<v8::Context> ctx) : Ref<v8::Context>(ctx->GetIsolate(), ctx) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -25,8 +25,13 @@ namespace rr {
|
|||
}
|
||||
|
||||
VALUE Handles::SetupAndCall(Isolate isolate, int* state, VALUE block) {
|
||||
Locker lock(isolate);
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
return rb_protect(&DoCall, block, state);
|
||||
|
||||
{
|
||||
Unlocker unlock(isolate);
|
||||
return rb_protect(&DoCall, block, state);
|
||||
}
|
||||
}
|
||||
|
||||
VALUE Handles::DoCall(VALUE block) {
|
||||
|
|
|
@ -17,7 +17,6 @@ extern "C" {
|
|||
Object::Init();
|
||||
Primitive::Init();
|
||||
String::Init();
|
||||
Locker::Init();
|
||||
|
||||
// Accessor::Init();
|
||||
// Invocation::Init();
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
#include "rr.h"
|
||||
|
||||
namespace rr {
|
||||
|
||||
void Locker::Init() {
|
||||
ClassBuilder("Locker").
|
||||
defineSingletonMethod("IsLocked", &IsLocked).
|
||||
defineSingletonMethod("IsActive", &IsActive);
|
||||
|
||||
VALUE v8 = rb_define_module("V8");
|
||||
VALUE c = rb_define_module_under(v8, "C");
|
||||
|
||||
rb_define_singleton_method(c, "Locker", (VALUE (*)(...))&Lock, -1);
|
||||
rb_define_singleton_method(c, "Unlocker",(VALUE (*)(...))&Unlock, -1);
|
||||
}
|
||||
|
||||
VALUE Locker::IsLocked(VALUE self, VALUE isolate) {
|
||||
return Bool(v8::Locker::IsLocked(Isolate(isolate)));
|
||||
}
|
||||
|
||||
VALUE Locker::IsActive(VALUE self) {
|
||||
return Bool(v8::Locker::IsActive());
|
||||
}
|
||||
|
||||
VALUE Locker::Lock(int argc, VALUE* argv, VALUE self) {
|
||||
if (!rb_block_given_p()) {
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
int state = 0;
|
||||
|
||||
VALUE isolate, block;
|
||||
rb_scan_args(argc, argv, "10&", &isolate, &block);
|
||||
|
||||
VALUE result = setupLockAndCall(Isolate(isolate), &state, block);
|
||||
|
||||
if (state != 0) {
|
||||
rb_jump_tag(state);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
VALUE Locker::setupLockAndCall(Isolate isolate, int* state, VALUE block) {
|
||||
v8::Locker locker(isolate);
|
||||
|
||||
return rb_protect(&doLockCall, block, state);
|
||||
}
|
||||
|
||||
VALUE Locker::doLockCall(VALUE block) {
|
||||
return rb_funcall(block, rb_intern("call"), 0);
|
||||
}
|
||||
|
||||
VALUE Locker::Unlock(int argc, VALUE* argv, VALUE self) {
|
||||
if (!rb_block_given_p()) {
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
int state = 0;
|
||||
|
||||
VALUE isolate, block;
|
||||
rb_scan_args(argc, argv, "10&", &isolate, &block);
|
||||
|
||||
VALUE result = setupUnlockAndCall(Isolate(isolate), &state, block);
|
||||
|
||||
if (state != 0) {
|
||||
rb_jump_tag(state);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
VALUE Locker::setupUnlockAndCall(Isolate isolate, int* state, VALUE block) {
|
||||
v8::Unlocker unlocker(isolate);
|
||||
|
||||
return rb_protect(&doUnlockCall, block, state);
|
||||
}
|
||||
|
||||
VALUE Locker::doUnlockCall(VALUE block) {
|
||||
return rb_funcall(block, rb_intern("call"), 0);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
#ifndef RR_LOCKER
|
||||
#define RR_LOCKER
|
||||
|
||||
namespace rr {
|
||||
|
||||
class Locker {
|
||||
public:
|
||||
static void Init();
|
||||
|
||||
static VALUE IsLocked(VALUE self, VALUE isolate);
|
||||
static VALUE IsActive(VALUE self);
|
||||
|
||||
static VALUE Lock(int argc, VALUE* argv, VALUE self);
|
||||
static VALUE Unlock(int argc, VALUE* argv, VALUE self);
|
||||
|
||||
protected:
|
||||
static VALUE setupLockAndCall(Isolate isolate, int* state, VALUE code);
|
||||
static VALUE doLockCall(VALUE code);
|
||||
|
||||
static VALUE setupUnlockAndCall(Isolate isolate, int* state, VALUE code);
|
||||
static VALUE doUnlockCall(VALUE code);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
31
ext/v8/locks.h
Normal file
31
ext/v8/locks.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
#ifndef RR_LOCKER
|
||||
#define RR_LOCKER
|
||||
|
||||
namespace rr {
|
||||
|
||||
class Locker {
|
||||
public:
|
||||
Locker(v8::Isolate* _isolate): isolate(_isolate), locker(_isolate) {
|
||||
isolate->Enter();
|
||||
}
|
||||
|
||||
~Locker() {
|
||||
isolate->Exit();
|
||||
}
|
||||
|
||||
protected:
|
||||
v8::Isolate* isolate;
|
||||
v8::Locker locker;
|
||||
};
|
||||
|
||||
class Unlocker {
|
||||
public:
|
||||
Unlocker(v8::Isolate* isolate): unlocker(isolate) {}
|
||||
|
||||
protected:
|
||||
v8::Unlocker unlocker;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -12,24 +12,33 @@ namespace rr {
|
|||
store(&Class);
|
||||
}
|
||||
|
||||
VALUE Object::New(VALUE self) {
|
||||
return Object(v8::Object::New(v8::Isolate::GetCurrent()));
|
||||
VALUE Object::New(VALUE self, VALUE rb_isolate) {
|
||||
Isolate isolate(rb_isolate);
|
||||
Locker lock(isolate);
|
||||
|
||||
return Object(isolate, v8::Object::New(isolate));
|
||||
}
|
||||
|
||||
// TODO: Allow setting of property attributes
|
||||
VALUE Object::Set(VALUE self, VALUE key, VALUE value) {
|
||||
Object object(self);
|
||||
Locker lock(object.getIsolate());
|
||||
|
||||
if (rb_obj_is_kind_of(key, rb_cNumeric)) {
|
||||
return Bool(Object(self)->Set(UInt32(key), Value(value)));
|
||||
return Bool(object->Set(UInt32(key), Value::rubyObjectToHandle(object.getIsolate(), value)));
|
||||
} else {
|
||||
return Bool(Object(self)->Set(*Value(key), Value(value)));
|
||||
return Bool(object->Set(*Value(key), Value::rubyObjectToHandle(object.getIsolate(), value)));
|
||||
}
|
||||
}
|
||||
|
||||
VALUE Object::Get(VALUE self, VALUE key) {
|
||||
Object object(self);
|
||||
Locker lock(object.getIsolate());
|
||||
|
||||
if (rb_obj_is_kind_of(key, rb_cNumeric)) {
|
||||
return Value(Object(self)->Get(UInt32(key)));
|
||||
return Value::handleToRubyObject(object.getIsolate(), object->Get(UInt32(key)));
|
||||
} else {
|
||||
return Value(Object(self)->Get(*Value(key)));
|
||||
return Value::handleToRubyObject(object.getIsolate(), object->Get(*Value(key)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,7 +49,7 @@ namespace rr {
|
|||
|
||||
Backref* backref;
|
||||
|
||||
v8::Local<v8::String> key(v8::String::NewFromUtf8(v8::Isolate::GetCurrent(), "rr::Backref"));
|
||||
v8::Local<v8::String> key(v8::String::NewFromUtf8(getIsolate(), "rr::Backref"));
|
||||
v8::Local<v8::Value> external = handle->GetHiddenValue(key);
|
||||
|
||||
VALUE value;
|
||||
|
|
|
@ -6,7 +6,7 @@ namespace rr {
|
|||
class Object : public Ref<v8::Object> {
|
||||
public:
|
||||
static void Init();
|
||||
static VALUE New(VALUE self);
|
||||
static VALUE New(VALUE self, VALUE isolate);
|
||||
static VALUE Set(VALUE self, VALUE key, VALUE value);
|
||||
// static VALUE ForceSet(VALUE self, VALUE key, VALUE value);
|
||||
static VALUE Get(VALUE self, VALUE key);
|
||||
|
@ -55,7 +55,7 @@ namespace rr {
|
|||
// static VALUE CallAsConstructor(VALUE self, VALUE argv);
|
||||
|
||||
inline Object(VALUE value) : Ref<v8::Object>(value) {}
|
||||
inline Object(v8::Handle<v8::Object> object) : Ref<v8::Object>(object) {}
|
||||
inline Object(v8::Isolate* isolate, v8::Handle<v8::Object> object) : Ref<v8::Object>(isolate, object) {}
|
||||
|
||||
virtual operator VALUE();
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace rr {
|
|||
static void Init();
|
||||
|
||||
inline Primitive(VALUE value) : Ref<v8::Primitive>(value) {}
|
||||
inline Primitive(v8::Handle<v8::Primitive> primitive) : Ref<v8::Primitive>(primitive) {}
|
||||
inline Primitive(v8::Isolate* isolate, v8::Handle<v8::Primitive> primitive) : Ref<v8::Primitive>(isolate, primitive) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
55
ext/v8/ref.h
55
ext/v8/ref.h
|
@ -33,9 +33,19 @@ namespace rr {
|
|||
public:
|
||||
Ref(VALUE value) {
|
||||
this->value = value;
|
||||
Holder* holder = unwrapHolder();
|
||||
|
||||
if (holder) {
|
||||
this->isolate = holder->isolate;
|
||||
this->handle = v8::Local<T>::New(holder->isolate, *holder->handle);
|
||||
} else {
|
||||
this->isolate = NULL;
|
||||
this->handle = v8::Local<T>();
|
||||
}
|
||||
}
|
||||
|
||||
Ref(v8::Local<T> handle) {
|
||||
Ref(v8::Isolate* isolate, v8::Local<T> handle) {
|
||||
this->isolate = isolate;
|
||||
this->handle = handle;
|
||||
}
|
||||
|
||||
|
@ -49,21 +59,18 @@ namespace rr {
|
|||
return Qnil;
|
||||
}
|
||||
|
||||
return Data_Wrap_Struct(Class, 0, &Holder::destroy, new Holder(handle));
|
||||
return Data_Wrap_Struct(Class, 0, &Holder::destroy, new Holder(isolate, handle));
|
||||
}
|
||||
|
||||
/*
|
||||
* Coerce a Ref into a v8::Local.
|
||||
*/
|
||||
virtual operator v8::Handle<T>() const {
|
||||
if (RTEST(this->value)) {
|
||||
Holder* holder = NULL;
|
||||
Data_Get_Struct(this->value, class Holder, holder);
|
||||
return handle;
|
||||
}
|
||||
|
||||
return v8::Local<T>::New(v8::Isolate::GetCurrent(), *holder->handle);
|
||||
} else {
|
||||
return v8::Local<T>();
|
||||
}
|
||||
v8::Isolate* getIsolate() const {
|
||||
return isolate;
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
|
@ -84,9 +91,10 @@ namespace rr {
|
|||
class Holder {
|
||||
friend class Ref;
|
||||
public:
|
||||
Holder(v8::Handle<T> handle) {
|
||||
Holder(v8::Isolate* isolate, v8::Handle<T> handle) {
|
||||
this->disposed_p = false;
|
||||
this->handle = new v8::Persistent<T>(v8::Isolate::GetCurrent(), handle);
|
||||
this->isolate = isolate;
|
||||
this->handle = new v8::Persistent<T>(isolate, handle);
|
||||
}
|
||||
|
||||
virtual ~Holder() {
|
||||
|
@ -102,23 +110,34 @@ namespace rr {
|
|||
}
|
||||
|
||||
protected:
|
||||
v8::Isolate* isolate;
|
||||
v8::Persistent<T>* handle;
|
||||
bool disposed_p;
|
||||
|
||||
static void destroy(Holder* holder) {
|
||||
holder->dispose();
|
||||
|
||||
// TODO: This previously enqueued the holder to be disposed of
|
||||
// in `AddGCPrologueCallback`. Now that `AddGCPrologueCallback` depends
|
||||
// on an active Isolate (and must be registered for each one) it
|
||||
// might be better to just dispose of the object on the spot.
|
||||
// GC::Finalize(holder);
|
||||
// TODO: Use a nonblocking queue with `AddGCPrologueCallback`
|
||||
}
|
||||
};
|
||||
|
||||
VALUE value;
|
||||
v8::Handle<T> handle;
|
||||
static VALUE Class;
|
||||
|
||||
protected:
|
||||
Holder* unwrapHolder() const {
|
||||
if (RTEST(this->value)) {
|
||||
Holder* holder = NULL;
|
||||
Data_Get_Struct(this->value, class Holder, holder);
|
||||
return holder;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
VALUE value;
|
||||
|
||||
v8::Isolate* isolate;
|
||||
v8::Handle<T> handle;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
|
|
|
@ -15,11 +15,12 @@
|
|||
#include "equiv.h"
|
||||
#include "bool.h"
|
||||
#include "pointer.h"
|
||||
#include "isolate.h"
|
||||
|
||||
#include "ref.h"
|
||||
|
||||
#include "v8.h"
|
||||
#include "isolate.h"
|
||||
#include "locker.h"
|
||||
#include "locks.h"
|
||||
#include "handles.h"
|
||||
#include "context.h"
|
||||
|
||||
|
|
|
@ -12,10 +12,12 @@ namespace rr {
|
|||
store(&Class);
|
||||
}
|
||||
|
||||
VALUE String::NewFromUtf8(VALUE StringClass, VALUE string) {
|
||||
v8::Local<v8::String> v8_string = v8::String::NewFromUtf8(v8::Isolate::GetCurrent(), RSTRING_PTR(string), v8::String::kNormalString, (int)RSTRING_LEN(string));
|
||||
VALUE String::NewFromUtf8(VALUE StringClass, VALUE rb_isolate, VALUE string) {
|
||||
Isolate isolate(rb_isolate);
|
||||
|
||||
return String(v8_string);
|
||||
v8::Local<v8::String> v8_string = v8::String::NewFromUtf8(isolate, RSTRING_PTR(string), v8::String::kNormalString, (int)RSTRING_LEN(string));
|
||||
|
||||
return String(isolate, v8_string);
|
||||
}
|
||||
|
||||
VALUE String::Utf8Value(VALUE self) {
|
||||
|
@ -29,20 +31,20 @@ namespace rr {
|
|||
}
|
||||
|
||||
VALUE String::Concat(VALUE self, VALUE left, VALUE right) {
|
||||
return String(v8::String::Concat(String(left), String(right)));
|
||||
String left_string(left);
|
||||
|
||||
return String(left_string.getIsolate(), v8::String::Concat(left_string, String(right)));
|
||||
}
|
||||
|
||||
String::operator v8::Handle<v8::String>() const {
|
||||
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
||||
|
||||
switch (TYPE(value)) {
|
||||
case T_STRING:
|
||||
return v8::String::NewFromUtf8(isolate, RSTRING_PTR(value), v8::String::kNormalString, (int)RSTRING_LEN(value));
|
||||
return v8::String::NewFromUtf8(getIsolate(), RSTRING_PTR(value), v8::String::kNormalString, (int)RSTRING_LEN(value));
|
||||
case T_DATA:
|
||||
return Ref<v8::String>::operator v8::Handle<v8::String>();
|
||||
default:
|
||||
VALUE string = rb_funcall(value, rb_intern("to_s"), 0);
|
||||
return v8::String::NewFromUtf8(isolate, RSTRING_PTR(string), v8::String::kNormalString, (int)RSTRING_LEN(string));
|
||||
return v8::String::NewFromUtf8(getIsolate(), RSTRING_PTR(string), v8::String::kNormalString, (int)RSTRING_LEN(string));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,12 +7,12 @@ namespace rr {
|
|||
public:
|
||||
static void Init();
|
||||
|
||||
static VALUE NewFromUtf8(VALUE self, VALUE value);
|
||||
static VALUE NewFromUtf8(VALUE self, VALUE isolate, VALUE value);
|
||||
static VALUE Utf8Value(VALUE self);
|
||||
static VALUE Concat(VALUE self, VALUE left, VALUE right);
|
||||
|
||||
inline String(VALUE value) : Ref<v8::String>(value) {}
|
||||
inline String(v8::Handle<v8::String> string) : Ref<v8::String>(string) {}
|
||||
inline String(v8::Isolate* isolate, v8::Handle<v8::String> string) : Ref<v8::String>(isolate, string) {}
|
||||
|
||||
virtual operator v8::Handle<v8::String>() const;
|
||||
};
|
||||
|
|
|
@ -40,6 +40,9 @@ namespace rr {
|
|||
// defineMethod("Equals", &Equals).
|
||||
// defineMethod("StrictEquals", &StrictEquals).
|
||||
|
||||
defineMethod("ToRubyObject", &ToRubyObject).
|
||||
defineSingletonMethod("FromRubyObject", &FromRubyObject).
|
||||
|
||||
store(&Class);
|
||||
|
||||
rb_gc_register_address(&Empty);
|
||||
|
@ -82,7 +85,9 @@ namespace rr {
|
|||
}
|
||||
|
||||
VALUE Value::ToString(VALUE self) {
|
||||
return String(Value(self)->ToString());
|
||||
Value value(self);
|
||||
|
||||
return String(value.getIsolate(), value->ToString());
|
||||
}
|
||||
|
||||
VALUE Value::Equals(VALUE self, VALUE other) {
|
||||
|
@ -93,7 +98,21 @@ namespace rr {
|
|||
return Bool(Value(self)->StrictEquals(Value(other)));
|
||||
}
|
||||
|
||||
Value::operator VALUE() {
|
||||
VALUE Value::ToRubyObject(VALUE self) {
|
||||
Value value(self);
|
||||
Locker lock(value.getIsolate());
|
||||
|
||||
return handleToRubyObject(value.getIsolate(), value);
|
||||
}
|
||||
|
||||
VALUE Value::FromRubyObject(VALUE selfClass, VALUE rb_isolate, VALUE value) {
|
||||
Isolate isolate(rb_isolate);
|
||||
Locker lock(isolate);
|
||||
|
||||
return Value(isolate, rubyObjectToHandle(isolate, value));
|
||||
}
|
||||
|
||||
VALUE Value::handleToRubyObject(v8::Isolate* isolate, v8::Handle<v8::Value> handle) {
|
||||
if (handle.IsEmpty() || handle->IsUndefined() || handle->IsNull()) {
|
||||
return Qnil;
|
||||
}
|
||||
|
@ -129,7 +148,7 @@ namespace rr {
|
|||
// }
|
||||
|
||||
if (handle->IsString()) {
|
||||
return String(handle->ToString());
|
||||
return String(isolate, handle->ToString());
|
||||
}
|
||||
|
||||
// TODO
|
||||
|
@ -138,19 +157,19 @@ namespace rr {
|
|||
// }
|
||||
|
||||
if (handle->IsObject()) {
|
||||
return Object(handle->ToObject());
|
||||
return Object(isolate, handle->ToObject());
|
||||
}
|
||||
|
||||
return Ref<v8::Value>::operator VALUE();
|
||||
return Value(isolate, handle);
|
||||
}
|
||||
|
||||
Value::operator v8::Handle<v8::Value>() const {
|
||||
v8::Handle<v8::Value> Value::rubyObjectToHandle(v8::Isolate* isolate, VALUE value) {
|
||||
Locker lock(isolate);
|
||||
|
||||
if (rb_equal(value, Empty)) {
|
||||
return v8::Handle<v8::Value>();
|
||||
}
|
||||
|
||||
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
||||
|
||||
switch (TYPE(value)) {
|
||||
case T_FIXNUM:
|
||||
return v8::Integer::New(isolate, NUM2INT(value));
|
||||
|
@ -165,7 +184,7 @@ namespace rr {
|
|||
case T_FALSE:
|
||||
return v8::False(isolate);
|
||||
case T_DATA:
|
||||
return Ref<v8::Value>::operator v8::Handle<v8::Value>();
|
||||
return Value(value);
|
||||
case T_OBJECT:
|
||||
case T_CLASS:
|
||||
case T_ICLASS:
|
||||
|
|
|
@ -35,14 +35,17 @@ namespace rr {
|
|||
// static VALUE Uint32Value(VALUE self);
|
||||
// static VALUE Int32Value(VALUE self);
|
||||
|
||||
static VALUE ToRubyObject(VALUE self);
|
||||
static VALUE FromRubyObject(VALUE self, VALUE isolate, VALUE value);
|
||||
|
||||
static VALUE Equals(VALUE self, VALUE other);
|
||||
static VALUE StrictEquals(VALUE self, VALUE other);
|
||||
|
||||
inline Value(VALUE value) : Ref<v8::Value>(value) {}
|
||||
inline Value(v8::Handle<v8::Value> value) : Ref<v8::Value>(value) {}
|
||||
inline Value(v8::Isolate* isolate, v8::Handle<v8::Value> value) : Ref<v8::Value>(isolate, value) {}
|
||||
|
||||
virtual operator VALUE();
|
||||
virtual operator v8::Handle<v8::Value>() const;
|
||||
static VALUE handleToRubyObject(v8::Isolate* isolate, v8::Handle<v8::Value> value);
|
||||
static v8::Handle<v8::Value> rubyObjectToHandle(v8::Isolate* isolate, VALUE value);
|
||||
|
||||
static VALUE Empty;
|
||||
};
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
require 'c_spec_helper'
|
||||
|
||||
describe V8::C::Isolate do
|
||||
before(:each) { cleanup_isolates }
|
||||
|
||||
it 'can create a new isolate' do
|
||||
expect(V8::C::Isolate.New).to be
|
||||
end
|
||||
|
@ -14,43 +12,4 @@ describe V8::C::Isolate do
|
|||
expect(isolate_one.Equals(isolate_one)).to eq true
|
||||
expect(isolate_one.Equals(isolate_two)).to eq false
|
||||
end
|
||||
|
||||
describe 'GetCurrent' do
|
||||
it 'can get the current isolate for the thread' do
|
||||
isolate = V8::C::Isolate.New
|
||||
isolate.Enter
|
||||
|
||||
expect(V8::C::Isolate.GetCurrent.Equals(isolate)).to eq true
|
||||
end
|
||||
|
||||
it 'returns nil if there are no entered isolates' do
|
||||
expect(V8::C::Isolate.GetCurrent).to be_nil
|
||||
end
|
||||
|
||||
it 'is different accross threads' do
|
||||
V8::C::Isolate.New.Enter
|
||||
|
||||
Thread.new do
|
||||
expect(V8::C::Isolate.GetCurrent).to be_nil
|
||||
end.join
|
||||
end
|
||||
end
|
||||
|
||||
it 'can enter and exit multiple times' do
|
||||
isolate_one = V8::C::Isolate.New
|
||||
isolate_two = V8::C::Isolate.New
|
||||
|
||||
isolate_one.Enter
|
||||
isolate_one.Enter
|
||||
isolate_two.Enter
|
||||
|
||||
expect(V8::C::Isolate.GetCurrent.Equals(isolate_two)).to eq true
|
||||
isolate_two.Exit
|
||||
|
||||
expect(V8::C::Isolate.GetCurrent.Equals(isolate_one)).to eq true
|
||||
isolate_one.Exit
|
||||
|
||||
expect(V8::C::Isolate.GetCurrent.Equals(isolate_one)).to eq true
|
||||
isolate_one.Exit
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
require 'c_spec_helper'
|
||||
|
||||
describe V8::C::Locker do
|
||||
let(:isolate) { V8::C::Isolate.New }
|
||||
|
||||
it 'can lock and unlock the VM' do
|
||||
expect(V8::C::Locker::IsLocked(isolate)).to eq false
|
||||
|
||||
V8::C::Locker(isolate) do
|
||||
expect(V8::C::Locker::IsLocked(isolate)).to eq true
|
||||
|
||||
V8::C::Unlocker(isolate) do
|
||||
expect(V8::C::Locker::IsLocked(isolate)).to eq false
|
||||
end
|
||||
end
|
||||
|
||||
expect(V8::C::Locker::IsLocked(isolate)).to eq false
|
||||
end
|
||||
|
||||
it 'properly unlocks if an exception is thrown inside a lock block' do
|
||||
begin
|
||||
V8::C::Locker(isolate) do
|
||||
raise 'boom!'
|
||||
end
|
||||
rescue
|
||||
expect(V8::C::Locker::IsLocked(isolate)).to eq false
|
||||
end
|
||||
end
|
||||
|
||||
it 'properly re-locks if an exception is thrown inside an un-lock block' do
|
||||
V8::C::Locker(isolate) do
|
||||
begin
|
||||
V8::C::Unlocker(isolate) do
|
||||
raise 'boom!'
|
||||
end
|
||||
rescue
|
||||
expect(V8::C::Locker::IsLocked(isolate)).to eq true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -4,13 +4,13 @@ describe V8::C::Object do
|
|||
requires_v8_context
|
||||
|
||||
it 'can create a new object' do
|
||||
expect(V8::C::Object.New).to be
|
||||
expect(V8::C::Object.New(@isolate)).to be
|
||||
end
|
||||
|
||||
it 'can store and retrieve a value' do
|
||||
o = V8::C::Object.New
|
||||
key = V8::C::String.NewFromUtf8('foo')
|
||||
value = V8::C::String.NewFromUtf8('bar')
|
||||
o = V8::C::Object.New(@isolate)
|
||||
key = V8::C::String.NewFromUtf8(@isolate, 'foo')
|
||||
value = V8::C::String.NewFromUtf8(@isolate, 'bar')
|
||||
|
||||
o.Set(key, value)
|
||||
expect(o.Get(key).Utf8Value).to eq 'bar'
|
||||
|
@ -48,10 +48,11 @@ describe V8::C::Object do
|
|||
# end
|
||||
|
||||
it 'always returns the same ruby object for the same V8 object' do
|
||||
one = V8::C::Object.New
|
||||
two = V8::C::Object.New
|
||||
key = V8::C::String.NewFromUtf8(@isolate, 'two')
|
||||
one = V8::C::Object.New(@isolate)
|
||||
two = V8::C::Object.New(@isolate)
|
||||
|
||||
one.Set('two', two)
|
||||
expect(one.Get('two')).to be two
|
||||
one.Set(key, two)
|
||||
expect(one.Get(key)).to be two
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,34 +4,26 @@ describe V8::C::String do
|
|||
requires_v8_context
|
||||
|
||||
it 'is a primitive' do
|
||||
expect(V8::C::String.NewFromUtf8('test')).to be_a V8::C::Primitive
|
||||
expect(V8::C::String.NewFromUtf8(@isolate, 'test')).to be_a V8::C::Primitive
|
||||
end
|
||||
|
||||
it 'can create new strings' do
|
||||
expect(V8::C::String.NewFromUtf8('test')).to be
|
||||
expect(V8::C::String.NewFromUtf8(@isolate, 'test')).to be
|
||||
end
|
||||
|
||||
it 'can be converted to a ruby string' do
|
||||
expect(V8::C::String.NewFromUtf8('test').Utf8Value).to eq 'test'
|
||||
expect(V8::C::String.NewFromUtf8(@isolate, 'test').Utf8Value).to eq 'test'
|
||||
end
|
||||
|
||||
it 'can hold Unicode values outside the Basic Multilingual Plane' do
|
||||
string = V8::C::String.NewFromUtf8("\u{100000}")
|
||||
string = V8::C::String.NewFromUtf8(@isolate, "\u{100000}")
|
||||
expect(string.Utf8Value).to eq "\u{100000}"
|
||||
end
|
||||
|
||||
it 'can concatenate strings' do
|
||||
string_one = V8::C::String.NewFromUtf8('Hello ')
|
||||
string_two = V8::C::String.NewFromUtf8('World!')
|
||||
string_one = V8::C::String.NewFromUtf8(@isolate, 'Hello ')
|
||||
string_two = V8::C::String.NewFromUtf8(@isolate, 'World!')
|
||||
|
||||
expect(V8::C::String.Concat(string_one, string_two).Utf8Value).to eq 'Hello World!'
|
||||
end
|
||||
|
||||
it 'can naturally translate ruby strings into v8 strings' do
|
||||
expect(V8::C::String.Concat(V8::C::String.NewFromUtf8('Hello '), 'World').Utf8Value).to eq 'Hello World'
|
||||
end
|
||||
|
||||
it 'can naturally translate ruby objects into v8 strings' do
|
||||
expect(V8::C::String.Concat(V8::C::String.NewFromUtf8('forty two is '), 42).Utf8Value).to eq 'forty two is 42'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,42 +3,38 @@ require 'c_spec_helper'
|
|||
describe V8::C::Value do
|
||||
requires_v8_context
|
||||
|
||||
def to_value(value)
|
||||
object = V8::C::Object.New
|
||||
key = V8::C::String.NewFromUtf8('key')
|
||||
|
||||
object.Set(key, value)
|
||||
object.Get(key)
|
||||
def convert(value)
|
||||
V8::C::Value.FromRubyObject(@isolate, value).ToRubyObject
|
||||
end
|
||||
|
||||
it 'converts strings' do
|
||||
expect(to_value(V8::C::String.NewFromUtf8('value')).Utf8Value).to eq 'value'
|
||||
expect(convert('value').Utf8Value).to eq 'value'
|
||||
end
|
||||
|
||||
it 'converts nil' do
|
||||
expect(to_value(nil)).to eq nil
|
||||
expect(convert(nil)).to eq nil
|
||||
end
|
||||
|
||||
it 'converts undefined to nil' do
|
||||
object = V8::C::Object.New
|
||||
key = V8::C::String.NewFromUtf8('key')
|
||||
object = V8::C::Object.New(@isolate)
|
||||
key = V8::C::String.NewFromUtf8(@isolate, 'key')
|
||||
|
||||
expect(object.Get(key)).to eq nil
|
||||
end
|
||||
|
||||
it 'converts FixNums' do
|
||||
expect(to_value(42)).to eq 42
|
||||
expect(convert(42)).to eq 42
|
||||
end
|
||||
|
||||
it 'converts booleans' do
|
||||
expect(to_value(true)).to eq true
|
||||
expect(to_value(false)).to eq false
|
||||
expect(convert(true)).to eq true
|
||||
expect(convert(false)).to eq false
|
||||
end
|
||||
|
||||
it 'converts objects' do
|
||||
object = V8::C::Object.New
|
||||
object.Set(V8::C::String.NewFromUtf8('test'), 1)
|
||||
object = V8::C::Object.New(@isolate)
|
||||
object.Set(V8::C::String.NewFromUtf8(@isolate, 'test'), 1)
|
||||
|
||||
expect(to_value(object)).to eq object
|
||||
expect(convert(object)).to eq object
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,28 +11,18 @@ module V8ContextHelpers
|
|||
end
|
||||
|
||||
def bootstrap_v8_context
|
||||
cleanup_isolates
|
||||
@isolate = V8::C::Isolate.New
|
||||
|
||||
isolate = V8::C::Isolate.New
|
||||
|
||||
V8::C::Locker(isolate) do
|
||||
isolate.Enter
|
||||
V8::C::HandleScope(isolate) do
|
||||
@cxt = V8::C::Context::New(isolate)
|
||||
begin
|
||||
@cxt.Enter
|
||||
yield
|
||||
ensure
|
||||
@cxt.Exit
|
||||
end
|
||||
V8::C::HandleScope(@isolate) do
|
||||
@cxt = V8::C::Context::New(@isolate)
|
||||
begin
|
||||
@cxt.Enter
|
||||
yield
|
||||
ensure
|
||||
@cxt.Exit
|
||||
end
|
||||
isolate.Exit
|
||||
end
|
||||
end
|
||||
|
||||
def cleanup_isolates
|
||||
V8::C::Isolate.GetCurrent.Exit while V8::C::Isolate.GetCurrent
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.configure do |c|
|
||||
|
|
Loading…
Add table
Reference in a new issue