test262/harness/typeCoercion.js

225 lines
5.8 KiB
JavaScript
Raw Normal View History

2017-08-28 19:37:38 +02:00
// 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 testCoercibleToIntegerZero(test) {
function testPrimitiveValue(value) {
test(value);
// ToPrimitive
testPrimitiveWrappers(value, "number", test);
}
2017-08-28 19:37:38 +02:00
// ToNumber
testPrimitiveValue(null);
testPrimitiveValue(false);
testPrimitiveValue(0);
testPrimitiveValue("0");
// ToInteger: NaN -> +0
testPrimitiveValue(undefined);
testPrimitiveValue(NaN);
testPrimitiveValue("");
testPrimitiveValue("foo");
testPrimitiveValue("true");
// ToInteger: floor(abs(number))
testPrimitiveValue(0.9);
testPrimitiveValue(-0);
testPrimitiveValue(-0.9);
testPrimitiveValue("0.9");
testPrimitiveValue("-0");
testPrimitiveValue("-0.9");
2017-08-28 19:37:38 +02:00
// Non-primitive values that coerce to 0:
// toString() returns a string that parses to NaN.
test({});
test([]);
2017-08-28 19:37:38 +02:00
}
function testCoercibleToIntegerOne(test) {
function testPrimitiveValue(value) {
test(value);
// ToPrimitive
testPrimitiveWrappers(value, "number", test);
}
2017-08-28 19:37:38 +02:00
// ToNumber
testPrimitiveValue(true);
testPrimitiveValue(1);
testPrimitiveValue("1");
2017-08-28 19:37:38 +02:00
// ToInteger: floor(abs(number))
testPrimitiveValue(1.9);
testPrimitiveValue("1.9");
2017-08-28 19:37:38 +02:00
// Non-primitive values that coerce to 1:
// toString() returns a string that parses to 1.
test([1]);
test(["1"]);
2017-08-28 19:37:38 +02:00
}
function testCoercibleToIntegerFromInteger(nominalInteger, test) {
2017-08-28 19:37:38 +02:00
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);
2017-08-28 19:37:38 +02:00
// ToInteger: floor(abs(number))
if (nominalInteger >= 0) {
testPrimitiveNumber(nominalInteger + 0.9);
2017-08-28 19:37:38 +02:00
}
if (nominalInteger <= 0) {
testPrimitiveNumber(nominalInteger - 0.9);
2017-08-28 19:37:38 +02:00
}
}
function testPrimitiveWrappers(primitiveValue, hint, test) {
2017-08-28 19:37:38 +02:00
if (primitiveValue != null) {
// null and undefined result in {} rather than a proper wrapper,
// so skip this case for those values.
test(Object(primitiveValue));
2017-08-28 19:37:38 +02:00
}
testCoercibleToPrimitiveWithMethod(hint, function() {
2017-08-28 19:37:38 +02:00
return primitiveValue;
}, test);
2017-08-28 19:37:38 +02:00
}
function testCoercibleToPrimitiveWithMethod(hint, method, test) {
2017-08-28 19:37:38 +02:00
var methodNames;
if (hint === "number") {
methodNames = ["valueOf", "toString"];
} else if (hint === "string") {
2017-08-28 19:37:38 +02:00
methodNames = ["toString", "valueOf"];
} else {
throw new Test262Error();
2017-08-28 19:37:38 +02:00
}
// 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(); },
});
test({
[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,
});
2017-08-28 19:37:38 +02:00
}
function testNotCoercibleToInteger(test) {
2017-08-28 19:37:38 +02:00
// ToInteger only throws from ToNumber.
return testNotCoercibleToNumber(test);
2017-08-28 19:37:38 +02:00
}
function testNotCoercibleToNumber(test) {
function testPrimitiveValue(value) {
test(TypeError, value);
// ToPrimitive
testPrimitiveWrappers(value, "number", function(value) {
test(TypeError, value);
});
}
2017-08-28 19:37:38 +02:00
// ToNumber: Symbol -> TypeError
testPrimitiveValue(Symbol("1"));
2017-08-28 19:37:38 +02:00
if (typeof BigInt !== "undefined") {
// ToNumber: BigInt -> TypeError
testPrimitiveValue(BigInt(0));
2017-08-28 19:37:38 +02:00
}
// ToPrimitive
testNotCoercibleToPrimitive("number", test);
2017-08-28 19:37:38 +02:00
}
function testNotCoercibleToPrimitive(hint, test) {
2017-08-28 19:37:38 +02:00
function MyError() {}
// ToPrimitive: input[@@toPrimitive] is not callable (and non-null)
test(TypeError, {[Symbol.toPrimitive]: 1});
test(TypeError, {[Symbol.toPrimitive]: {}});
2017-08-28 19:37:38 +02:00
// ToPrimitive: input[@@toPrimitive] returns object
test(TypeError, {[Symbol.toPrimitive]: function() { return Object(1); }});
test(TypeError, {[Symbol.toPrimitive]: function() { return {}; }});
2017-08-28 19:37:38 +02:00
// ToPrimitive: input[@@toPrimitive] throws
test(MyError, {[Symbol.toPrimitive]: function() { throw new MyError(); }});
2017-08-28 19:37:38 +02:00
// OrdinaryToPrimitive: method throws
testCoercibleToPrimitiveWithMethod(hint, function() {
2017-08-28 19:37:38 +02:00
throw new MyError();
}, function(value) {
test(MyError, value);
2017-08-28 19:37:38 +02:00
});
// 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 {}; });
2017-08-28 19:37:38 +02:00
}