diff --git a/lib/v8.rb b/lib/v8.rb index 2fd05fc..ee9dded 100644 --- a/lib/v8.rb +++ b/lib/v8.rb @@ -6,9 +6,9 @@ module V8 require 'v8/v8' #native glue require 'v8/portal' require 'v8/portal/caller' - require 'v8/portal/functions' require 'v8/portal/proxies' require 'v8/portal/templates' + require 'v8/portal/function' require 'v8/portal/constructor' require 'v8/portal/interceptors' require 'v8/context' diff --git a/lib/v8/portal.rb b/lib/v8/portal.rb index 9dee4c9..f3f3eaf 100644 --- a/lib/v8/portal.rb +++ b/lib/v8/portal.rb @@ -6,7 +6,6 @@ module V8 def initialize(context, access) @context, @access = context, access @proxies = Proxies.new - @functions = Functions.new(self) @templates = Templates.new(self) @interceptors = Interceptors.new(self) @caller = Caller.new(self) @@ -44,7 +43,7 @@ module V8 C::String::NewSymbol(value.to_s) when Proc,Method,UnboundMethod @proxies.rb2js(value) do - @functions.to_function(value).function + @templates.to_function(value).function end when ::Array C::Array::New(value.length).tap do |a| diff --git a/lib/v8/portal/function.rb b/lib/v8/portal/function.rb new file mode 100644 index 0000000..2247c06 --- /dev/null +++ b/lib/v8/portal/function.rb @@ -0,0 +1,46 @@ +module V8 + class Portal + class Function + + attr_reader :template, :function + + def initialize(portal, code) + @portal = portal + @template = V8::C::FunctionTemplate::New(code.respond_to?(:call) ? Call.new(portal) : BindAndCall.new(portal), code) + end + + def function + @template.GetFunction() + end + + class Call + def initialize(portal) + @portal = portal + end + + def call(arguments) + proc = arguments.Data() + rbargs = [] + for i in 0..arguments.Length() - 1 + rbargs << @portal.rb(arguments[i]) + end + @portal.caller.invoke(proc, *rbargs) + end + end + + class BindAndCall < Call + def call(arguments) + method = arguments.Data() + rbargs = [] + for i in 0..arguments.Length() - 1 + rbargs << @portal.rb(arguments[i]) + end + this = @portal.rb(arguments.This()) + @portal.caller.protect do + method.bind(this).call(*rbargs) + end + end + end + end + end +end \ No newline at end of file diff --git a/lib/v8/portal/functions.rb b/lib/v8/portal/functions.rb deleted file mode 100644 index c790515..0000000 --- a/lib/v8/portal/functions.rb +++ /dev/null @@ -1,60 +0,0 @@ -module V8 - class Portal - class Functions - def initialize(portal) - @portal = portal - @procs = {} - @methods = {} - end - - def to_function(code) - case code - when Method, UnboundMethod - #TODO: clear this reference when the C::FunctionTemplate becomes weak. - @methods[code.to_s] ||= Template.new(@portal, code.kind_of?(Method) ? :invoke_callable : :invoke_unbound_method, code) - else - if code.respond_to?(:call) - #TODO: clear with reference when the C::FunctionTemplate becomes weak. - @procs[code] ||= Template.new(@portal, :invoke_callable, code) - else - fail "invalid code type: #{code.class}" - end - end - end - - class Template - attr_reader :template, :function - - def initialize(portal, impl, code) - @portal = portal - @template = V8::C::FunctionTemplate::New(method(impl), code) - end - - def function - @template.GetFunction() - end - - def invoke_callable(arguments) - proc = arguments.Data() - rbargs = [] - for i in 0..arguments.Length() - 1 - rbargs << @portal.rb(arguments[i]) - end - @portal.caller.invoke(proc, *rbargs) - end - - def invoke_unbound_method(arguments) - method = arguments.Data() - rbargs = [] - for i in 0..arguments.Length() - 1 - rbargs << @portal.rb(arguments[i]) - end - this = @portal.rb(arguments.This()) - @portal.caller.protect do - method.bind(this).call(*rbargs) - end - end - end - end - end -end \ No newline at end of file diff --git a/lib/v8/portal/templates.rb b/lib/v8/portal/templates.rb index 8e472a0..08df963 100644 --- a/lib/v8/portal/templates.rb +++ b/lib/v8/portal/templates.rb @@ -8,6 +8,8 @@ module V8 def initialize(portal) @portal = portal @constructors = {} + @methods = {} + @procs = {} end def to_constructor(ruby_class) @@ -15,12 +17,35 @@ module V8 if constructor = @constructors[class_id] return constructor else - constructor = @constructors[class_id] = Portal::Constructor.new(self, class_id) + constructor = @constructors[class_id] = Constructor.new(self, class_id) ObjectSpace.define_finalizer(ruby_class, bind(@constructors, :delete, class_id)) return constructor end end + def to_function(code) + case code + when Method, UnboundMethod + if fn = @methods[code.to_s] + return fn + else + function = @methods[code.to_s] = Function.new(@portal, code) + #TODO: test this weak behavior + function.template.MakeWeak(0, bind(@methods, :delete, code.to_s)) + return function + end + else + if fn = @procs[code] + return fn + else + function = Function.new(@portal, code) + #TODO: test this weak behavior + function.template.MakeWeak(0, bind(@procs, :delete, code)) + return function + end + end + end + def proxies @portal.proxies end