123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427 |
- // Copyright (C) 2017 Josh Wolfe. All rights reserved.
- // This code is governed by the BSD license found in the LICENSE file.
- /*---
- description: |
- Functions to help generate test cases for testing type coercion abstract
- operations like ToNumber.
- ---*/
- function testCoercibleToIndexZero(test) {
- testCoercibleToIntegerZero(test);
- }
- function testCoercibleToIndexOne(test) {
- testCoercibleToIntegerOne(test);
- }
- function testCoercibleToIndexFromIndex(nominalIndex, test) {
- assert(Number.isInteger(nominalIndex));
- assert(0 <= nominalIndex && nominalIndex <= 2**53 - 1);
- testCoercibleToIntegerFromInteger(nominalIndex, test);
- }
- function testCoercibleToIntegerZero(test) {
- testCoercibleToNumberZero(test);
- testCoercibleToIntegerFromInteger(0, test);
- // NaN -> +0
- testCoercibleToNumberNan(test);
- // When toString() returns a string that parses to NaN:
- test({});
- test([]);
- }
- function testCoercibleToIntegerOne(test) {
- testCoercibleToNumberOne(test);
- testCoercibleToIntegerFromInteger(1, test);
- // When toString() returns "1"
- test([1]);
- test(["1"]);
- }
- function testCoercibleToNumberZero(test) {
- function testPrimitiveValue(value) {
- test(value);
- // ToPrimitive
- testPrimitiveWrappers(value, "number", test);
- }
- testPrimitiveValue(null);
- testPrimitiveValue(false);
- testPrimitiveValue(0);
- testPrimitiveValue("0");
- }
- function testCoercibleToNumberNan(test) {
- function testPrimitiveValue(value) {
- test(value);
- // ToPrimitive
- testPrimitiveWrappers(value, "number", test);
- }
- testPrimitiveValue(undefined);
- testPrimitiveValue(NaN);
- testPrimitiveValue("");
- testPrimitiveValue("foo");
- testPrimitiveValue("true");
- }
- function testCoercibleToNumberOne(test) {
- function testPrimitiveValue(value) {
- test(value);
- // ToPrimitive
- testPrimitiveWrappers(value, "number", test);
- }
- testPrimitiveValue(true);
- testPrimitiveValue(1);
- testPrimitiveValue("1");
- }
- function testCoercibleToIntegerFromInteger(nominalInteger, test) {
- assert(Number.isInteger(nominalInteger));
- function testPrimitiveValue(value) {
- test(value);
- // ToPrimitive
- testPrimitiveWrappers(value, "number", test);
- // Non-primitive values that coerce to the nominal integer:
- // toString() returns a string that parsers to a primitive value.
- test([value]);
- }
- function testPrimitiveNumber(number) {
- testPrimitiveValue(number);
- // ToNumber: String -> Number
- testPrimitiveValue(number.toString());
- }
- testPrimitiveNumber(nominalInteger);
- // ToInteger: floor(abs(number))
- if (nominalInteger >= 0) {
- testPrimitiveNumber(nominalInteger + 0.9);
- }
- if (nominalInteger <= 0) {
- testPrimitiveNumber(nominalInteger - 0.9);
- }
- }
- function testPrimitiveWrappers(primitiveValue, hint, test) {
- if (primitiveValue != null) {
- // null and undefined result in {} rather than a proper wrapper,
- // so skip this case for those values.
- test(Object(primitiveValue));
- }
- testCoercibleToPrimitiveWithMethod(hint, function() {
- return primitiveValue;
- }, test);
- }
- function testCoercibleToPrimitiveWithMethod(hint, method, test) {
- var methodNames;
- if (hint === "number") {
- methodNames = ["valueOf", "toString"];
- } else if (hint === "string") {
- methodNames = ["toString", "valueOf"];
- } else {
- throw new Test262Error();
- }
- // precedence order
- test({
- [Symbol.toPrimitive]: method,
- [methodNames[0]]: function() { throw new Test262Error(); },
- [methodNames[1]]: function() { throw new Test262Error(); },
- });
- test({
- [methodNames[0]]: method,
- [methodNames[1]]: function() { throw new Test262Error(); },
- });
- if (hint === "number") {
- // The default valueOf returns an object, which is unsuitable.
- // The default toString returns a String, which is suitable.
- // Therefore this test only works for valueOf falling back to toString.
- test({
- // this is toString:
- [methodNames[1]]: method,
- });
- }
- // GetMethod: if func is undefined or null, return undefined.
- test({
- [Symbol.toPrimitive]: undefined,
- [methodNames[0]]: method,
- [methodNames[1]]: method,
- });
- test({
- [Symbol.toPrimitive]: null,
- [methodNames[0]]: method,
- [methodNames[1]]: method,
- });
- // if methodNames[0] is not callable, fallback to methodNames[1]
- test({
- [methodNames[0]]: null,
- [methodNames[1]]: method,
- });
- test({
- [methodNames[0]]: 1,
- [methodNames[1]]: method,
- });
- test({
- [methodNames[0]]: {},
- [methodNames[1]]: method,
- });
- // if methodNames[0] returns an object, fallback to methodNames[1]
- test({
- [methodNames[0]]: function() { return {}; },
- [methodNames[1]]: method,
- });
- test({
- [methodNames[0]]: function() { return Object(1); },
- [methodNames[1]]: method,
- });
- }
- function testNotCoercibleToIndex(test) {
- function testPrimitiveValue(value) {
- test(RangeError, value);
- // ToPrimitive
- testPrimitiveWrappers(value, "number", function(value) {
- test(RangeError, value);
- });
- }
- // Let integerIndex be ? ToInteger(value).
- testNotCoercibleToInteger(test);
- // If integerIndex < 0, throw a RangeError exception.
- testPrimitiveValue(-1);
- testPrimitiveValue(-2.5);
- testPrimitiveValue("-2.5");
- testPrimitiveValue(-Infinity);
- // Let index be ! ToLength(integerIndex).
- // If SameValueZero(integerIndex, index) is false, throw a RangeError exception.
- testPrimitiveValue(2 ** 53);
- testPrimitiveValue(Infinity);
- }
- function testNotCoercibleToInteger(test) {
- // ToInteger only throws from ToNumber.
- testNotCoercibleToNumber(test);
- }
- function testNotCoercibleToNumber(test) {
- function testPrimitiveValue(value) {
- test(TypeError, value);
- // ToPrimitive
- testPrimitiveWrappers(value, "number", function(value) {
- test(TypeError, value);
- });
- }
- // ToNumber: Symbol -> TypeError
- testPrimitiveValue(Symbol("1"));
- if (typeof BigInt !== "undefined") {
- // ToNumber: BigInt -> TypeError
- testPrimitiveValue(BigInt(0));
- }
- // ToPrimitive
- testNotCoercibleToPrimitive("number", test);
- }
- function testNotCoercibleToPrimitive(hint, test) {
- function MyError() {}
- // ToPrimitive: input[@@toPrimitive] is not callable (and non-null)
- test(TypeError, {[Symbol.toPrimitive]: 1});
- test(TypeError, {[Symbol.toPrimitive]: {}});
- // ToPrimitive: input[@@toPrimitive] returns object
- test(TypeError, {[Symbol.toPrimitive]: function() { return Object(1); }});
- test(TypeError, {[Symbol.toPrimitive]: function() { return {}; }});
- // ToPrimitive: input[@@toPrimitive] throws
- test(MyError, {[Symbol.toPrimitive]: function() { throw new MyError(); }});
- // OrdinaryToPrimitive: method throws
- testCoercibleToPrimitiveWithMethod(hint, function() {
- throw new MyError();
- }, function(value) {
- test(MyError, value);
- });
- // OrdinaryToPrimitive: both methods are unsuitable
- function testUnsuitableMethod(method) {
- test(TypeError, {valueOf:method, toString:method});
- }
- // not callable:
- testUnsuitableMethod(null);
- testUnsuitableMethod(1);
- testUnsuitableMethod({});
- // returns object:
- testUnsuitableMethod(function() { return Object(1); });
- testUnsuitableMethod(function() { return {}; });
- }
- function testCoercibleToString(test) {
- function testPrimitiveValue(value, expectedString) {
- test(value, expectedString);
- // ToPrimitive
- testPrimitiveWrappers(value, "string", function(value) {
- test(value, expectedString);
- });
- }
- testPrimitiveValue(undefined, "undefined");
- testPrimitiveValue(null, "null");
- testPrimitiveValue(true, "true");
- testPrimitiveValue(false, "false");
- testPrimitiveValue(0, "0");
- testPrimitiveValue(-0, "0");
- testPrimitiveValue(Infinity, "Infinity");
- testPrimitiveValue(-Infinity, "-Infinity");
- testPrimitiveValue(123.456, "123.456");
- testPrimitiveValue(-123.456, "-123.456");
- testPrimitiveValue("", "");
- testPrimitiveValue("foo", "foo");
- if (typeof BigInt !== "undefined") {
- // BigInt -> TypeError
- testPrimitiveValue(BigInt(0), "0");
- }
- // toString of a few objects
- test([], "");
- test(["foo", "bar"], "foo,bar");
- test({}, "[object Object]");
- }
- function testNotCoercibleToString(test) {
- function testPrimitiveValue(value) {
- test(TypeError, value);
- // ToPrimitive
- testPrimitiveWrappers(value, "string", function(value) {
- test(TypeError, value);
- });
- }
- // Symbol -> TypeError
- testPrimitiveValue(Symbol("1"));
- // ToPrimitive
- testNotCoercibleToPrimitive("string", test);
- }
- function testCoercibleToBooleanTrue(test) {
- test(true);
- test(1);
- test("string");
- test(Symbol("1"));
- test({});
- }
- function testCoercibleToBooleanFalse(test) {
- test(undefined);
- test(null);
- test(false);
- test(0);
- test(-0);
- test(NaN);
- test("");
- }
- function testCoercibleToBigIntZero(test) {
- function testPrimitiveValue(value) {
- test(value);
- // ToPrimitive
- testPrimitiveWrappers(value, "number", test);
- }
- testCoercibleToBigIntFromBigInt(BigInt(0), test);
- testPrimitiveValue(-BigInt(0));
- testPrimitiveValue("-0");
- testPrimitiveValue(false);
- testPrimitiveValue("");
- testPrimitiveValue(" ");
- // toString() returns ""
- test([]);
- // toString() returns "0"
- test([0]);
- }
- function testCoercibleToBigIntOne(test) {
- function testPrimitiveValue(value) {
- test(value);
- // ToPrimitive
- testPrimitiveWrappers(value, "number", test);
- }
- testCoercibleToBigIntFromBigInt(BigInt(1), test);
- testPrimitiveValue(true);
- // toString() returns "1"
- test([1]);
- }
- function testCoercibleToBigIntFromBigInt(nominalBigInt, test) {
- function testPrimitiveValue(value) {
- test(value);
- // ToPrimitive
- testPrimitiveWrappers(value, "number", test);
- }
- testPrimitiveValue(nominalBigInt);
- testPrimitiveValue(nominalBigInt.toString());
- testPrimitiveValue("0b" + nominalBigInt.toString(2));
- testPrimitiveValue("0o" + nominalBigInt.toString(8));
- testPrimitiveValue("0x" + nominalBigInt.toString(16));
- testPrimitiveValue(" " + nominalBigInt.toString() + " ");
- // toString() returns the decimal string representation
- test([nominalBigInt]);
- test([nominalBigInt.toString()]);
- }
- function testNotCoercibleToBigInt(test) {
- function testPrimitiveValue(error, value) {
- test(error, value);
- // ToPrimitive
- testPrimitiveWrappers(value, "number", function(value) {
- test(error, value);
- });
- }
- // Undefined, Null, Number, Symbol -> TypeError
- testPrimitiveValue(TypeError, undefined);
- testPrimitiveValue(TypeError, null);
- testPrimitiveValue(TypeError, 0);
- testPrimitiveValue(TypeError, NaN);
- testPrimitiveValue(TypeError, Infinity);
- testPrimitiveValue(TypeError, Symbol("1"));
- // when a String parses to NaN -> SyntaxError
- function testStringValue(string) {
- testPrimitiveValue(SyntaxError, string);
- testPrimitiveValue(SyntaxError, " " + string);
- testPrimitiveValue(SyntaxError, string + " ");
- testPrimitiveValue(SyntaxError, " " + string + " ");
- }
- testStringValue("a");
- testStringValue("0b2");
- testStringValue("0o8");
- testStringValue("0xg");
- testStringValue("1n");
- }
|