From 72d181ecdc11fce0a9e2d3f27f076b8a2264a507 Mon Sep 17 00:00:00 2001 From: Charles Lowell Date: Tue, 19 Apr 2011 10:09:24 -0500 Subject: [PATCH] add support for managing ruby proxies. Currently 1.9.2 only. --- lib/v8/portal/proxies.rb | 24 ++++++++ spec/v8/portal/proxies_spec.rb | 100 +++++++++++++++++++++++++-------- 2 files changed, 101 insertions(+), 23 deletions(-) diff --git a/lib/v8/portal/proxies.rb b/lib/v8/portal/proxies.rb index d9e7492..b8c54c0 100644 --- a/lib/v8/portal/proxies.rb +++ b/lib/v8/portal/proxies.rb @@ -5,6 +5,8 @@ module V8 def initialize @js_proxies_rb2js = {} @js_proxies_js2rb = {} + @rb_proxies_rb2js = {} + @rb_proxies_js2rb = {} end def register_javascript_proxy(proxy, options = {}) @@ -24,6 +26,22 @@ module V8 @js_proxies_js2rb[proxy] end + def register_ruby_proxy(proxy, options = {}) + target = options[:for] or fail ArgumentError, "must specify the object that you're proxying with the :for => param" + fail ArgumentError, "javascript proxy must be a Handle to an actual V8 object" unless target.kind_of?(V8::C::Handle) + @rb_proxies_rb2js[proxy.object_id] = target + @rb_proxies_js2rb[target] = proxy.object_id + ObjectSpace.define_finalizer(proxy, method(:clear_rb_proxy)) + end + + def js_object_2_rb_proxy(object) + ObjectSpace._id2ref @rb_proxies_js2rb[object] + end + + def rb_proxy_2_js_object(proxy) + @rb_proxies_rb2js[proxy.object_id] + end + def clear_js_proxy(proxy) lambda do rb = @js_proxies_js2rb[proxy] @@ -31,6 +49,12 @@ module V8 @js_proxies_rb2js.delete(rb) end end + + def clear_rb_proxy(proxy_id) + js = @rb_proxies_rb2js[proxy_id] + @rb_proxies_rb2js.delete(proxy_id) + @rb_proxies_js2rb.delete(js) + end end end end \ No newline at end of file diff --git a/spec/v8/portal/proxies_spec.rb b/spec/v8/portal/proxies_spec.rb index 4b744c4..ec37aaa 100644 --- a/spec/v8/portal/proxies_spec.rb +++ b/spec/v8/portal/proxies_spec.rb @@ -6,11 +6,11 @@ describe V8::Portal::Proxies do context "for Ruby objects which are embedded into javascript" do it "allows you to resolve the Ruby object's JavaScript proxy" do - proxy = c::Object::New() - object = Object.new - subject.register_javascript_proxy proxy, :for => object - subject.rb_object_2_js_proxy(object).should be(proxy) - subject.js_proxy_2_rb_object(proxy).should be(object) + js_proxy = c::Object::New() + rb_object = Object.new + subject.register_javascript_proxy js_proxy, :for => rb_object + subject.rb_object_2_js_proxy(rb_object).should be(js_proxy) + subject.js_proxy_2_rb_object(js_proxy).should be(rb_object) end it "requires a Ruby object which is the actual object that is proxied" do @@ -20,35 +20,89 @@ describe V8::Portal::Proxies do it "can only register proxies which are low-level JavaScript objects" do expect {subject.register_javascript_proxy Object.new, :for => Object.new}.should raise_error(ArgumentError) end - it "holds a hard reference to any ruby object which is linked to a javascript proxy" do - object = Object.new - ObjectSpace.define_finalizer(object, method(:finalize)) - subject.register_javascript_proxy c::Object::New(), :for => object - object = nil - afterwards {@finalized.should be_false} - end - it "releases the hard reference if its corresponding javascript object has been garbage collected" do - object = Object.new - proxy = c::Object::New() - ObjectSpace.define_finalizer(object, method(:finalize)) - subject.register_javascript_proxy proxy, :for => object - ruby_gc do - object = nil - v8_gc() - afterwards {@finalized.should be_true} + context "Memory Management" do + it "holds a hard reference to any ruby object which is linked to a javascript proxy" do + rb_object = Object.new + check_not_finalized(rb_object) + subject.register_javascript_proxy c::Object::New(), :for => rb_object + rb_object = nil + end + + it "releases the hard reference if its corresponding javascript object has been garbage collected" do + rb_object = Object.new + js_proxy = c::Object::New() + check_finalized(rb_object) + subject.register_javascript_proxy js_proxy, :for => rb_object + rb_object = nil + ruby_gc do + v8_gc() + end end end end context "for a JavaScript objects which are embedded into Ruby" do - it "holds a weak referece to any javascript object which is " + + it "allows you to resolve the JavaScript object's Ruby proxy" do + rb_proxy = Object.new + js_object = c::Object::New() + subject.register_ruby_proxy rb_proxy, :for => js_object + subject.js_object_2_rb_proxy(js_object).should be(rb_proxy) + subject.rb_proxy_2_js_object(rb_proxy).should be(js_object) + end + + it "requires a JavaScript low level javascript object as the actual object that is proxied" do + expect {subject.register_javascript_proxy Object.new, :for => c::Object::New()}.should raise_error(ArgumentError) + end + + context "Memory Management" do + + it "holds a hard reference to any JavaScript object which is linked to a Ruby proxy" do + js_object = c::Object::New() + check_not_finalized(js_object) + subject.register_ruby_proxy Object.new, :for => js_object + js_object = nil + end + + it "clears any strong references to the JavaScript object when it's Ruby proxy is garbage collected" do + js_object = c::Object::New() + rb_proxy = Object.new + subject.register_ruby_proxy rb_proxy, :for => js_object + check_finalized(js_object) + js_object = rb_proxy = nil + ruby_gc do + v8_gc + end + end + end end private def finalize(object_id) - @finalized = true + @finalized ||= {} + @finalized[object_id] = true + end + + def check_finalized(object) + @finalized ||= {} + ObjectSpace.define_finalizer(object, method(:finalize)) + id_to_check = object.object_id + object = nil + afterwards do + @finalized[id_to_check].should be_true + end + end + + def check_not_finalized(object) + @finalized ||= {} + ObjectSpace.define_finalizer(object, method(:finalize)) + id_to_check = object.object_id + object = nil + afterwards do + @finalized[id_to_check].should be_false + end end def afterwards(&block)