1
0
Fork 0
mirror of https://github.com/rubyjs/therubyracer synced 2023-03-27 23:21:42 -04:00

Embed lambdas and procs into V8::Context

This adds the ability embed basic Ruby code blocks into a javascript
context and invoke it.

  cxt = V8::Context.new
  cxt['twice'] = lambda {|str| "#{str}#{str}"}
  cxt.eval('twice("hi")') #=> "hihi"

Note that error handling is not accounted for yet. If an exception
happens inside the called proc, it will cause terrible, awful things
to happen because portions of the C++ stack will not be unwound leaving
the Isolate in an indeterminate state.
This commit is contained in:
Charles Lowell 2015-08-12 21:02:39 +03:00
parent d4c68dfc62
commit 250b8e7d50
3 changed files with 67 additions and 31 deletions

View file

@ -140,3 +140,28 @@ class Symbol
V8::C::Symbol::For(context.isolate.native, V8::C::String::NewFromUtf8(isolate, to_s)) V8::C::Symbol::For(context.isolate.native, V8::C::String::NewFromUtf8(isolate, to_s))
end end
end end
class Object
def to_v8(context)
V8::C::Object::New(context.isolate.native)
end
end
class Proc
def to_v8(context)
isolate = context.isolate.native
callback = lambda do |info|
args = []
arity = info.Length()
if self.arity > -1
arity = self.arity
end
arity.times do |i|
args << context.to_ruby(info[i])
end
result = context.to_v8 self.call(*args)
info.GetReturnValue().Set(result)
end
V8::C::Function::New(isolate, callback)
end
end

View file

@ -43,7 +43,18 @@ class V8::Conversion
# First it checks to see if there is an entry in the id map for # First it checks to see if there is an entry in the id map for
# this object. Otherwise, it will run the default conversion. # this object. Otherwise, it will run the default conversion.
def to_v8(context, ruby_object) def to_v8(context, ruby_object)
rb_idmap[ruby_object.object_id] || ruby_object.to_v8(context) if v8_object = rb_idmap[ruby_object.object_id]
v8_object
else
v8_object = ruby_object.to_v8(context)
if v8_object.kind_of? V8::C::Object
v8_object.tap do
equate ruby_object, v8_object
end
else
v8_object
end
end
end end
## ##

View file

@ -93,12 +93,12 @@ describe "V8::Context" do
end end
end end
# xit "unwraps ruby objects returned by embedded ruby code to maintain referential integrity" do it "unwraps ruby objects returned by embedded ruby code to maintain referential integrity" do
# Object.new.tap do |o| Object.new.tap do |o|
# @cxt['get'] = lambda {o} @cxt['get'] = lambda {o}
# @cxt.eval('get()').should be(o) @cxt.eval('get()').should be(o)
# end end
# end end
it "always returns the same ruby object for a single javascript object" do it "always returns the same ruby object for a single javascript object" do
obj = @cxt.eval('obj = {}') obj = @cxt.eval('obj = {}')
@ -133,25 +133,25 @@ describe "V8::Context" do
# end # end
end end
# describe "Calling Ruby Code From Within Javascript", :compat => '0.1.0' do describe "Calling Ruby Code From Within Javascript" do
# before(:each) do before(:each) do
# @class = Class.new # @class = Class.new
# @instance = @class.new # @instance = @class.new
# @cxt = V8::Context.new @cxt = V8::Context.new
# @cxt['o'] = @instance # @cxt['o'] = @instance
# end end
# xit "can embed a closure into a context and call it" do it "can embed a closure into a context and call it" do
# @cxt["say"] = lambda { |*args| args[-2] * args[-1] } @cxt["say"] = lambda { |*args| args[-2] * args[-1] }
# @cxt.eval("say('Hello', 2)").should == "HelloHello" @cxt.eval("say('Hello', 2)").should == "HelloHello"
# end end
# xit "recognizes the same closure embedded into the same context as the same function object" do it "recognizes the same closure embedded into the same context as the same function object" do
# @cxt['say'] = @cxt['declare'] = lambda { |*args| args } @cxt['say'] = @cxt['declare'] = lambda { |*args| args }
# @cxt.eval('say == declare').should be(true) @cxt.eval('say == declare').should be(true)
# @cxt.eval('say === declare').should be(true) @cxt.eval('say === declare').should be(true)
# end end
# xit "translates ruby Array to Javascript Array" do # xit "translates ruby Array to Javascript Array" do
# class_eval do # class_eval do
@ -275,17 +275,17 @@ describe "V8::Context" do
# @cxt.eval('typeof(RObject)').should == 'function' # @cxt.eval('typeof(RObject)').should == 'function'
# end # end
# xit "truncates lambda arguments passed in to match the arity of the function", :compat => '0.4.2' do it "truncates lambda arguments passed in to match the arity of the function", :compat => '0.4.2' do
# @cxt['testing'] = lambda { |arg| arg } @cxt['testing'] = lambda { |arg| arg }
# lambda { lambda {
# @cxt.eval('testing(1,2,3)') @cxt.eval('testing(1,2,3)')
# }.should_not raise_error }.should_not raise_error
# @cxt['testing'] = lambda { } @cxt['testing'] = lambda { }
# lambda { lambda {
# @cxt.eval('testing(1,2,3)') @cxt.eval('testing(1,2,3)')
# }.should_not raise_error }.should_not raise_error
# end end
# xit "truncates method arguments passed in to match the arity of the function", :compat => '0.4.3' do # xit "truncates method arguments passed in to match the arity of the function", :compat => '0.4.3' do
# @instance.instance_eval do # @instance.instance_eval do
@ -652,7 +652,7 @@ describe "V8::Context" do
# @class.class_eval &body # @class.class_eval &body
# end # end
# end end
describe "Calling JavaScript Code From Within Ruby", :compat => '0.1.0' do describe "Calling JavaScript Code From Within Ruby", :compat => '0.1.0' do