From 92352f5be3ae54b88b88d2bff648e17264f574b9 Mon Sep 17 00:00:00 2001 From: Charles Lowell Date: Mon, 2 Nov 2009 21:03:56 -0600 Subject: [PATCH] refactor the context out into its own header and impl file. Establish linkage between javascript objects and their ruby peers --- ruby_data.h | 3 +- spec/therubyracer_spec.rb | 80 ++++++++++++++++++++++----------------- v8.cpp | 66 +++----------------------------- v8_context.cpp | 46 ++++++++++++++++++++++ v8_context.h | 45 ++++++++++++++++++++++ v8_data.cpp | 9 ----- v8_data.h | 32 ---------------- v8_object.cpp | 36 ++++++++++++------ v8_object.h | 6 ++- 9 files changed, 174 insertions(+), 149 deletions(-) create mode 100644 v8_context.cpp create mode 100644 v8_context.h delete mode 100644 v8_data.cpp diff --git a/ruby_data.h b/ruby_data.h index 2d6e29f..1043519 100644 --- a/ruby_data.h +++ b/ruby_data.h @@ -91,7 +91,8 @@ class RubyDest { } VALUE pushObject(v8::Handle& value, const char* name = 0) { - return Data_Wrap_Struct(rb_cV8_JSObject, v8_object_mark, v8_object_free, new v8_object(value)); + v8_object* wrapper = new v8_object(value); + return wrapper->ruby_value; } }; diff --git a/spec/therubyracer_spec.rb b/spec/therubyracer_spec.rb index b948ad8..37a209c 100644 --- a/spec/therubyracer_spec.rb +++ b/spec/therubyracer_spec.rb @@ -44,53 +44,63 @@ describe "The Ruby Racer" do it "can pass objects back to ruby" do eval("({foo: 'bar', baz: 'bang', '5': 5, segfault: {}})").tap do |object| - object.should_not be_nil + object.should_not be_nil object['foo'].should == 'bar' object['baz'].should == 'bang' object['5'].should == 5 object['segfault'].should be_kind_of(V8::JSObject) end - end + end end - describe "Passing Objects from Ruby to Javascript" do - - it "can pass strings to javascript" do - eval("this", "foo").should == "foo" - end + describe "Calling Ruby Code from JavaScript" do - it "can pass large strings from ruby to javascript" do - lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis faucibus, diam vel pellentesque aliquet, nisl sapien molestie eros, vitae vehicula libero massa vel neque. Phasellus tempor pharetra ipsum vel venenatis. Quisque vitae nisl vitae quam mattis pellentesque et in sapien. Sed at lectus quis eros pharetra feugiat non ac neque. Vivamus lacus eros, feugiat at volutpat at, viverra id nisl. Vivamus ac dolor eleifend libero venenatis pharetra ut iaculis arcu. Donec neque nibh, vehicula non porta a, consectetur eu erat. Sed eleifend, metus vel euismod placerat, lectus lectus sollicitudin nisl, ac elementum sem quam nec dolor. In hac habitasse platea dictumst. Proin vitae suscipit orci. Suspendisse a ipsum vel lorem tempus scelerisque et vitae neque. Proin sodales, tellus sit amet consequat cursus, odio massa ultricies enim, eu fermentum velit lectus in lacus. Quisque eu porttitor diam. Nunc felis purus, facilisis non tristique ac, pulvinar nec nulla. Duis dolor risus, egestas nec tristique ac, ullamcorper cras amet." - eval("this", lorem).should == lorem - end - - it "can pass the empty string from ruby to javascript" do - eval("this", "").should == "" - end - - it "can pass doubles from ruby to javascript" do - eval("this", 3.14).should == 3.14 - end - - it "can pass integers from ruby to javascript" do - eval("this", 5).should == 5 - end - - it "can pass boolean values from ruby to javascript" do - eval("this", true).should be(true) - eval("this", false).should be(false) - end - - it "can pass an object value from ruby to javascript" do - V8::JSObject.new.tap do |o| - eval("this", o).should be(o) + it "can embed a ruby closure and call it from javascript" do + pending + V8::JSObject.new.tap do |scope| + scope['times'] = lambda {|lhs, rhs| lhs * rhs} + eval('times(5,2)', scope).should == 10 end end - - end - def eval(str) + it "can call a bound ruby method" do + pending + mult = Class.new.class_eval do + self.tap do + def initialize(lhs) + @lhs + end + + def times(rhs) + @lhs * rhs + end + end + end + V8::JSObject.new.tap do |scope| + scope['timesfive'] = mult.new(5).method(:times) + eval('timesfive(3)', scope).should == 15 + end + end + + it "can embed a ruby object and call its methods" do + pending + V8::JSObject.new.tap do |scope| + scope['object'] = Class.new.instance_eval { + self.tap do + def times(lhs, rhs) + lhs * rhs + end + end + }.new + eval('object.times(8,8)', scope) + end + end + end + + describe "Passing Ruby Values Back from Javascript" + + def eval(str, scope = nil) @cxt.eval(str) end diff --git a/v8.cpp b/v8.cpp index bb5c18c..a6f7622 100644 --- a/v8.cpp +++ b/v8.cpp @@ -1,20 +1,9 @@ #include "ruby_data.h" -#include "v8_data.h" #include "generic_data.h" -#include +#include "v8_data.h" +#include "v8_context.h" -/** - * struct that encapsulates a v8 context. this - * object's lifetime matches the lifetime of - * the ruby v8 context item. - */ -typedef struct v8_context { - v8_context() : context(v8::Context::New()) {} - ~v8_context() { - context.Dispose(); - } - v8::Persistent context; -} v8_context; +#include extern "C" { /** @@ -23,12 +12,6 @@ extern "C" { void Init_v8(); } -VALUE v8_allocate(VALUE clazz); -void v8_mark(v8_context *s); -void v8_free(v8_context *s); - -VALUE eval(VALUE self, VALUE javascript); - VALUE rb_mModule; VALUE rb_cV8; @@ -36,49 +19,12 @@ extern "C" { void Init_v8() { rb_mModule = rb_define_module("V8"); rb_cV8 = rb_define_class_under(rb_mModule, "Context", rb_cObject); - rb_define_alloc_func(rb_cV8, v8_allocate); - rb_define_method(rb_cV8, "eval", (VALUE(*)(...)) eval, 1); + rb_define_alloc_func(rb_cV8, v8_context_allocate); + rb_define_method(rb_cV8, "eval", (VALUE(*)(...)) v8_context_eval, 1); rb_cV8_JSObject = rb_define_class_under(rb_mModule, "JSObject", rb_cObject); rb_define_alloc_func(rb_cV8_JSObject, v8_object_allocate); rb_define_method(rb_cV8_JSObject, "[]", (VALUE(*)(...)) v8_object_hash_access, 1); - // rb_define_method(rb_cV8_JSObject, "[]=", (VALUE(*)(...)) v8_object_hash_assignment, 2); + rb_define_method(rb_cV8_JSObject, "[]=", (VALUE(*)(...)) v8_object_hash_assignment, 2); } } - -VALUE v8_allocate(VALUE clazz) { - v8_context *s = new v8_context; - return Data_Wrap_Struct(clazz, v8_mark, v8_free, s); -} - -void v8_mark(v8_context *s) { - // marked for gc? -} - -void v8_free(v8_context *s) { - delete s; -} - -/** - * eval a javascript. Currently, return - * types can only be primitive types. - * @param self ruby handle to a v8_context struct - * @param javascript, should be a string - * @return the result of evaluating the script, if - * it can be converted - */ -VALUE eval(VALUE self, VALUE javascript) { - v8_context* s = 0; - Data_Get_Struct(self, struct v8_context, s); - v8::Context::Scope context_scope(s->context); - v8::HandleScope handle_scope; - - RubyValueSource tostring; - const std::string text(tostring.push(javascript)); - v8::Handle source = v8::String::New(text.c_str()); - v8::Handle script = v8::Script::Compile(source); - - v8::Handle local_result = script->Run(); - V8HandleSource toValue; - return toValue.push(local_result); -} diff --git a/v8_context.cpp b/v8_context.cpp new file mode 100644 index 0000000..f30f550 --- /dev/null +++ b/v8_context.cpp @@ -0,0 +1,46 @@ + +#include +#include +#include +#include + +using namespace v8; + +v8_context::v8_context() : handle(Context::New()) {} + +v8_context::~v8_context() { + handle.Dispose(); +} + +VALUE v8_context_allocate(VALUE clazz) { + v8_context *cxt = new v8_context; + return Data_Wrap_Struct(clazz, v8_context_mark, v8_context_free, cxt); + +} +void v8_context_free(v8_context *context) { + delete context; +} +void v8_context_mark(v8_context *context) { + //don't mark anything. +} + + +//methods +VALUE v8_context_eval(VALUE self, VALUE javascript) { + v8_context* cxt = 0; + Data_Get_Struct(self, struct v8_context, cxt); + + Context::Scope enter(cxt->handle); + HandleScope handles; + + RubyValueSource tostring; + const std::string source(tostring.push(javascript)); + + Local