diff --git a/lib/v8/access.rb b/lib/v8/access.rb index 31c5018..f52c4a4 100644 --- a/lib/v8/access.rb +++ b/lib/v8/access.rb @@ -1,6 +1,89 @@ require 'set' module V8 - + 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| + t.InstanceTemplate().SetNamedPropertyHandler( + NamedPropertyGetter, + NamedPropertySetter, + nil, + nil, + NamedPropertyEnumerator + ) + if cls.name && cls.name =~ /(::)?(\w+?)$/ + t.SetClassName(C::String::NewSymbol($2)) + 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| + t.SetNamedPropertyHandler( + NamedPropertyGetter, + NamedPropertySetter, + nil, + nil, + NamedPropertyEnumerator + ) + end + end + end + + class Constructors < Access + def self.[](cls) + @constructors ||= Constructors.new + @constructors[cls] + end + + def template(cls) + t = C::FunctionTemplate::New() do |arguments| + rbargs = [] + for i in 0..arguments.Length() - 1 + rbargs << To.rb(arguments[i]) + end + instance = V8::Function.rubycall(cls.method(:new), *rbargs) + arguments.This().tap do |this| + this.SetHiddenValue(C::String::NewSymbol("TheRubyRacer::RubyObject"), C::External::New(instance)) + end + end + t.Inherit(Access[cls]) + return t + end + end class NamedPropertyGetter def self.call(property, info) name = To.rb(property) @@ -41,7 +124,7 @@ module V8 end end end - + class NamedPropertyEnumerator def self.call(info) obj = To.rb(info.This()) diff --git a/lib/v8/context.rb b/lib/v8/context.rb index be4f37b..18b8bbd 100644 --- a/lib/v8/context.rb +++ b/lib/v8/context.rb @@ -4,7 +4,7 @@ module V8 class Context attr_reader :native, :scope def initialize(opts = {}) - @native = opts[:with] ? C::Context::New(To.template) : C::Context::New() + @native = opts[:with] ? C::Context::New(Access.rubyobject) : C::Context::New() @native.enter do @native.Global().SetHiddenValue(C::String::New("TheRubyRacer::RubyObject"), C::External::New(opts[:with])) if opts[:with] @scope = To.rb(@native.Global()) diff --git a/lib/v8/to.rb b/lib/v8/to.rb index 49981af..5093e3f 100644 --- a/lib/v8/to.rb +++ b/lib/v8/to.rb @@ -19,8 +19,10 @@ module V8 case value when V8::Object value.instance_eval {@native} - when String, Symbol + when String C::String::New(value.to_s) + when Symbol + C::String::NewSymbol(value.to_s) when Proc,Method template = C::FunctionTemplate::New() do |arguments| rbargs = [] @@ -45,7 +47,7 @@ module V8 when ::Time C::Date::New(value) when ::Class - To.class_template(value).GetFunction().tap do |f| + Constructors[value].GetFunction().tap do |f| f.SetHiddenValue(C::String::NewSymbol("TheRubyRacer::RubyObject"), C::External::New(value)) end when nil,Numeric,TrueClass,FalseClass, C::Value @@ -53,7 +55,7 @@ module V8 else args = C::Array::New(1) args.Set(0, C::External::New(value)) - obj = To.class_template(value.class).GetFunction().NewInstance(args) + obj = Access[value.class].GetFunction().NewInstance(args) return obj end end @@ -67,60 +69,6 @@ module V8 end end - def template - @rubyobject ||= C::ObjectTemplate::New().tap do |t| - t.SetNamedPropertyHandler( - NamedPropertyGetter, - NamedPropertySetter, - nil, - nil, - NamedPropertyEnumerator - ) - end - end - - def class_template(cls) - @classes ||= {} - if ref = @classes[cls.object_id] - if ref.weakref_alive? - ref.__getobj__ - else - @classes.delete(cls.object_id) - self.class_template(cls) - end - else - class_template = C::FunctionTemplate::New() do |arguments| - if arguments.Length() > 0 && arguments[0].kind_of?(C::External) - wrapper = arguments[0] - else - rbargs = [] - for i in 0..arguments.Length() - 1 - rbargs << To.rb(arguments[i]) - end - instance = V8::Function.rubycall(cls.method(:new), *rbargs) - wrapper = C::External::New(instance) - end - arguments.This().tap do |this| - this.SetHiddenValue(C::String::NewSymbol("TheRubyRacer::RubyObject"), wrapper) - end - end - class_template.InstanceTemplate().SetNamedPropertyHandler( - NamedPropertyGetter, - NamedPropertySetter, - nil, - nil, - NamedPropertyEnumerator - ) - if cls.name && cls.name =~ /(::)?(\w+?)$/ - class_template.SetClassName(C::String::NewSymbol($2)) - else - class_template.SetClassName("Ruby") - end - @classes[cls.object_id] = WeakRef.new(class_template) - class_template - end - end - def camel_case(str) str.to_s.gsub(/_(\w)/) {$1.upcase} end