1
0
Fork 0
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:
Georgy Angelov 2015-04-04 14:03:06 +00:00
parent 7f48722d72
commit a926860150
22 changed files with 188 additions and 304 deletions

View file

@ -38,12 +38,20 @@ namespace rr {
} }
VALUE Context::Enter(VALUE self) { VALUE Context::Enter(VALUE self) {
Context(self)->Enter(); Context context(self);
Locker lock(context.getIsolate());
context->Enter();
return Qnil; return Qnil;
} }
VALUE Context::Exit(VALUE self) { VALUE Context::Exit(VALUE self) {
Context(self)->Exit(); Context context(self);
Locker lock(context.getIsolate());
context->Exit();
return Qnil; return Qnil;
} }

View file

@ -31,7 +31,7 @@ namespace rr {
// static VALUE IsCodeGenerationFromStringsAllowed(VALUE self); // static VALUE IsCodeGenerationFromStringsAllowed(VALUE self);
inline Context(VALUE value) : Ref<v8::Context>(value) {} 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) {}
}; };
} }

View file

@ -25,8 +25,13 @@ namespace rr {
} }
VALUE Handles::SetupAndCall(Isolate isolate, int* state, VALUE block) { VALUE Handles::SetupAndCall(Isolate isolate, int* state, VALUE block) {
Locker lock(isolate);
v8::HandleScope handle_scope(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) { VALUE Handles::DoCall(VALUE block) {

View file

@ -17,7 +17,6 @@ extern "C" {
Object::Init(); Object::Init();
Primitive::Init(); Primitive::Init();
String::Init(); String::Init();
Locker::Init();
// Accessor::Init(); // Accessor::Init();
// Invocation::Init(); // Invocation::Init();

View file

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

View file

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

View file

@ -12,24 +12,33 @@ namespace rr {
store(&Class); store(&Class);
} }
VALUE Object::New(VALUE self) { VALUE Object::New(VALUE self, VALUE rb_isolate) {
return Object(v8::Object::New(v8::Isolate::GetCurrent())); Isolate isolate(rb_isolate);
Locker lock(isolate);
return Object(isolate, v8::Object::New(isolate));
} }
// TODO: Allow setting of property attributes // TODO: Allow setting of property attributes
VALUE Object::Set(VALUE self, VALUE key, VALUE value) { 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)) { 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 { } 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) { VALUE Object::Get(VALUE self, VALUE key) {
Object object(self);
Locker lock(object.getIsolate());
if (rb_obj_is_kind_of(key, rb_cNumeric)) { 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 { } 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; 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); v8::Local<v8::Value> external = handle->GetHiddenValue(key);
VALUE value; VALUE value;

View file

@ -6,7 +6,7 @@ namespace rr {
class Object : public Ref<v8::Object> { class Object : public Ref<v8::Object> {
public: public:
static void Init(); 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 Set(VALUE self, VALUE key, VALUE value);
// static VALUE ForceSet(VALUE self, VALUE key, VALUE value); // static VALUE ForceSet(VALUE self, VALUE key, VALUE value);
static VALUE Get(VALUE self, VALUE key); static VALUE Get(VALUE self, VALUE key);
@ -55,7 +55,7 @@ namespace rr {
// static VALUE CallAsConstructor(VALUE self, VALUE argv); // static VALUE CallAsConstructor(VALUE self, VALUE argv);
inline Object(VALUE value) : Ref<v8::Object>(value) {} 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(); virtual operator VALUE();

View file

@ -8,7 +8,7 @@ namespace rr {
static void Init(); static void Init();
inline Primitive(VALUE value) : Ref<v8::Primitive>(value) {} 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) {}
}; };
} }

View file

@ -33,9 +33,19 @@ namespace rr {
public: public:
Ref(VALUE value) { Ref(VALUE value) {
this->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; this->handle = handle;
} }
@ -49,21 +59,18 @@ namespace rr {
return Qnil; 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. * Coerce a Ref into a v8::Local.
*/ */
virtual operator v8::Handle<T>() const { virtual operator v8::Handle<T>() const {
if (RTEST(this->value)) { return handle;
Holder* holder = NULL; }
Data_Get_Struct(this->value, class Holder, holder);
return v8::Local<T>::New(v8::Isolate::GetCurrent(), *holder->handle); v8::Isolate* getIsolate() const {
} else { return isolate;
return v8::Local<T>();
}
} }
void dispose() { void dispose() {
@ -84,9 +91,10 @@ namespace rr {
class Holder { class Holder {
friend class Ref; friend class Ref;
public: public:
Holder(v8::Handle<T> handle) { Holder(v8::Isolate* isolate, v8::Handle<T> handle) {
this->disposed_p = false; 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() { virtual ~Holder() {
@ -102,23 +110,34 @@ namespace rr {
} }
protected: protected:
v8::Isolate* isolate;
v8::Persistent<T>* handle; v8::Persistent<T>* handle;
bool disposed_p; bool disposed_p;
static void destroy(Holder* holder) { static void destroy(Holder* holder) {
holder->dispose(); holder->dispose();
// TODO: This previously enqueued the holder to be disposed of // TODO: Use a nonblocking queue with `AddGCPrologueCallback`
// 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);
} }
}; };
VALUE value;
v8::Handle<T> handle;
static VALUE Class; 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> template <class T>

View file

@ -15,11 +15,12 @@
#include "equiv.h" #include "equiv.h"
#include "bool.h" #include "bool.h"
#include "pointer.h" #include "pointer.h"
#include "isolate.h"
#include "ref.h" #include "ref.h"
#include "v8.h" #include "v8.h"
#include "isolate.h" #include "locks.h"
#include "locker.h"
#include "handles.h" #include "handles.h"
#include "context.h" #include "context.h"

View file

@ -12,10 +12,12 @@ namespace rr {
store(&Class); store(&Class);
} }
VALUE String::NewFromUtf8(VALUE StringClass, VALUE string) { VALUE String::NewFromUtf8(VALUE StringClass, VALUE rb_isolate, VALUE string) {
v8::Local<v8::String> v8_string = v8::String::NewFromUtf8(v8::Isolate::GetCurrent(), RSTRING_PTR(string), v8::String::kNormalString, (int)RSTRING_LEN(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) { VALUE String::Utf8Value(VALUE self) {
@ -29,20 +31,20 @@ namespace rr {
} }
VALUE String::Concat(VALUE self, VALUE left, VALUE right) { 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 { String::operator v8::Handle<v8::String>() const {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
switch (TYPE(value)) { switch (TYPE(value)) {
case T_STRING: 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: case T_DATA:
return Ref<v8::String>::operator v8::Handle<v8::String>(); return Ref<v8::String>::operator v8::Handle<v8::String>();
default: default:
VALUE string = rb_funcall(value, rb_intern("to_s"), 0); 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));
} }
} }

View file

@ -7,12 +7,12 @@ namespace rr {
public: public:
static void Init(); 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 Utf8Value(VALUE self);
static VALUE Concat(VALUE self, VALUE left, VALUE right); static VALUE Concat(VALUE self, VALUE left, VALUE right);
inline String(VALUE value) : Ref<v8::String>(value) {} 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; virtual operator v8::Handle<v8::String>() const;
}; };

View file

@ -40,6 +40,9 @@ namespace rr {
// defineMethod("Equals", &Equals). // defineMethod("Equals", &Equals).
// defineMethod("StrictEquals", &StrictEquals). // defineMethod("StrictEquals", &StrictEquals).
defineMethod("ToRubyObject", &ToRubyObject).
defineSingletonMethod("FromRubyObject", &FromRubyObject).
store(&Class); store(&Class);
rb_gc_register_address(&Empty); rb_gc_register_address(&Empty);
@ -82,7 +85,9 @@ namespace rr {
} }
VALUE Value::ToString(VALUE self) { 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) { VALUE Value::Equals(VALUE self, VALUE other) {
@ -93,7 +98,21 @@ namespace rr {
return Bool(Value(self)->StrictEquals(Value(other))); 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()) { if (handle.IsEmpty() || handle->IsUndefined() || handle->IsNull()) {
return Qnil; return Qnil;
} }
@ -129,7 +148,7 @@ namespace rr {
// } // }
if (handle->IsString()) { if (handle->IsString()) {
return String(handle->ToString()); return String(isolate, handle->ToString());
} }
// TODO // TODO
@ -138,19 +157,19 @@ namespace rr {
// } // }
if (handle->IsObject()) { 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)) { if (rb_equal(value, Empty)) {
return v8::Handle<v8::Value>(); return v8::Handle<v8::Value>();
} }
v8::Isolate* isolate = v8::Isolate::GetCurrent();
switch (TYPE(value)) { switch (TYPE(value)) {
case T_FIXNUM: case T_FIXNUM:
return v8::Integer::New(isolate, NUM2INT(value)); return v8::Integer::New(isolate, NUM2INT(value));
@ -165,7 +184,7 @@ namespace rr {
case T_FALSE: case T_FALSE:
return v8::False(isolate); return v8::False(isolate);
case T_DATA: case T_DATA:
return Ref<v8::Value>::operator v8::Handle<v8::Value>(); return Value(value);
case T_OBJECT: case T_OBJECT:
case T_CLASS: case T_CLASS:
case T_ICLASS: case T_ICLASS:

View file

@ -35,14 +35,17 @@ namespace rr {
// static VALUE Uint32Value(VALUE self); // static VALUE Uint32Value(VALUE self);
// static VALUE Int32Value(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 Equals(VALUE self, VALUE other);
static VALUE StrictEquals(VALUE self, VALUE other); static VALUE StrictEquals(VALUE self, VALUE other);
inline Value(VALUE value) : Ref<v8::Value>(value) {} 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(); static VALUE handleToRubyObject(v8::Isolate* isolate, v8::Handle<v8::Value> value);
virtual operator v8::Handle<v8::Value>() const; static v8::Handle<v8::Value> rubyObjectToHandle(v8::Isolate* isolate, VALUE value);
static VALUE Empty; static VALUE Empty;
}; };

View file

@ -1,8 +1,6 @@
require 'c_spec_helper' require 'c_spec_helper'
describe V8::C::Isolate do describe V8::C::Isolate do
before(:each) { cleanup_isolates }
it 'can create a new isolate' do it 'can create a new isolate' do
expect(V8::C::Isolate.New).to be expect(V8::C::Isolate.New).to be
end end
@ -14,43 +12,4 @@ describe V8::C::Isolate do
expect(isolate_one.Equals(isolate_one)).to eq true expect(isolate_one.Equals(isolate_one)).to eq true
expect(isolate_one.Equals(isolate_two)).to eq false expect(isolate_one.Equals(isolate_two)).to eq false
end 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 end

View file

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

View file

@ -4,13 +4,13 @@ describe V8::C::Object do
requires_v8_context requires_v8_context
it 'can create a new object' do it 'can create a new object' do
expect(V8::C::Object.New).to be expect(V8::C::Object.New(@isolate)).to be
end end
it 'can store and retrieve a value' do it 'can store and retrieve a value' do
o = V8::C::Object.New o = V8::C::Object.New(@isolate)
key = V8::C::String.NewFromUtf8('foo') key = V8::C::String.NewFromUtf8(@isolate, 'foo')
value = V8::C::String.NewFromUtf8('bar') value = V8::C::String.NewFromUtf8(@isolate, 'bar')
o.Set(key, value) o.Set(key, value)
expect(o.Get(key).Utf8Value).to eq 'bar' expect(o.Get(key).Utf8Value).to eq 'bar'
@ -48,10 +48,11 @@ describe V8::C::Object do
# end # end
it 'always returns the same ruby object for the same V8 object' do it 'always returns the same ruby object for the same V8 object' do
one = V8::C::Object.New key = V8::C::String.NewFromUtf8(@isolate, 'two')
two = V8::C::Object.New one = V8::C::Object.New(@isolate)
two = V8::C::Object.New(@isolate)
one.Set('two', two) one.Set(key, two)
expect(one.Get('two')).to be two expect(one.Get(key)).to be two
end end
end end

View file

@ -4,34 +4,26 @@ describe V8::C::String do
requires_v8_context requires_v8_context
it 'is a primitive' do 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 end
it 'can create new strings' do it 'can create new strings' do
expect(V8::C::String.NewFromUtf8('test')).to be expect(V8::C::String.NewFromUtf8(@isolate, 'test')).to be
end end
it 'can be converted to a ruby string' do 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 end
it 'can hold Unicode values outside the Basic Multilingual Plane' do 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}" expect(string.Utf8Value).to eq "\u{100000}"
end end
it 'can concatenate strings' do it 'can concatenate strings' do
string_one = V8::C::String.NewFromUtf8('Hello ') string_one = V8::C::String.NewFromUtf8(@isolate, 'Hello ')
string_two = V8::C::String.NewFromUtf8('World!') string_two = V8::C::String.NewFromUtf8(@isolate, 'World!')
expect(V8::C::String.Concat(string_one, string_two).Utf8Value).to eq 'Hello World!' expect(V8::C::String.Concat(string_one, string_two).Utf8Value).to eq 'Hello World!'
end 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 end

View file

@ -3,42 +3,38 @@ require 'c_spec_helper'
describe V8::C::Value do describe V8::C::Value do
requires_v8_context requires_v8_context
def to_value(value) def convert(value)
object = V8::C::Object.New V8::C::Value.FromRubyObject(@isolate, value).ToRubyObject
key = V8::C::String.NewFromUtf8('key')
object.Set(key, value)
object.Get(key)
end end
it 'converts strings' do it 'converts strings' do
expect(to_value(V8::C::String.NewFromUtf8('value')).Utf8Value).to eq 'value' expect(convert('value').Utf8Value).to eq 'value'
end end
it 'converts nil' do it 'converts nil' do
expect(to_value(nil)).to eq nil expect(convert(nil)).to eq nil
end end
it 'converts undefined to nil' do it 'converts undefined to nil' do
object = V8::C::Object.New object = V8::C::Object.New(@isolate)
key = V8::C::String.NewFromUtf8('key') key = V8::C::String.NewFromUtf8(@isolate, 'key')
expect(object.Get(key)).to eq nil expect(object.Get(key)).to eq nil
end end
it 'converts FixNums' do it 'converts FixNums' do
expect(to_value(42)).to eq 42 expect(convert(42)).to eq 42
end end
it 'converts booleans' do it 'converts booleans' do
expect(to_value(true)).to eq true expect(convert(true)).to eq true
expect(to_value(false)).to eq false expect(convert(false)).to eq false
end end
it 'converts objects' do it 'converts objects' do
object = V8::C::Object.New object = V8::C::Object.New(@isolate)
object.Set(V8::C::String.NewFromUtf8('test'), 1) object.Set(V8::C::String.NewFromUtf8(@isolate, 'test'), 1)
expect(to_value(object)).to eq object expect(convert(object)).to eq object
end end
end end

View file

@ -11,28 +11,18 @@ module V8ContextHelpers
end end
def bootstrap_v8_context def bootstrap_v8_context
cleanup_isolates @isolate = V8::C::Isolate.New
isolate = V8::C::Isolate.New V8::C::HandleScope(@isolate) do
@cxt = V8::C::Context::New(@isolate)
V8::C::Locker(isolate) do begin
isolate.Enter @cxt.Enter
V8::C::HandleScope(isolate) do yield
@cxt = V8::C::Context::New(isolate) ensure
begin @cxt.Exit
@cxt.Enter
yield
ensure
@cxt.Exit
end
end end
isolate.Exit
end end
end end
def cleanup_isolates
V8::C::Isolate.GetCurrent.Exit while V8::C::Isolate.GetCurrent
end
end end
RSpec.configure do |c| RSpec.configure do |c|