diff options
Diffstat (limited to 'js/src/tests/non262/object/toPrimitive.js')
-rw-r--r-- | js/src/tests/non262/object/toPrimitive.js | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/js/src/tests/non262/object/toPrimitive.js b/js/src/tests/non262/object/toPrimitive.js new file mode 100644 index 0000000000..59ed6ac475 --- /dev/null +++ b/js/src/tests/non262/object/toPrimitive.js @@ -0,0 +1,101 @@ +// ES6 7.1.1 ToPrimitive(input [, PreferredType]) specifies a new extension +// point in the language. Objects can override the behavior of ToPrimitive +// somewhat by supporting the method obj[@@toPrimitive](hint). +// +// (Rationale: ES5 had a [[DefaultValue]] internal method, overridden only by +// Date objects. The change in ES6 is to make [[DefaultValue]] a plain old +// method. This allowed ES6 to eliminate the [[DefaultValue]] internal method, +// simplifying the meta-object protocol and thus proxies.) + +// obj[Symbol.toPrimitive]() is called whenever the ToPrimitive algorithm is invoked. +var expectedThis, expectedHint; +var obj = { + [Symbol.toPrimitive](hint, ...rest) { + assertEq(this, expectedThis); + assertEq(hint, expectedHint); + assertEq(rest.length, 0); + return 2015; + } +}; +expectedThis = obj; +expectedHint = "string"; +assertEq(String(obj), "2015"); +expectedHint = "number"; +assertEq(Number(obj), 2015); + +// It is called even through proxies. +var proxy = new Proxy(obj, {}); +expectedThis = proxy; +expectedHint = "default"; +assertEq("ES" + proxy, "ES2015"); + +// It is called even through additional proxies and the prototype chain. +proxy = new Proxy(Object.create(proxy), {}); +expectedThis = proxy; +expectedHint = "default"; +assertEq("ES" + (proxy + 1), "ES2016"); + +// It is not called if the operand is already a primitive. +var ok = true; +for (var constructor of [Boolean, Number, String, Symbol]) { + constructor.prototype[Symbol.toPrimitive] = function () { + ok = false; + throw "FAIL"; + }; +} +assertEq(Number(true), 1); +assertEq(Number(77.7), 77.7); +assertEq(Number("123"), 123); +assertThrowsInstanceOf(() => Number(Symbol.iterator), TypeError); +assertEq(String(true), "true"); +assertEq(String(77.7), "77.7"); +assertEq(String("123"), "123"); +assertEq(String(Symbol.iterator), "Symbol(Symbol.iterator)"); +assertEq(ok, true); + +// Converting a primitive symbol to another primitive type throws even if you +// delete the @@toPrimitive method from Symbol.prototype. +delete Symbol.prototype[Symbol.toPrimitive]; +var sym = Symbol("ok"); +assertThrowsInstanceOf(() => `${sym}`, TypeError); +assertThrowsInstanceOf(() => Number(sym), TypeError); +assertThrowsInstanceOf(() => "" + sym, TypeError); + +// However, having deleted that method, converting a Symbol wrapper object does +// work: it calls Symbol.prototype.toString(). +obj = Object(sym); +assertEq(String(obj), "Symbol(ok)"); +assertEq(`${obj}`, "Symbol(ok)"); + +// Deleting valueOf as well makes numeric conversion also call toString(). +delete Symbol.prototype.valueOf; +delete Object.prototype.valueOf; +assertEq(Number(obj), NaN); +Symbol.prototype.toString = function () { return "2060"; }; +assertEq(Number(obj), 2060); + +// Deleting Date.prototype[Symbol.toPrimitive] changes the result of addition +// involving Date objects. +var d = new Date; +assertEq(0 + d, 0 + d.toString()); +delete Date.prototype[Symbol.toPrimitive]; +assertEq(0 + d, 0 + d.valueOf()); + +// If @@toPrimitive, .toString, and .valueOf are all missing, we get a +// particular sequence of property accesses, followed by a TypeError exception. +var log = []; +function doGet(target, propertyName, receiver) { + log.push(propertyName); +} +var handler = new Proxy({}, { + get(target, trapName, receiver) { + if (trapName !== "get") + throw `FAIL: system tried to access handler method: ${String(trapName)}`; + return doGet; + } +}); +proxy = new Proxy(Object.create(null), handler); +assertThrowsInstanceOf(() => proxy == 0, TypeError); +assertDeepEq(log, [Symbol.toPrimitive, "valueOf", "toString"]); + +reportCompare(0, 0); |