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// Tests the Function.prototype.bind (ES 15.3.4.5) method. 29 30// Simple tests. 31function foo(x, y, z) { 32 return [this, arguments.length, x]; 33} 34 35assertEquals(3, foo.length); 36 37var f = foo.bind(foo); 38assertEquals([foo, 3, 1], f(1, 2, 3)); 39assertEquals(3, f.length); 40 41f = foo.bind(foo, 1); 42assertEquals([foo, 3, 1], f(2, 3)); 43assertEquals(2, f.length); 44 45f = foo.bind(foo, 1, 2); 46assertEquals([foo, 3, 1], f(3)); 47assertEquals(1, f.length); 48 49f = foo.bind(foo, 1, 2, 3); 50assertEquals([foo, 3, 1], f()); 51assertEquals(0, f.length); 52 53// Test that length works correctly even if more than the actual number 54// of arguments are given when binding. 55f = foo.bind(foo, 1, 2, 3, 4, 5, 6, 7, 8, 9); 56assertEquals([foo, 9, 1], f()); 57assertEquals(0, f.length); 58 59// Use a different bound object. 60var obj = {x: 42, y: 43}; 61// Values that would normally be in "this" when calling f_bound_this. 62var x = 42; 63var y = 44; 64 65function f_bound_this(z) { 66 return z + this.y - this.x; 67} 68 69assertEquals(3, f_bound_this(1)) 70f = f_bound_this.bind(obj); 71assertEquals(2, f(1)); 72assertEquals(1, f.length); 73 74f = f_bound_this.bind(obj, 2); 75assertEquals(3, f()); 76assertEquals(0, f.length); 77 78// Test chained binds. 79 80// When only giving the thisArg, any number of binds should have 81// the same effect. 82f = foo.bind(foo); 83assertEquals([foo, 3, 1], f(1, 2, 3)); 84 85var not_foo = {}; 86f = foo.bind(foo).bind(not_foo).bind(not_foo).bind(not_foo); 87assertEquals([foo, 3, 1], f(1, 2, 3)); 88assertEquals(3, f.length); 89 90// Giving bound parameters should work at any place in the chain. 91f = foo.bind(foo, 1).bind(not_foo).bind(not_foo).bind(not_foo); 92assertEquals([foo, 3, 1], f(2, 3)); 93assertEquals(2, f.length); 94 95f = foo.bind(foo).bind(not_foo, 1).bind(not_foo).bind(not_foo); 96assertEquals([foo, 3, 1], f(2, 3)); 97assertEquals(2, f.length); 98 99f = foo.bind(foo).bind(not_foo).bind(not_foo,1 ).bind(not_foo); 100assertEquals([foo, 3, 1], f(2, 3)); 101assertEquals(2, f.length); 102 103f = foo.bind(foo).bind(not_foo).bind(not_foo).bind(not_foo, 1); 104assertEquals([foo, 3, 1], f(2, 3)); 105assertEquals(2, f.length); 106 107// Several parameters can be given, and given in different bind invocations. 108f = foo.bind(foo, 1, 2).bind(not_foo).bind(not_foo).bind(not_foo); 109assertEquals([foo, 3, 1], f(3)); 110assertEquals(1, f.length); 111 112f = foo.bind(foo).bind(not_foo, 1, 2).bind(not_foo).bind(not_foo); 113assertEquals([foo, 3, 1], f(1)); 114assertEquals(1, f.length); 115 116f = foo.bind(foo).bind(not_foo, 1, 2).bind(not_foo).bind(not_foo); 117assertEquals([foo, 3, 1], f(3)); 118assertEquals(1, f.length); 119 120f = foo.bind(foo).bind(not_foo).bind(not_foo, 1, 2).bind(not_foo); 121assertEquals([foo, 3, 1], f(1)); 122assertEquals(1, f.length); 123 124f = foo.bind(foo).bind(not_foo).bind(not_foo).bind(not_foo, 1, 2); 125assertEquals([foo, 3, 1], f(3)); 126assertEquals(1, f.length); 127 128f = foo.bind(foo, 1).bind(not_foo, 2).bind(not_foo).bind(not_foo); 129assertEquals([foo, 3, 1], f(3)); 130assertEquals(1, f.length); 131 132f = foo.bind(foo, 1).bind(not_foo).bind(not_foo, 2).bind(not_foo); 133assertEquals([foo, 3, 1], f(3)); 134assertEquals(1, f.length); 135 136f = foo.bind(foo, 1).bind(not_foo).bind(not_foo).bind(not_foo, 2); 137assertEquals([foo, 3, 1], f(3)); 138assertEquals(1, f.length); 139 140f = foo.bind(foo).bind(not_foo, 1).bind(not_foo).bind(not_foo, 2); 141assertEquals([foo, 3, 1], f(3)); 142assertEquals(1, f.length); 143 144// The wrong number of arguments can be given to bound functions too. 145f = foo.bind(foo); 146assertEquals(3, f.length); 147assertEquals([foo, 0, undefined], f()); 148assertEquals([foo, 1, 1], f(1)); 149assertEquals([foo, 2, 1], f(1, 2)); 150assertEquals([foo, 3, 1], f(1, 2, 3)); 151assertEquals([foo, 4, 1], f(1, 2, 3, 4)); 152 153f = foo.bind(foo, 1); 154assertEquals(2, f.length); 155assertEquals([foo, 1, 1], f()); 156assertEquals([foo, 2, 1], f(2)); 157assertEquals([foo, 3, 1], f(2, 3)); 158assertEquals([foo, 4, 1], f(2, 3, 4)); 159 160f = foo.bind(foo, 1, 2); 161assertEquals(1, f.length); 162assertEquals([foo, 2, 1], f()); 163assertEquals([foo, 3, 1], f(3)); 164assertEquals([foo, 4, 1], f(3, 4)); 165 166f = foo.bind(foo, 1, 2, 3); 167assertEquals(0, f.length); 168assertEquals([foo, 3, 1], f()); 169assertEquals([foo, 4, 1], f(4)); 170 171f = foo.bind(foo, 1, 2, 3, 4); 172assertEquals(0, f.length); 173assertEquals([foo, 4, 1], f()); 174 175// Test constructor calls. 176 177function bar(x, y, z) { 178 this.x = x; 179 this.y = y; 180 this.z = z; 181} 182 183f = bar.bind(bar); 184var obj2 = new f(1,2,3); 185assertEquals(1, obj2.x); 186assertEquals(2, obj2.y); 187assertEquals(3, obj2.z); 188 189f = bar.bind(bar, 1); 190obj2 = new f(2,3); 191assertEquals(1, obj2.x); 192assertEquals(2, obj2.y); 193assertEquals(3, obj2.z); 194 195f = bar.bind(bar, 1, 2); 196obj2 = new f(3); 197assertEquals(1, obj2.x); 198assertEquals(2, obj2.y); 199assertEquals(3, obj2.z); 200 201f = bar.bind(bar, 1, 2, 3); 202obj2 = new f(); 203assertEquals(1, obj2.x); 204assertEquals(2, obj2.y); 205assertEquals(3, obj2.z); 206 207 208// Test bind chains when used as a constructor. 209f = bar.bind(bar, 1).bind(bar, 2).bind(bar, 3); 210obj2 = new f(); 211assertEquals(1, obj2.x); 212assertEquals(2, obj2.y); 213assertEquals(3, obj2.z); 214 215// Test obj2 is instanceof both bar and f. 216assertTrue(obj2 instanceof bar); 217assertTrue(obj2 instanceof f); 218 219// This-args are not relevant to instanceof. 220f = bar.bind(foo.prototype, 1). 221 bind(String.prototype, 2). 222 bind(Function.prototype, 3); 223var obj3 = new f(); 224assertTrue(obj3 instanceof bar); 225assertTrue(obj3 instanceof f); 226assertFalse(obj3 instanceof foo); 227assertFalse(obj3 instanceof Function); 228assertFalse(obj3 instanceof String); 229 230// thisArg is converted to object. 231f = foo.bind(undefined); 232assertEquals([this, 0, undefined], f()); 233 234f = foo.bind(null); 235assertEquals([this, 0, undefined], f()); 236 237f = foo.bind(42); 238assertEquals([Object(42), 0, undefined], f()); 239 240f = foo.bind("foo"); 241assertEquals([Object("foo"), 0, undefined], f()); 242 243f = foo.bind(true); 244assertEquals([Object(true), 0, undefined], f()); 245 246// Strict functions don't convert thisArg. 247function soo(x, y, z) { 248 "use strict"; 249 return [this, arguments.length, x]; 250} 251 252var s = soo.bind(undefined); 253assertEquals([undefined, 0, undefined], s()); 254 255s = soo.bind(null); 256assertEquals([null, 0, undefined], s()); 257 258s = soo.bind(42); 259assertEquals([42, 0, undefined], s()); 260 261s = soo.bind("foo"); 262assertEquals(["foo", 0, undefined], s()); 263 264s = soo.bind(true); 265assertEquals([true, 0, undefined], s()); 266 267// Test that .arguments and .caller are poisoned according to the ES5 spec. 268 269// Check that property descriptors are correct (unconfigurable, unenumerable, 270// and both get and set is the ThrowTypeError function). 271var cdesc = Object.getOwnPropertyDescriptor(f, "caller"); 272var adesc = Object.getOwnPropertyDescriptor(f, "arguments"); 273 274assertFalse(cdesc.enumerable); 275assertFalse(cdesc.configurable); 276 277assertFalse(adesc.enumerable); 278assertFalse(adesc.configurable); 279 280assertSame(cdesc.get, cdesc.set); 281assertSame(cdesc.get, adesc.get); 282assertSame(cdesc.get, adesc.set); 283 284assertTrue(cdesc.get instanceof Function); 285assertEquals(0, cdesc.get.length); 286assertThrows(cdesc.get, TypeError); 287 288assertThrows(function() { return f.caller; }, TypeError); 289assertThrows(function() { f.caller = 42; }, TypeError); 290assertThrows(function() { return f.arguments; }, TypeError); 291assertThrows(function() { f.arguments = 42; }, TypeError); 292 293// Shouldn't throw. Accessing the functions caller must throw if 294// the caller is strict and the callee isn't. A bound function is built-in, 295// but not considered strict. 296(function foo() { return foo.caller; }).bind()(); 297