michael@0: /* michael@0: * Any copyright is dedicated to the Public Domain. michael@0: * http://creativecommons.org/licenses/publicdomain/ michael@0: */ michael@0: michael@0: var gTestfile = 'function-bind.js'; michael@0: var BUGNUMBER = 429507; michael@0: var summary = "ES5: Function.prototype.bind"; michael@0: michael@0: print(BUGNUMBER + ": " + summary); michael@0: michael@0: /************** michael@0: * BEGIN TEST * michael@0: **************/ michael@0: michael@0: // ad-hoc testing michael@0: michael@0: assertEq(Function.prototype.hasOwnProperty("bind"), true); michael@0: michael@0: var bind = Function.prototype.bind; michael@0: assertEq(bind.length, 1); michael@0: michael@0: michael@0: var strictReturnThis = function() { "use strict"; return this; }; michael@0: michael@0: assertEq(strictReturnThis.bind(undefined)(), undefined); michael@0: assertEq(strictReturnThis.bind(null)(), null); michael@0: michael@0: var obj = {}; michael@0: assertEq(strictReturnThis.bind(obj)(), obj); michael@0: michael@0: assertEq(strictReturnThis.bind(NaN)(), NaN); michael@0: michael@0: assertEq(strictReturnThis.bind(true)(), true); michael@0: assertEq(strictReturnThis.bind(false)(), false); michael@0: michael@0: assertEq(strictReturnThis.bind("foopy")(), "foopy"); michael@0: michael@0: michael@0: // rigorous, step-by-step testing michael@0: michael@0: function expectThrowTypeError(fun) michael@0: { michael@0: try michael@0: { michael@0: var r = fun(); michael@0: throw new Error("didn't throw TypeError, returned " + r); michael@0: } michael@0: catch (e) michael@0: { michael@0: assertEq(e instanceof TypeError, true, michael@0: "didn't throw TypeError, got: " + e); michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * 1. Let Target be the this value. michael@0: * 2. If IsCallable(Target) is false, throw a TypeError exception. michael@0: */ michael@0: expectThrowTypeError(function() { bind.call(null); }); michael@0: expectThrowTypeError(function() { bind.call(undefined); }); michael@0: expectThrowTypeError(function() { bind.call(NaN); }); michael@0: expectThrowTypeError(function() { bind.call(0); }); michael@0: expectThrowTypeError(function() { bind.call(-0); }); michael@0: expectThrowTypeError(function() { bind.call(17); }); michael@0: expectThrowTypeError(function() { bind.call(42); }); michael@0: expectThrowTypeError(function() { bind.call("foobar"); }); michael@0: expectThrowTypeError(function() { bind.call(true); }); michael@0: expectThrowTypeError(function() { bind.call(false); }); michael@0: expectThrowTypeError(function() { bind.call([]); }); michael@0: expectThrowTypeError(function() { bind.call({}); }); michael@0: michael@0: michael@0: /* michael@0: * 3. Let A be a new (possibly empty) internal list of all of the argument michael@0: * values provided after thisArg (arg1, arg2 etc), in order. michael@0: * 4. Let F be a new native ECMAScript object . michael@0: * 5. Set all the internal methods, except for [[Get]], of F as specified in michael@0: * 8.12. michael@0: * 6. Set the [[Get]] internal property of F as specified in 15.3.5.4. michael@0: * 7. Set the [[TargetFunction]] internal property of F to Target. michael@0: * 8. Set the [[BoundThis]] internal property of F to the value of thisArg. michael@0: * 9. Set the [[BoundArgs]] internal property of F to A. michael@0: */ michael@0: // throughout michael@0: michael@0: michael@0: /* 10. Set the [[Class]] internal property of F to "Function". */ michael@0: var toString = Object.prototype.toString; michael@0: assertEq(toString.call(function(){}), "[object Function]"); michael@0: assertEq(toString.call(function a(){}), "[object Function]"); michael@0: assertEq(toString.call(function(a){}), "[object Function]"); michael@0: assertEq(toString.call(function a(b){}), "[object Function]"); michael@0: assertEq(toString.call(function(){}.bind()), "[object Function]"); michael@0: assertEq(toString.call(function a(){}.bind()), "[object Function]"); michael@0: assertEq(toString.call(function(a){}.bind()), "[object Function]"); michael@0: assertEq(toString.call(function a(b){}.bind()), "[object Function]"); michael@0: michael@0: michael@0: /* michael@0: * 11. Set the [[Prototype]] internal property of F to the standard built-in michael@0: * Function prototype object as specified in 15.3.3.1. michael@0: */ michael@0: assertEq(Object.getPrototypeOf(bind.call(function(){})), Function.prototype); michael@0: assertEq(Object.getPrototypeOf(bind.call(function a(){})), Function.prototype); michael@0: assertEq(Object.getPrototypeOf(bind.call(function(a){})), Function.prototype); michael@0: assertEq(Object.getPrototypeOf(bind.call(function a(b){})), Function.prototype); michael@0: michael@0: michael@0: /* michael@0: * 12. Set the [[Call]] internal property of F as described in 15.3.4.5.1. michael@0: */ michael@0: var a = Array.bind(1, 2); michael@0: assertEq(a().length, 2); michael@0: assertEq(a(4).length, 2); michael@0: assertEq(a(4, 8).length, 3); michael@0: michael@0: function t() { return this; } michael@0: var bt = t.bind(t); michael@0: assertEq(bt(), t); michael@0: michael@0: function callee() { return arguments.callee; } michael@0: var call = callee.bind(); michael@0: assertEq(call(), callee); michael@0: assertEq(new call(), callee); michael@0: michael@0: michael@0: /* michael@0: * 13. Set the [[Construct]] internal property of F as described in 15.3.4.5.2. michael@0: */ michael@0: function Point(x, y) michael@0: { michael@0: this.x = x; michael@0: this.y = y; michael@0: } michael@0: var YAxisPoint = Point.bind(null, 0) michael@0: michael@0: assertEq(YAxisPoint.hasOwnProperty("prototype"), false); michael@0: var p = new YAxisPoint(5); michael@0: assertEq(p.x, 0); michael@0: assertEq(p.y, 5); michael@0: assertEq(p instanceof Point, true); michael@0: assertEq(p instanceof YAxisPoint, true); michael@0: assertEq(Object.prototype.toString.call(YAxisPoint), "[object Function]"); michael@0: assertEq(YAxisPoint.length, 1); michael@0: michael@0: michael@0: /* michael@0: * 14. Set the [[HasInstance]] internal property of F as described in michael@0: * 15.3.4.5.3. michael@0: */ michael@0: function JoinArguments() michael@0: { michael@0: this.args = Array.prototype.join.call(arguments, ", "); michael@0: } michael@0: michael@0: var Join1 = JoinArguments.bind(null, 1); michael@0: var Join2 = Join1.bind(null, 2); michael@0: var Join3 = Join2.bind(null, 3); michael@0: var Join4 = Join3.bind(null, 4); michael@0: var Join5 = Join4.bind(null, 5); michael@0: var Join6 = Join5.bind(null, 6); michael@0: michael@0: var r = new Join6(7); michael@0: assertEq(r instanceof Join6, true); michael@0: assertEq(r instanceof Join5, true); michael@0: assertEq(r instanceof Join4, true); michael@0: assertEq(r instanceof Join3, true); michael@0: assertEq(r instanceof Join2, true); michael@0: assertEq(r instanceof Join1, true); michael@0: assertEq(r instanceof JoinArguments, true); michael@0: assertEq(r.args, "1, 2, 3, 4, 5, 6, 7"); michael@0: michael@0: michael@0: /* michael@0: * 15. If the [[Class]] internal property of Target is "Function", then michael@0: * a. Let L be the length property of Target minus the length of A. michael@0: * b. Set the length own property of F to either 0 or L, whichever is larger. michael@0: * 16. Else set the length own property of F to 0. michael@0: */ michael@0: function none() { return arguments.length; } michael@0: assertEq(none.bind(1, 2)(3, 4), 3); michael@0: assertEq(none.bind(1, 2)(), 1); michael@0: assertEq(none.bind(1)(2, 3), 2); michael@0: assertEq(none.bind().length, 0); michael@0: assertEq(none.bind(null).length, 0); michael@0: assertEq(none.bind(null, 1).length, 0); michael@0: assertEq(none.bind(null, 1, 2).length, 0); michael@0: michael@0: function one(a) { } michael@0: assertEq(one.bind().length, 1); michael@0: assertEq(one.bind(null).length, 1); michael@0: assertEq(one.bind(null, 1).length, 0); michael@0: assertEq(one.bind(null, 1, 2).length, 0); michael@0: michael@0: // retch michael@0: var br = Object.create(null, { length: { value: 0 } }); michael@0: try michael@0: { michael@0: br = bind.call(/a/g, /a/g, "aaaa"); michael@0: } michael@0: catch (e) { /* nothing */ } michael@0: assertEq(br.length, 0); michael@0: michael@0: michael@0: /* michael@0: * 17. Set the attributes of the length own property of F to the values michael@0: * specified in 15.3.5.1. michael@0: */ michael@0: var len1Desc = michael@0: Object.getOwnPropertyDescriptor(function(a, b, c){}.bind(), "length"); michael@0: assertEq(len1Desc.value, 3); michael@0: assertEq(len1Desc.writable, false); michael@0: assertEq(len1Desc.enumerable, false); michael@0: assertEq(len1Desc.configurable, false); michael@0: michael@0: var len2Desc = michael@0: Object.getOwnPropertyDescriptor(function(a, b, c){}.bind(null, 2), "length"); michael@0: assertEq(len2Desc.value, 2); michael@0: assertEq(len2Desc.writable, false); michael@0: assertEq(len2Desc.enumerable, false); michael@0: assertEq(len2Desc.configurable, false); michael@0: michael@0: michael@0: /* michael@0: * 18. Set the [[Extensible]] internal property of F to true. michael@0: */ michael@0: var bound = (function() { }).bind(); michael@0: michael@0: var isExtensible = Object.isExtensible || function() { return true; }; michael@0: assertEq(isExtensible(bound), true); michael@0: michael@0: bound.foo = 17; michael@0: var fooDesc = Object.getOwnPropertyDescriptor(bound, "foo"); michael@0: assertEq(fooDesc.value, 17); michael@0: assertEq(fooDesc.writable, true); michael@0: assertEq(fooDesc.enumerable, true); michael@0: assertEq(fooDesc.configurable, true); michael@0: michael@0: michael@0: /* michael@0: * 19. Let thrower be the [[ThrowTypeError]] function Object (13.2.3). michael@0: * 20. Call the [[DefineOwnProperty]] internal method of F with arguments michael@0: * "caller", PropertyDescriptor {[[Get]]: thrower, [[Set]]: thrower, michael@0: * [[Enumerable]]: false, [[Configurable]]: false}, and false. michael@0: * 21. Call the [[DefineOwnProperty]] internal method of F with arguments michael@0: * "arguments", PropertyDescriptor {[[Get]]: thrower, [[Set]]: thrower, michael@0: * [[Enumerable]]: false, [[Configurable]]: false}, and false. michael@0: */ michael@0: function f() { "use strict"; } michael@0: var canonicalTTE = Object.getOwnPropertyDescriptor(f, "caller").get; michael@0: michael@0: var tte; michael@0: michael@0: var boundf = f.bind(); michael@0: michael@0: var boundfCaller = Object.getOwnPropertyDescriptor(boundf, "caller"); michael@0: assertEq("get" in boundfCaller, true); michael@0: assertEq("set" in boundfCaller, true); michael@0: tte = boundfCaller.get; michael@0: assertEq(tte, canonicalTTE); michael@0: assertEq(tte, boundfCaller.set); michael@0: michael@0: var boundfArguments = Object.getOwnPropertyDescriptor(boundf, "arguments"); michael@0: assertEq("get" in boundfArguments, true); michael@0: assertEq("set" in boundfArguments, true); michael@0: tte = boundfArguments.get; michael@0: assertEq(tte, canonicalTTE); michael@0: assertEq(tte, boundfArguments.set); michael@0: michael@0: michael@0: /* 22. Return F. */ michael@0: var passim = function p(){}.bind(1); michael@0: assertEq(typeof passim, "function"); michael@0: michael@0: michael@0: /******************************************************************************/ michael@0: michael@0: if (typeof reportCompare === "function") michael@0: reportCompare(true, true); michael@0: michael@0: print("All tests passed!");