Search code examples
rubyv8therubyracer

Method closure not seeming to work in therubyracer


Once I store a method in a V8::Context, all subsequent instances of that method stored in any context under any name behave like the initial one (i.e. as if the original instance had been stored again).

I tried to isolate/demonstrate the problem with the following:

require 'V8'

class TestClass
  def test_method
    self.inspect
  end
end

(1..2).each do |cxt_i|
  cxt = V8::Context.new
  [:test_method, :test_method2].each_with_index do |method_name, method_i|
    method = TestClass.new.method(:test_method)
    cxt[method_name.to_s] = method
    script = method_name.to_s+'()'
    puts "Context #{cxt_i}, method #{method_i+1} result is #{method.call}, V8 returns #{cxt.eval(script)}"
  end
end

Which produces the following output:

Context 1, method 1 result is #<TestClass:0x007fce2419cdb0>, V8 returns #<TestClass:0x007fce2419cdb0>
Context 1, method 2 result is #<TestClass:0x007fce2419b780>, V8 returns #<TestClass:0x007fce2419cdb0>
Context 2, method 1 result is #<TestClass:0x007fce2419abc8>, V8 returns #<TestClass:0x007fce2419cdb0>
Context 2, method 2 result is #<TestClass:0x007fce24199a98>, V8 returns #<TestClass:0x007fce2419cdb0>

Solution

  • It's a weak reference issue. By inserting a GC.start inside the inner loop, we force the weak references in the V8 context to be garbage collected. Slows it down immensely though.

    require 'v8'
    
    class TestClass
      def test_method
        self.inspect
      end
    end
    
    (1..2).each do |cxt_i|
      cxt = V8::Context.new
      [:test_method, :test_method2].each_with_index do |method_name, method_i|
        method = TestClass.new.method(:test_method)
        cxt[method_name.to_s] = method
        script = method_name.to_s+'()'
        puts "Context #{cxt_i}, method #{method_i+1} result is #{method.call}, V8 returns #{cxt.eval(script)}"
        GC.start # <<<<=========
      end
    end
    

    Output:

    Context 1, method 1 result is #<TestClass:0x007f8f13a26cd8>, V8 returns #<TestClass:0x007f8f13a26cd8>
    Context 1, method 2 result is #<TestClass:0x007f8f135cca48>, V8 returns #<TestClass:0x007f8f135cca48>
    Context 2, method 1 result is #<TestClass:0x007f8f135ceac8>, V8 returns #<TestClass:0x007f8f135ceac8>
    Context 2, method 2 result is #<TestClass:0x007f8f135cdbf0>, V8 returns #<TestClass:0x007f8f135cdbf0>
    

    ruby -v is ruby 2.0.0p195 (2013-05-14 revision 40734) [x86_64-linux], libv8 is 3.11.8.17 x86_64-linux