2010-05-26 12:54:56 -04:00
|
|
|
require 'set'
|
2010-05-22 05:12:42 -04:00
|
|
|
module V8
|
2010-06-08 17:59:22 -04:00
|
|
|
|
|
|
|
#TODO each context should get its own access rules instead of sharing them across
|
|
|
|
# contetxts
|
|
|
|
###### --cowboyd 07/07/2010
|
2010-06-08 17:32:31 -04:00
|
|
|
class Access
|
|
|
|
def self.[](cls)
|
|
|
|
@access ||= Access.new
|
|
|
|
@access[cls]
|
|
|
|
end
|
|
|
|
|
|
|
|
def initialize
|
|
|
|
@classes = {}
|
|
|
|
end
|
|
|
|
|
|
|
|
def [](cls)
|
|
|
|
@classes ||= {}
|
|
|
|
if ref = @classes[cls.object_id]
|
|
|
|
if ref.weakref_alive?
|
|
|
|
ref.__getobj__
|
|
|
|
else
|
|
|
|
@classes.delete(cls.object_id)
|
|
|
|
self[cls]
|
|
|
|
end
|
|
|
|
else
|
|
|
|
template(cls).tap do |t|
|
2010-06-29 00:38:23 -04:00
|
|
|
Access.setuptemplate(t.InstanceTemplate())
|
2010-06-08 17:32:31 -04:00
|
|
|
if cls.name && cls.name =~ /(::)?(\w+?)$/
|
2010-06-08 17:59:22 -04:00
|
|
|
t.SetClassName(C::String::NewSymbol("rb::" + $2))
|
2010-06-08 17:32:31 -04:00
|
|
|
else
|
|
|
|
t.SetClassName("Ruby")
|
|
|
|
end
|
|
|
|
@classes[cls.object_id] = WeakRef.new(t)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def template(cls)
|
|
|
|
C::FunctionTemplate::New() do |arguments|
|
|
|
|
unless arguments.Length() == 1 && arguments[0].kind_of?(C::External)
|
|
|
|
C::ThrowException(C::Exception::Error(C::String::New("cannot call native constructor from javascript")))
|
|
|
|
else
|
|
|
|
arguments.This().tap do |this|
|
|
|
|
this.SetHiddenValue(C::String::NewSymbol("TheRubyRacer::RubyObject"), arguments[0])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.rubyobject
|
|
|
|
@rubyobject ||= C::ObjectTemplate::New().tap do |t|
|
2010-06-29 00:38:23 -04:00
|
|
|
setuptemplate(t)
|
2010-06-08 17:32:31 -04:00
|
|
|
end
|
|
|
|
end
|
2010-06-29 00:38:23 -04:00
|
|
|
|
|
|
|
def self.setuptemplate(t)
|
|
|
|
t.SetNamedPropertyHandler(
|
|
|
|
NamedPropertyGetter,
|
|
|
|
NamedPropertySetter,
|
|
|
|
nil,
|
|
|
|
nil,
|
|
|
|
NamedPropertyEnumerator
|
|
|
|
)
|
|
|
|
t.SetIndexedPropertyHandler(
|
|
|
|
IndexedPropertyGetter,
|
|
|
|
IndexedPropertySetter,
|
|
|
|
nil,
|
|
|
|
nil,
|
|
|
|
IndexedPropertyEnumerator
|
|
|
|
)
|
|
|
|
end
|
2010-06-08 17:32:31 -04:00
|
|
|
end
|
2010-07-08 11:10:19 -04:00
|
|
|
module AccessibleMethods
|
2010-08-07 01:53:08 -04:00
|
|
|
def ruby
|
|
|
|
@ruby ||= RubyAccess.new
|
2010-07-08 11:10:19 -04:00
|
|
|
end
|
2010-08-06 11:23:13 -04:00
|
|
|
def access(info, retval = nil, &code)
|
|
|
|
obj = To.rb(info.This())
|
|
|
|
intercepts = true
|
|
|
|
result = Function.rubyprotect do
|
2010-08-06 12:17:17 -04:00
|
|
|
dontintercept = proc do
|
2010-08-06 11:23:13 -04:00
|
|
|
intercepts = false
|
|
|
|
end
|
2010-08-06 12:17:17 -04:00
|
|
|
code.call(obj, dontintercept)
|
2010-08-06 11:23:13 -04:00
|
|
|
end
|
|
|
|
intercepts ? (retval || result) : C::Empty
|
|
|
|
end
|
2010-07-08 11:10:19 -04:00
|
|
|
end
|
|
|
|
|
2010-08-07 01:53:08 -04:00
|
|
|
class RubyAccess
|
|
|
|
def get(obj, name, &dontintercept)
|
2010-06-28 12:02:54 -04:00
|
|
|
methods = accessible_methods(obj)
|
2010-06-28 09:36:41 -04:00
|
|
|
if methods.include?(name)
|
|
|
|
method = obj.method(name)
|
2010-08-06 11:23:13 -04:00
|
|
|
method.arity == 0 ? method.call : method
|
2010-06-29 01:22:41 -04:00
|
|
|
elsif obj.respond_to?(:[])
|
2010-08-06 11:23:13 -04:00
|
|
|
obj.send(:[], name, &dontintercept)
|
2010-05-22 05:12:42 -04:00
|
|
|
else
|
2010-08-06 11:23:13 -04:00
|
|
|
yield
|
2010-05-22 05:12:42 -04:00
|
|
|
end
|
|
|
|
end
|
2010-08-07 01:53:08 -04:00
|
|
|
|
|
|
|
def iget(obj, index, &dontintercept)
|
|
|
|
if obj.respond_to?(:[])
|
|
|
|
obj.send(:[], index, &dontintercept)
|
|
|
|
else
|
|
|
|
yield
|
2010-08-06 11:23:13 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2010-08-07 01:53:08 -04:00
|
|
|
def set(obj, name, value, &dontintercept)
|
2010-06-29 01:22:41 -04:00
|
|
|
setter = name + "="
|
2010-06-28 12:02:54 -04:00
|
|
|
methods = accessible_methods(obj)
|
|
|
|
if methods.include?(setter)
|
2010-08-06 11:23:13 -04:00
|
|
|
obj.send(setter, value)
|
2010-06-29 01:22:41 -04:00
|
|
|
elsif obj.respond_to?(:[]=)
|
2010-08-06 11:23:13 -04:00
|
|
|
obj.send(:[]=, name, value, &dontintercept)
|
2010-05-22 05:12:42 -04:00
|
|
|
else
|
2010-08-06 11:23:13 -04:00
|
|
|
yield
|
2010-05-22 05:12:42 -04:00
|
|
|
end
|
|
|
|
end
|
2010-08-07 01:53:08 -04:00
|
|
|
|
|
|
|
def iset(obj, index, value, &dontintercept)
|
|
|
|
if obj.respond_to?(:[]=)
|
|
|
|
obj.send(:[]=, index, value, &dontintercept)
|
|
|
|
else
|
|
|
|
yield
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2010-08-16 16:20:04 -04:00
|
|
|
def query(obj, name, attributes)
|
|
|
|
if obj.respond_to?(name)
|
|
|
|
attributes.dont_delete
|
|
|
|
unless obj.respond_to?(name + "=")
|
|
|
|
attributes.read_only
|
|
|
|
end
|
|
|
|
else
|
|
|
|
yield
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def iquery(obj, index, attributes)
|
|
|
|
if obj.respond_to?(:[])
|
|
|
|
attributes.dont_delete
|
|
|
|
unless obj.respond_to?(:[]=)
|
|
|
|
attributes.read_only
|
|
|
|
end
|
|
|
|
else
|
|
|
|
yield
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def names(obj)
|
|
|
|
obj.public_methods(false).map {|m| m.to_s}.to_set.tap do |methods|
|
|
|
|
ancestors = obj.class.ancestors.dup
|
|
|
|
while ancestor = ancestors.shift
|
|
|
|
break if ancestor == ::Object
|
|
|
|
methods.merge(ancestor.public_instance_methods(false).map {|m| m.to_s})
|
|
|
|
end
|
|
|
|
methods.reject! {|m| m == "[]" || m == "[]="}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def indices(obj)
|
|
|
|
obj.respond_to?(:length) ? (0..obj.length).to_a : yield
|
|
|
|
end
|
|
|
|
|
2010-08-07 01:53:08 -04:00
|
|
|
def accessible_methods(obj)
|
|
|
|
obj.public_methods(false).map {|m| m.to_s}.to_set.tap do |methods|
|
|
|
|
ancestors = obj.class.ancestors.dup
|
|
|
|
while ancestor = ancestors.shift
|
|
|
|
break if ancestor == ::Object
|
|
|
|
methods.merge(ancestor.public_instance_methods(false).map {|m| m.to_s})
|
|
|
|
end
|
|
|
|
methods.reject! {|m| m == "[]" || m == "[]="}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class Constructors < Access
|
|
|
|
def self.[](cls)
|
|
|
|
Access[cls].tap do |template|
|
|
|
|
template.SetCallHandler() do |arguments|
|
|
|
|
wrap = nil
|
|
|
|
if arguments.Length() > 0 && arguments[0].kind_of?(C::External)
|
|
|
|
wrap = arguments[0]
|
|
|
|
else
|
|
|
|
rbargs = []
|
|
|
|
for i in 0..arguments.Length() - 1
|
|
|
|
rbargs << To.rb(arguments[i])
|
|
|
|
end
|
|
|
|
instance = V8::Function.rubysend(cls, :new, *rbargs)
|
|
|
|
wrap = C::External::New(instance)
|
|
|
|
end
|
|
|
|
arguments.This().tap do |this|
|
|
|
|
this.SetHiddenValue(C::String::NewSymbol("TheRubyRacer::RubyObject"), wrap)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class NamedPropertyGetter
|
|
|
|
extend AccessibleMethods
|
|
|
|
def self.call(property, info)
|
|
|
|
access(info) do |obj, dontintercept|
|
|
|
|
ruby.get(obj, To.rb(property), &dontintercept)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class NamedPropertySetter
|
|
|
|
extend AccessibleMethods
|
|
|
|
def self.call(property, value, info)
|
|
|
|
access(info, value) do |obj, dontintercept|
|
|
|
|
ruby.set(obj, To.rb(property), To.rb(value), &dontintercept)
|
|
|
|
end
|
|
|
|
end
|
2010-05-22 05:12:42 -04:00
|
|
|
end
|
2010-06-08 17:32:31 -04:00
|
|
|
|
2010-08-06 13:10:20 -04:00
|
|
|
class NamedPropertyQuery
|
|
|
|
extend AccessibleMethods
|
|
|
|
class Attrs
|
|
|
|
def initialize
|
|
|
|
@flags = 0
|
|
|
|
end
|
|
|
|
|
|
|
|
def read_only
|
|
|
|
tap do
|
|
|
|
@flags |= V8::C::ReadOnly
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def dont_enum
|
|
|
|
tap do
|
|
|
|
@flags |= V8::C::DontEnum
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def dont_delete
|
|
|
|
tap do
|
|
|
|
@flags |= V8::C::DontDelete
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.call(property, info)
|
|
|
|
attrs = Attrs.new
|
|
|
|
intercepts = true
|
|
|
|
access_query(To.rb(info.This()), To.rb(property), attrs) do
|
|
|
|
intercepts = false
|
|
|
|
end
|
|
|
|
intercepts ? C::Integer::New(attrs.flags) : C::Empty
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.access_attributes(obj, name, attrs)
|
|
|
|
unless obj.respond_to?(name)
|
|
|
|
attrs.dont_enum
|
|
|
|
attrs.dont_delete
|
|
|
|
end
|
|
|
|
unless obj.respond_to?("name=")
|
|
|
|
attrs.read_only
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2010-05-22 05:12:42 -04:00
|
|
|
class NamedPropertyEnumerator
|
2010-07-08 11:10:19 -04:00
|
|
|
extend AccessibleMethods
|
2010-05-22 05:12:42 -04:00
|
|
|
def self.call(info)
|
|
|
|
obj = To.rb(info.This())
|
2010-08-07 01:53:08 -04:00
|
|
|
methods = ruby.accessible_methods(obj)
|
2010-08-26 00:49:44 -04:00
|
|
|
methods.reject! {|m| m.to_s =~ /=$/}
|
2010-06-28 12:02:54 -04:00
|
|
|
names = V8::C::Array::New(methods.length)
|
|
|
|
methods.each_with_index do |name, i|
|
2010-05-22 05:12:42 -04:00
|
|
|
names.Set(i, C::String::New(name))
|
|
|
|
end
|
|
|
|
return names
|
|
|
|
end
|
|
|
|
end
|
2010-06-29 00:38:23 -04:00
|
|
|
|
|
|
|
class IndexedPropertyGetter
|
2010-08-06 11:23:13 -04:00
|
|
|
extend AccessibleMethods
|
2010-06-29 00:38:23 -04:00
|
|
|
def self.call(index, info)
|
2010-08-06 12:17:17 -04:00
|
|
|
access(info) do |obj, dontintercept|
|
2010-08-07 01:53:08 -04:00
|
|
|
ruby.iget(obj, index, &dontintercept)
|
2010-06-29 00:38:23 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class IndexedPropertySetter
|
2010-08-06 11:23:13 -04:00
|
|
|
extend AccessibleMethods
|
2010-06-29 00:38:23 -04:00
|
|
|
def self.call(index, value, info)
|
2010-08-06 12:17:17 -04:00
|
|
|
access(info, value) do |obj, dontintercept|
|
2010-08-07 01:53:08 -04:00
|
|
|
ruby.iset(obj, index, To.rb(value), &dontintercept)
|
2010-06-29 00:38:23 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class IndexedPropertyEnumerator
|
|
|
|
def self.call(info)
|
|
|
|
obj = To.rb(info.This())
|
|
|
|
if obj.respond_to?(:length)
|
|
|
|
C::Array::New(obj.length).tap do |indices|
|
|
|
|
for index in 0..obj.length - 1
|
|
|
|
indices.Set(index,index)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
else
|
|
|
|
C::Array::New()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2010-08-06 11:23:13 -04:00
|
|
|
end
|