rubyjs / therubyracer

Embed the V8 Javascript Interpreter into Ruby
1.66k stars 192 forks source link

Global template class is missing built in objects #303

Closed calh closed 10 years ago

calh commented 10 years ago

Hello,

I'm trying to use a custom class for my global namespace, and I've found that using the :with argument to Context destroys the built in standard javascript objects. Here's a short snippet to show what I mean:

c = V8::Context.new(:with => Object.new)
1.9.3-p545 :032 > c.eval("var d = new Date();")
 => nil 
1.9.3-p545 :033 > c['d']
 => nil 

c = V8::Context.new
1.9.3-p545 :035 > c.eval("var d = new Date();")
 => nil 
1.9.3-p545 :036 > c['d']
 => 2014-06-16 14:56:16 -0500 

Do I need to do something special to my custom global class to include Date, String, Array, etc?

Thank you!

calh commented 10 years ago

Just a follow up, I think I found a workaround for this, if anyone is interested. First, the global object needs to respond to [] and []= like a Hash would. The issue there, is that the custom defined getter/setter array methods would receive variable names like String and Date.

/lib/v8/access/names.rb has a get() method which yields if the [] method is not defined on the global template object. But, yielding inside my class methods didn't seem to work. They would throw an exception "no block given".

So, I atttempted to monkey patch the special? method, and that seems to work.

module V8
  Access.class_eval do
    private
    def special?(name=nil)
      @special ||= lambda do |m| 
        m == "[]" || m == "[]=" || m =~ /=$/ ||
          m == "String" || m == "Date" || m == "Array" ||
          m == "Number" || m == "RegExp" || m == "Object" ||
          m == "Boolean" || m == "Math"
      end
      name.nil? ? @special : @special[name]
    end
  end
end # end module V8

I probably missed a few of the standard built in types there, but you get the point. After defining these built in names as being special, the get method yields instead of calling my getter, and the builtin js classes are used.

Is there a more elegant way to solve this problem?

cowboyd commented 10 years ago

This is the expected behavior.

The thing is, if you're using a custom scope, it's up to you to provide implementation of those object. If you yield from [] it will pass the object as undefined.

calh commented 10 years ago

Is there any way to easily wrap or mix in V8's implementation of the standard types in my global class? (Array, String, Date, RegExp, etc)

cowboyd commented 10 years ago

What if you just added the properties directly to the context's global object?

context = V8::Context.new
context['foo'] = Object.new