mirror of
https://github.com/rubyjs/therubyracer
synced 2023-03-27 23:21:42 -04:00
keep a back reference to object wrappers
This commit is contained in:
parent
eb9b7b7490
commit
0d29c7fc15
5 changed files with 105 additions and 0 deletions
56
ext/v8/backref.cc
Normal file
56
ext/v8/backref.cc
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
#include "rr.h"
|
||||||
|
|
||||||
|
namespace rr {
|
||||||
|
|
||||||
|
VALUE Backref::WeakRef;
|
||||||
|
ID Backref::_new;
|
||||||
|
ID Backref::__getobj__;
|
||||||
|
ID Backref::__setobj__;
|
||||||
|
ID Backref::__weakref_alive;
|
||||||
|
|
||||||
|
void Backref::Init() {
|
||||||
|
WeakRef = rb_eval_string("require 'weakref'; WeakRef");
|
||||||
|
rb_gc_register_address(&WeakRef);
|
||||||
|
_new = rb_intern("new");
|
||||||
|
__getobj__ = rb_intern("__getobj__");
|
||||||
|
__setobj__ = rb_intern("__setobj__");
|
||||||
|
__weakref_alive = rb_intern("weakref_alive?");
|
||||||
|
}
|
||||||
|
|
||||||
|
Backref::Backref(VALUE initial) {
|
||||||
|
this->ref = rb_funcall(WeakRef, _new, 1, initial);
|
||||||
|
rb_gc_register_address(&ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
Backref::~Backref() {
|
||||||
|
rb_gc_unregister_address(&ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
VALUE Backref::get() {
|
||||||
|
if (alive_p()) {
|
||||||
|
return rb_funcall(ref, __getobj__, 0);
|
||||||
|
} else {
|
||||||
|
return Qnil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Backref::set(VALUE value) {
|
||||||
|
rb_funcall(ref, __setobj__, 1, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Backref::alive_p() {
|
||||||
|
return RTEST(rb_funcall(ref, __weakref_alive, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
v8::Handle<v8::Value> Backref::to_external() {
|
||||||
|
v8::Local<v8::Value> wrapper = v8::External::Wrap(this);
|
||||||
|
v8::Persistent<v8::Value>::New(wrapper).MakeWeak(this, &release);
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Backref::release(v8::Persistent<v8::Value> handle, void* data) {
|
||||||
|
handle.Dispose();
|
||||||
|
Backref* backref = (Backref*)data;
|
||||||
|
delete backref;
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,5 +33,6 @@ extern "C" {
|
||||||
Locker::Init();
|
Locker::Init();
|
||||||
ResourceConstraints::Init();
|
ResourceConstraints::Init();
|
||||||
HeapStatistics::Init();
|
HeapStatistics::Init();
|
||||||
|
Backref::Init();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -132,6 +132,27 @@ VALUE Object::SetAccessor(int argc, VALUE* argv, VALUE self) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Object::operator VALUE() {
|
Object::operator VALUE() {
|
||||||
|
VALUE value;
|
||||||
|
Backref* backref;
|
||||||
|
v8::Local<v8::String> key(v8::String::NewSymbol("rr::Backref"));
|
||||||
|
v8::Local<v8::Value> holder = handle->GetHiddenValue(key);
|
||||||
|
if (holder.IsEmpty()) {
|
||||||
|
value = downcast();
|
||||||
|
backref = new Backref(value);
|
||||||
|
handle->SetHiddenValue(key, backref->to_external());
|
||||||
|
} else {
|
||||||
|
backref = (Backref*)v8::External::Unwrap(holder);
|
||||||
|
if (backref->alive_p()) {
|
||||||
|
value = backref->get();
|
||||||
|
} else {
|
||||||
|
value = downcast();
|
||||||
|
backref->set(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
VALUE Object::downcast() {
|
||||||
if (handle->IsFunction()) {
|
if (handle->IsFunction()) {
|
||||||
return Function((v8::Handle<v8::Function>) v8::Function::Cast(*handle));
|
return Function((v8::Handle<v8::Function>) v8::Function::Cast(*handle));
|
||||||
}
|
}
|
||||||
|
|
21
ext/v8/rr.h
21
ext/v8/rr.h
|
@ -155,6 +155,24 @@ public:
|
||||||
};
|
};
|
||||||
template <class T> VALUE Ref<T>::Class;
|
template <class T> VALUE Ref<T>::Class;
|
||||||
|
|
||||||
|
class Backref {
|
||||||
|
public:
|
||||||
|
static void Init();
|
||||||
|
Backref(VALUE initial);
|
||||||
|
virtual ~Backref();
|
||||||
|
VALUE get();
|
||||||
|
void set(VALUE);
|
||||||
|
bool alive_p();
|
||||||
|
v8::Handle<v8::Value> to_external();
|
||||||
|
static void release(v8::Persistent<v8::Value> handle, void* data);
|
||||||
|
private:
|
||||||
|
VALUE ref;
|
||||||
|
static VALUE WeakRef;
|
||||||
|
static ID _new;
|
||||||
|
static ID __getobj__;
|
||||||
|
static ID __setobj__;
|
||||||
|
static ID __weakref_alive;
|
||||||
|
};
|
||||||
class Handles {
|
class Handles {
|
||||||
public:
|
public:
|
||||||
static void Init();
|
static void Init();
|
||||||
|
@ -492,6 +510,9 @@ public:
|
||||||
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::Handle<v8::Object> object) : Ref<v8::Object>(object) {}
|
||||||
virtual operator VALUE();
|
virtual operator VALUE();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
VALUE downcast();
|
||||||
};
|
};
|
||||||
|
|
||||||
class Array : public Ref<v8::Array> {
|
class Array : public Ref<v8::Array> {
|
||||||
|
|
|
@ -37,4 +37,10 @@ describe V8::C::Object do
|
||||||
o.Set(property, V8::C::String::New("Bro! "))
|
o.Set(property, V8::C::String::New("Bro! "))
|
||||||
o.Get(property).Utf8Value().should eql "Bro! I am Legend"
|
o.Get(property).Utf8Value().should eql "Bro! I am Legend"
|
||||||
end
|
end
|
||||||
|
it "always returns the same ruby object for the same V8 object" do
|
||||||
|
one = V8::C::Object::New()
|
||||||
|
two = V8::C::Object::New()
|
||||||
|
one.Set("two", two)
|
||||||
|
one.Get("two").should be two
|
||||||
|
end
|
||||||
end
|
end
|
Loading…
Add table
Reference in a new issue