From 56c5558be71ba394e76fcf0b0bf340770c5256b5 Mon Sep 17 00:00:00 2001 From: Charles Lowell Date: Thu, 14 Jan 2010 00:51:46 +0200 Subject: [PATCH] native C objects now have a reference to the context that created them. Add identity/equality check for C::Context --- ext/v8/converters.cpp | 5 ++++- ext/v8/v8.cpp | 16 ++++++++++------ ext/v8/v8_cxt.cpp | 26 +++++++++++++++++++++++++- ext/v8/v8_cxt.h | 3 +++ ext/v8/v8_obj.cpp | 7 +++++++ ext/v8/v8_obj.h | 1 + lib/v8/context.rb | 24 ++++++++++++++++-------- lib/v8/object.rb | 8 ++++++-- spec/ext/cxt_spec.rb | 25 +++++++++++++++++++++++++ spec/ext/obj_spec.rb | 13 +++++++++++++ spec/redjs | 2 +- 11 files changed, 111 insertions(+), 19 deletions(-) create mode 100644 spec/ext/cxt_spec.rb create mode 100644 spec/ext/obj_spec.rb diff --git a/ext/v8/converters.cpp b/ext/v8/converters.cpp index f7202c3..d1f23c4 100644 --- a/ext/v8/converters.cpp +++ b/ext/v8/converters.cpp @@ -2,6 +2,7 @@ #include "callbacks.h" #include "v8_ref.h" #include "v8_obj.h" +#include "v8_cxt.h" using namespace v8; @@ -32,7 +33,9 @@ VALUE V82RB(Handle& value) { Local object(Object::Cast(*value)); Local peer = object->GetHiddenValue(String::New("TheRubyRacer::RubyObject")); if (peer.IsEmpty()) { - return V8_Ref_Create(V8_C_Object, value); + VALUE context_ref = V8_Ref_Create(V8_C_Context, Context::GetCurrent()); + object->SetHiddenValue(String::New("TheRubyRacer::Context"), External::Wrap((void *)context_ref)); + return V8_Ref_Create(V8_C_Object, value, context_ref); } else { return (VALUE)External::Unwrap(peer); } diff --git a/ext/v8/v8.cpp b/ext/v8/v8.cpp index 684dd6f..76f2a03 100644 --- a/ext/v8/v8.cpp +++ b/ext/v8/v8.cpp @@ -37,12 +37,15 @@ extern "C" { VALUE rb_mNative = rb_define_module_under(rb_mModule, "C"); //native context - VALUE V8__C__Context = rb_define_class_under(rb_mNative, "Context", rb_cObject); - rb_define_singleton_method(V8__C__Context, "new", (VALUE(*)(...)) v8_Context_New, -1); - rb_define_singleton_method(V8__C__Context, "InContext", (VALUE(*)(...)) v8_Context_InContext, 0); - rb_define_method(V8__C__Context, "Global", (VALUE(*)(...)) v8_cxt_Global, 0); - rb_define_method(V8__C__Context, "open", (VALUE(*)(...)) v8_cxt_open, 0); - rb_define_method(V8__C__Context, "eval", (VALUE(*)(...)) v8_cxt_eval, 1); + V8_C_Context = rb_define_class_under(rb_mNative, "Context", rb_cObject); + rb_define_singleton_method(V8_C_Context, "new", (VALUE(*)(...)) v8_Context_New, -1); + rb_define_singleton_method(V8_C_Context, "InContext", (VALUE(*)(...)) v8_Context_InContext, 0); + rb_define_singleton_method(V8_C_Context, "GetCurrent", (VALUE(*)(...)) v8_Context_GetCurrent, 0); + rb_define_method(V8_C_Context, "Global", (VALUE(*)(...)) v8_cxt_Global, 0); + rb_define_method(V8_C_Context, "open", (VALUE(*)(...)) v8_cxt_open, 0); + rb_define_method(V8_C_Context, "eval", (VALUE(*)(...)) v8_cxt_eval, 1); + rb_define_method(V8_C_Context, "eql?", (VALUE(*)(...)) v8_cxt_eql, 1); + rb_define_method(V8_C_Context, "==", (VALUE(*)(...)) v8_cxt_eql, 1); //native String VALUE V8__C__String = rb_define_class_under(rb_mNative, "String", rb_cObject); @@ -68,6 +71,7 @@ extern "C" { rb_define_method(V8_C_Object, "Get", (VALUE(*)(...))v8_Object_Get, 1); rb_define_method(V8_C_Object, "Set", (VALUE(*)(...))v8_Object_Set, 2); rb_define_method(V8_C_Object, "GetPropertyNames", (VALUE(*)(...)) v8_Object_GetPropertyNames, 0); + rb_define_method(V8_C_Object, "context", (VALUE(*)(...)) v8_Object_context, 0); V8_C_Message = rb_define_class_under(rb_mNative, "Message", rb_cObject); rb_define_method(V8_C_Message, "Get", (VALUE(*)(...))v8_Message_Get, 0); diff --git a/ext/v8/v8_cxt.cpp b/ext/v8/v8_cxt.cpp index fa77f3e..88aaa98 100644 --- a/ext/v8/v8_cxt.cpp +++ b/ext/v8/v8_cxt.cpp @@ -4,6 +4,8 @@ using namespace v8; +VALUE V8_C_Context; + //TODO: rename everything to Context_ //TODO: do the object init from within here @@ -24,7 +26,17 @@ VALUE v8_Context_New(int argc, VALUE *argv, VALUE self) { } VALUE v8_Context_InContext(VALUE self) { - return Context::InContext() ? Qtrue : Qnil; + return Context::InContext() ? Qtrue : Qfalse; +} + +VALUE v8_Context_GetCurrent(VALUE self) { + HandleScope handles; + if (Context::InContext()) { + Local current = Context::GetCurrent(); + return V8_Ref_Create(self, current); + } else { + return Qnil; + } } VALUE v8_cxt_Global(VALUE self) { @@ -60,4 +72,16 @@ VALUE v8_cxt_eval(VALUE self, VALUE source) { } } +VALUE v8_cxt_eql(VALUE self, VALUE other) { + HandleScope handles; + if (RTEST(CLASS_OF(other) != V8_C_Context)) { + return Qnil; + } else { + Local cxt = V8_Ref_Get(self); + Local that = V8_Ref_Get(other); + return cxt == that ? Qtrue : Qfalse; + } + return Qnil; +} + diff --git a/ext/v8/v8_cxt.h b/ext/v8/v8_cxt.h index 14b26f7..eb3480f 100644 --- a/ext/v8/v8_cxt.h +++ b/ext/v8/v8_cxt.h @@ -7,11 +7,14 @@ extern VALUE rb_cV8; extern VALUE V8_C_Object; +extern VALUE V8_C_Context; VALUE v8_Context_New(int argc, VALUE *argv, VALUE self); VALUE v8_Context_InContext(VALUE self); +VALUE v8_Context_GetCurrent(VALUE self); VALUE v8_cxt_Global(VALUE self); VALUE v8_cxt_open(VALUE self); VALUE v8_cxt_eval(VALUE self, VALUE source); +VALUE v8_cxt_eql(VALUE self, VALUE other); #endif \ No newline at end of file diff --git a/ext/v8/v8_obj.cpp b/ext/v8/v8_obj.cpp index 4af8760..8dd0bbf 100644 --- a/ext/v8/v8_obj.cpp +++ b/ext/v8/v8_obj.cpp @@ -42,3 +42,10 @@ VALUE v8_Object_GetPropertyNames(VALUE self) { Local names = object->GetPropertyNames(); return V82RB(names); } + +VALUE v8_Object_context(VALUE self) { + HandleScope handles; + Local object = unwrap(self); + Local cxt = object->GetHiddenValue(String::New("TheRubyRacer::Context")); + return cxt.IsEmpty() ? Qnil : (VALUE)External::Unwrap(cxt); +} diff --git a/ext/v8/v8_obj.h b/ext/v8/v8_obj.h index 32d5f8d..bf39f74 100644 --- a/ext/v8/v8_obj.h +++ b/ext/v8/v8_obj.h @@ -9,4 +9,5 @@ VALUE v8_Object_New(VALUE clazz); VALUE v8_Object_Get(VALUE self, VALUE key); VALUE v8_Object_Set(VALUE self, VALUE key, VALUE value); VALUE v8_Object_GetPropertyNames(VALUE self); +VALUE v8_Object_context(VALUE self); #endif \ No newline at end of file diff --git a/lib/v8/context.rb b/lib/v8/context.rb index c1c5a07..f246740 100644 --- a/lib/v8/context.rb +++ b/lib/v8/context.rb @@ -7,9 +7,15 @@ module V8 end def open(&block) - @native.open do - block.call(self) - end if block_given? + if block_given? + unless @native == C::Context::GetCurrent() + @native.open do + block.call(self) + end + else + block.call(self) + end + end end def eval(javascript, sourcename = '', line = 1) @@ -33,15 +39,17 @@ module V8 end def [](key) - ContextError.check_open('V8::Context#[]') - To.ruby(@native.Global().Get(key.to_s)) + open do + To.ruby(@native.Global().Get(key.to_s)) + end end def []=(key, value) - ContextError.check_open('V8::Context#[]=') value.tap do - @native.Global().tap do |scope| - scope.Set(key.to_s, value) + open do + @native.Global().tap do |scope| + scope.Set(key.to_s, value) + end end end end diff --git a/lib/v8/object.rb b/lib/v8/object.rb index 2700ec6..8c8286b 100644 --- a/lib/v8/object.rb +++ b/lib/v8/object.rb @@ -8,12 +8,16 @@ module V8 end def [](key) - To.ruby(@native.Get(key.to_s)) + @native.context.open do + To.ruby(@native.Get(key.to_s)) + end end def []=(key, value) value.tap do - @native.Set(key.to_s, value) + @native.context.open do + @native.Set(key.to_s, value) + end end end diff --git a/spec/ext/cxt_spec.rb b/spec/ext/cxt_spec.rb new file mode 100644 index 0000000..a172f7c --- /dev/null +++ b/spec/ext/cxt_spec.rb @@ -0,0 +1,25 @@ + +require "#{File.dirname(__FILE__)}/../spec_helper.rb" + +include V8 + +describe C::Context do + + it "should not have a current context if no context is open" do + C::Context::GetCurrent().should be_nil + end + + it "can determine if there is a current context" do + C::Context::InContext().should be(false) + C::Context.new.open do |cxt| + C::Context::InContext().should be(true) + end + end + + it "returns the currently open context" do + C::Context.new.open do |cxt| + cxt.should be_eql(C::Context::GetCurrent()) + cxt.should == C::Context::GetCurrent() + end + end +end \ No newline at end of file diff --git a/spec/ext/obj_spec.rb b/spec/ext/obj_spec.rb new file mode 100644 index 0000000..557dd6f --- /dev/null +++ b/spec/ext/obj_spec.rb @@ -0,0 +1,13 @@ +require "#{File.dirname(__FILE__)}/../spec_helper.rb" + +include V8 + +describe C::Object do + it "has a reference to its calling context" do + C::Context.new.open do |cxt| + o = cxt.eval('new Object()'); + o.context.should == cxt + o.context.should be_eql(cxt) + end + end +end \ No newline at end of file diff --git a/spec/redjs b/spec/redjs index 915d204..260ff99 160000 --- a/spec/redjs +++ b/spec/redjs @@ -1 +1 @@ -Subproject commit 915d20496c7af6bbc1c9f2ae4c794fd5031cd56c +Subproject commit 260ff99e34670cedbd03867a6da6f581512cc2cc