1// Copyright 2010 the V8 project authors. All rights reserved. 2// Redistribution and use in source and binary forms, with or without 3// modification, are permitted provided that the following conditions are 4// met: 5// 6// * Redistributions of source code must retain the above copyright 7// notice, this list of conditions and the following disclaimer. 8// * Redistributions in binary form must reproduce the above 9// copyright notice, this list of conditions and the following 10// disclaimer in the documentation and/or other materials provided 11// with the distribution. 12// * Neither the name of Google Inc. nor the names of its 13// contributors may be used to endorse or promote products derived 14// from this software without specific prior written permission. 15// 16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28// Flags: --allow-natives-syntax 29 30// Tests the Function.prototype.bind method. 31 32 33// Simple tests. 34function foo(x, y, z) { 35 return [this, arguments.length, x]; 36} 37 38assertEquals(3, foo.length); 39 40var f = foo.bind(foo); 41assertEquals([foo, 3, 1], f(1, 2, 3)); 42assertEquals(3, f.length); 43assertEquals("function () { [native code] }", f.toString()); 44 45f = foo.bind(foo, 1); 46assertEquals([foo, 3, 1], f(2, 3)); 47assertEquals(2, f.length); 48assertEquals("function () { [native code] }", f.toString()); 49 50f = foo.bind(foo, 1, 2); 51assertEquals([foo, 3, 1], f(3)); 52assertEquals(1, f.length); 53assertEquals("function () { [native code] }", f.toString()); 54 55f = foo.bind(foo, 1, 2, 3); 56assertEquals([foo, 3, 1], f()); 57assertEquals(0, f.length); 58assertEquals("function () { [native code] }", f.toString()); 59 60// Test that length works correctly even if more than the actual number 61// of arguments are given when binding. 62f = foo.bind(foo, 1, 2, 3, 4, 5, 6, 7, 8, 9); 63assertEquals([foo, 9, 1], f()); 64assertEquals(0, f.length); 65assertEquals("function () { [native code] }", f.toString()); 66 67// Use a different bound object. 68var obj = {x: 42, y: 43}; 69// Values that would normally be in "this" when calling f_bound_this. 70var x = 42; 71var y = 44; 72 73function f_bound_this(z) { 74 return z + this.y - this.x; 75} 76 77assertEquals(3, f_bound_this(1)) 78f = f_bound_this.bind(obj); 79assertEquals(2, f(1)); 80assertEquals(1, f.length); 81 82f = f_bound_this.bind(obj, 2); 83assertEquals(3, f()); 84assertEquals(0, f.length); 85assertEquals('[object Function]', Object.prototype.toString.call(f)); 86 87// Test chained binds. 88 89// When only giving the thisArg, any number of binds should have 90// the same effect. 91f = foo.bind(foo); 92assertEquals([foo, 3, 1], f(1, 2, 3)); 93 94var not_foo = {}; 95f = foo.bind(foo).bind(not_foo).bind(not_foo).bind(not_foo); 96assertEquals([foo, 3, 1], f(1, 2, 3)); 97assertEquals(3, f.length); 98 99// Giving bound parameters should work at any place in the chain. 100f = foo.bind(foo, 1).bind(not_foo).bind(not_foo).bind(not_foo); 101assertEquals([foo, 3, 1], f(2, 3)); 102assertEquals(2, f.length); 103 104f = foo.bind(foo).bind(not_foo, 1).bind(not_foo).bind(not_foo); 105assertEquals([foo, 3, 1], f(2, 3)); 106assertEquals(2, f.length); 107 108f = foo.bind(foo).bind(not_foo).bind(not_foo,1 ).bind(not_foo); 109assertEquals([foo, 3, 1], f(2, 3)); 110assertEquals(2, f.length); 111 112f = foo.bind(foo).bind(not_foo).bind(not_foo).bind(not_foo, 1); 113assertEquals([foo, 3, 1], f(2, 3)); 114assertEquals(2, f.length); 115 116// Several parameters can be given, and given in different bind invocations. 117f = foo.bind(foo, 1, 2).bind(not_foo).bind(not_foo).bind(not_foo); 118assertEquals([foo, 3, 1], f(3)); 119assertEquals(1, f.length); 120 121f = foo.bind(foo).bind(not_foo, 1, 2).bind(not_foo).bind(not_foo); 122assertEquals([foo, 3, 1], f(1)); 123assertEquals(1, f.length); 124 125f = foo.bind(foo).bind(not_foo, 1, 2).bind(not_foo).bind(not_foo); 126assertEquals([foo, 3, 1], f(3)); 127assertEquals(1, f.length); 128 129f = foo.bind(foo).bind(not_foo).bind(not_foo, 1, 2).bind(not_foo); 130assertEquals([foo, 3, 1], f(1)); 131assertEquals(1, f.length); 132 133f = foo.bind(foo).bind(not_foo).bind(not_foo).bind(not_foo, 1, 2); 134assertEquals([foo, 3, 1], f(3)); 135assertEquals(1, f.length); 136 137f = foo.bind(foo, 1).bind(not_foo, 2).bind(not_foo).bind(not_foo); 138assertEquals([foo, 3, 1], f(3)); 139assertEquals(1, f.length); 140 141f = foo.bind(foo, 1).bind(not_foo).bind(not_foo, 2).bind(not_foo); 142assertEquals([foo, 3, 1], f(3)); 143assertEquals(1, f.length); 144 145f = foo.bind(foo, 1).bind(not_foo).bind(not_foo).bind(not_foo, 2); 146assertEquals([foo, 3, 1], f(3)); 147assertEquals(1, f.length); 148 149f = foo.bind(foo).bind(not_foo, 1).bind(not_foo).bind(not_foo, 2); 150assertEquals([foo, 3, 1], f(3)); 151assertEquals(1, f.length); 152 153// The wrong number of arguments can be given to bound functions too. 154f = foo.bind(foo); 155assertEquals(3, f.length); 156assertEquals([foo, 0, undefined], f()); 157assertEquals([foo, 1, 1], f(1)); 158assertEquals([foo, 2, 1], f(1, 2)); 159assertEquals([foo, 3, 1], f(1, 2, 3)); 160assertEquals([foo, 4, 1], f(1, 2, 3, 4)); 161 162f = foo.bind(foo, 1); 163assertEquals(2, f.length); 164assertEquals([foo, 1, 1], f()); 165assertEquals([foo, 2, 1], f(2)); 166assertEquals([foo, 3, 1], f(2, 3)); 167assertEquals([foo, 4, 1], f(2, 3, 4)); 168 169f = foo.bind(foo, 1, 2); 170assertEquals(1, f.length); 171assertEquals([foo, 2, 1], f()); 172assertEquals([foo, 3, 1], f(3)); 173assertEquals([foo, 4, 1], f(3, 4)); 174 175f = foo.bind(foo, 1, 2, 3); 176assertEquals(0, f.length); 177assertEquals([foo, 3, 1], f()); 178assertEquals([foo, 4, 1], f(4)); 179 180f = foo.bind(foo, 1, 2, 3, 4); 181assertEquals(0, f.length); 182assertEquals([foo, 4, 1], f()); 183 184// Test constructor calls. 185 186function bar(x, y, z) { 187 this.x = x; 188 this.y = y; 189 this.z = z; 190} 191 192f = bar.bind(bar); 193var obj2 = new f(1,2,3); 194assertEquals(1, obj2.x); 195assertEquals(2, obj2.y); 196assertEquals(3, obj2.z); 197 198f = bar.bind(bar, 1); 199obj2 = new f(2,3); 200assertEquals(1, obj2.x); 201assertEquals(2, obj2.y); 202assertEquals(3, obj2.z); 203 204f = bar.bind(bar, 1, 2); 205obj2 = new f(3); 206assertEquals(1, obj2.x); 207assertEquals(2, obj2.y); 208assertEquals(3, obj2.z); 209 210f = bar.bind(bar, 1, 2, 3); 211obj2 = new f(); 212assertEquals(1, obj2.x); 213assertEquals(2, obj2.y); 214assertEquals(3, obj2.z); 215 216 217// Test bind chains when used as a constructor. 218f = bar.bind(bar, 1).bind(bar, 2).bind(bar, 3); 219obj2 = new f(); 220assertEquals(1, obj2.x); 221assertEquals(2, obj2.y); 222assertEquals(3, obj2.z); 223 224// Test obj2 is instanceof both bar and f. 225assertTrue(obj2 instanceof bar); 226assertTrue(obj2 instanceof f); 227 228// This-args are not relevant to instanceof. 229f = bar.bind(foo.prototype, 1). 230 bind(String.prototype, 2). 231 bind(Function.prototype, 3); 232var obj3 = new f(); 233assertTrue(obj3 instanceof bar); 234assertTrue(obj3 instanceof f); 235assertFalse(obj3 instanceof foo); 236assertFalse(obj3 instanceof Function); 237assertFalse(obj3 instanceof String); 238 239// thisArg is converted to object. 240f = foo.bind(undefined); 241assertEquals([this, 0, undefined], f()); 242 243f = foo.bind(null); 244assertEquals([this, 0, undefined], f()); 245 246f = foo.bind(42); 247assertEquals([Object(42), 0, undefined], f()); 248 249f = foo.bind("foo"); 250assertEquals([Object("foo"), 0, undefined], f()); 251 252f = foo.bind(true); 253assertEquals([Object(true), 0, undefined], f()); 254 255// Strict functions don't convert thisArg. 256function soo(x, y, z) { 257 "use strict"; 258 return [this, arguments.length, x]; 259} 260 261var s = soo.bind(undefined); 262assertEquals([undefined, 0, undefined], s()); 263 264s = soo.bind(null); 265assertEquals([null, 0, undefined], s()); 266 267s = soo.bind(42); 268assertEquals([42, 0, undefined], s()); 269 270s = soo.bind("foo"); 271assertEquals(["foo", 0, undefined], s()); 272 273s = soo.bind(true); 274assertEquals([true, 0, undefined], s()); 275 276// Test that .arguments and .caller are poisoned according to the ES5 spec. 277 278// Check that property descriptors are correct (unconfigurable, unenumerable, 279// and both get and set is the ThrowTypeError function). 280// 281// Poisoned accessors are no longer own properties --- get them from the 282// prototype 283var f_proto = Object.getPrototypeOf(f); 284var cdesc = Object.getOwnPropertyDescriptor(f_proto, "caller"); 285var adesc = Object.getOwnPropertyDescriptor(f_proto, "arguments"); 286 287assertFalse(cdesc.enumerable); 288assertTrue(cdesc.configurable); 289 290assertFalse(adesc.enumerable); 291assertTrue(adesc.configurable); 292 293assertSame(cdesc.get, cdesc.set); 294assertSame(cdesc.get, adesc.get); 295assertSame(cdesc.get, adesc.set); 296 297assertTrue(cdesc.get instanceof Function); 298assertEquals(0, cdesc.get.length); 299assertThrows(cdesc.get, TypeError); 300 301assertThrows(function() { return f.caller; }, TypeError); 302assertThrows(function() { f.caller = 42; }, TypeError); 303assertThrows(function() { return f.arguments; }, TypeError); 304assertThrows(function() { f.arguments = 42; }, TypeError); 305 306// Shouldn't throw. Accessing the functions caller must throw if 307// the caller is strict and the callee isn't. A bound function is built-in, 308// but not considered strict. 309(function foo() { return foo.caller; }).bind()(); 310 311 312(function TestProtoIsPreserved() { 313 function fun() {} 314 315 function proto() {} 316 Object.setPrototypeOf(fun, proto); 317 var bound = fun.bind({}); 318 assertEquals(proto, Object.getPrototypeOf(bound)); 319 320 var bound2 = fun.bind({}); 321 assertTrue(%HaveSameMap(new bound, new bound2)); 322 323 Object.setPrototypeOf(fun, null); 324 bound = Function.prototype.bind.call(fun, {}); 325 assertEquals(null, Object.getPrototypeOf(bound)); 326})(); 327