From 176bf1ffe9bb07d9257ba9ae7f92312bc91269bb Mon Sep 17 00:00:00 2001 From: kares Date: Wed, 11 Jan 2012 08:28:06 +0100 Subject: [PATCH] introduced Function#bind for the Ruby side; setting this to nil does not end up as undefined - there seems no way to pass undefined as a function context thus pass the global object (mirroring non-strict mode) --- lib/rhino/rhino_ext.rb | 16 +++++++++++++-- spec/rhino/rhino_ext_spec.rb | 39 ++++++++++++++++++++++++++++++------ 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/lib/rhino/rhino_ext.rb b/lib/rhino/rhino_ext.rb index 51ca2f3..f9b330d 100644 --- a/lib/rhino/rhino_ext.rb +++ b/lib/rhino/rhino_ext.rb @@ -152,11 +152,22 @@ class Java::OrgMozillaJavascript::BaseFunction # make JavaScript functions callable Ruby style e.g. `fn.call('42')` def call(*args) context = Rhino::JS::Context.enter; scope = current_scope(context) - __call__(context, scope, nil, Rhino.args_to_javascript(args, scope)) + # calling as a (var) stored function - no this === undefined "use strict" + # TODO can't pass Undefined.instance as this - it's not a Scriptable !? + this = Rhino::JS::ScriptRuntime.getGlobal(context) + __call__(context, scope, this, Rhino.args_to_javascript(args, scope)) ensure Rhino::JS::Context.exit end + def bind(this, *args) + context = Rhino::JS::Context.enter; scope = current_scope(context) + args = Rhino.args_to_javascript(args, scope) + Rhino::JS::BoundFunction.new(context, scope, self, Rhino.to_javascript(this), args) + ensure + Rhino::JS::Context.exit + end + # use JavaScript functions constructors from Ruby as `fn.new` def new(*args) context = Rhino::JS::Context.enter; scope = current_scope(context) @@ -167,7 +178,8 @@ class Java::OrgMozillaJavascript::BaseFunction def methodcall(this, *args) context = Rhino::JS::Context.enter; scope = current_scope(context) - __call__(context, scope, Rhino.to_javascript(this), Rhino.args_to_javascript(args, scope)) + args = Rhino.args_to_javascript(args, scope) + __call__(context, scope, Rhino.to_javascript(this), args) ensure Rhino::JS::Context.exit end diff --git a/spec/rhino/rhino_ext_spec.rb b/spec/rhino/rhino_ext_spec.rb index 505e31f..0cff4b6 100644 --- a/spec/rhino/rhino_ext_spec.rb +++ b/spec/rhino/rhino_ext_spec.rb @@ -118,14 +118,14 @@ describe "NativeFunction" do before do factory = Rhino::JS::ContextFactory.new - context, scope = nil, nil + @context, @scope = nil, nil factory.call do |ctx| - context = ctx - scope = context.initStandardObjects(nil, false) + @context = ctx + @scope = @context.initStandardObjects(nil, false) end - factory.enterContext(context) + factory.enterContext(@context) - object = context.newObject(scope) + object = @context.newObject(@scope) @object = Rhino::JS::ScriptableObject.getProperty(object, 'toString') @object.instance_eval do def to_h_properties @@ -141,7 +141,34 @@ describe "NativeFunction" do it_should_behave_like 'ScriptableObject' it 'is callable' do - @object.call.should == '[object Object]' + # NOTE: no implicit or bound this thus this === global + @object.call.should == '[object global]' + end + + it 'might be bind and called' do + @object.bind(@object).should be_a(Rhino::JS::Function) + @object.bind(@object).call.should == '[object Function]' + end + + it 'might be bind to a this context' do + @object.bind(@object).should be_a(Rhino::JS::Function) + @object.bind(@object).call.should == '[object Function]' + end + + it 'might be bind to a this with args' do + array = @context.newArray(@scope, [].to_java) + push = Rhino::JS::ScriptableObject.getProperty(array, 'push') + + this = @context.newArray(@scope, [ 0 ].to_java) + push.bind(this, 1, 2).call(3, 4) + + this.length.should == 5 + 5.times { |i| this.get(i, this).should == i } + end + + it 'might be method-called' do + an_obj = @context.newObject(@scope) + @object.methodcall(an_obj).should == '[object Object]' end end