typeCoercion.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. // Copyright (C) 2017 Josh Wolfe. All rights reserved.
  2. // This code is governed by the BSD license found in the LICENSE file.
  3. /*---
  4. description: |
  5. Functions to help generate test cases for testing type coercion abstract
  6. operations like ToNumber.
  7. ---*/
  8. function testCoercibleToIndexZero(test) {
  9. testCoercibleToIntegerZero(test);
  10. }
  11. function testCoercibleToIndexOne(test) {
  12. testCoercibleToIntegerOne(test);
  13. }
  14. function testCoercibleToIndexFromIndex(nominalIndex, test) {
  15. assert(Number.isInteger(nominalIndex));
  16. assert(0 <= nominalIndex && nominalIndex <= 2**53 - 1);
  17. testCoercibleToIntegerFromInteger(nominalIndex, test);
  18. }
  19. function testCoercibleToIntegerZero(test) {
  20. testCoercibleToNumberZero(test);
  21. testCoercibleToIntegerFromInteger(0, test);
  22. // NaN -> +0
  23. testCoercibleToNumberNan(test);
  24. // When toString() returns a string that parses to NaN:
  25. test({});
  26. test([]);
  27. }
  28. function testCoercibleToIntegerOne(test) {
  29. testCoercibleToNumberOne(test);
  30. testCoercibleToIntegerFromInteger(1, test);
  31. // When toString() returns "1"
  32. test([1]);
  33. test(["1"]);
  34. }
  35. function testCoercibleToNumberZero(test) {
  36. function testPrimitiveValue(value) {
  37. test(value);
  38. // ToPrimitive
  39. testPrimitiveWrappers(value, "number", test);
  40. }
  41. testPrimitiveValue(null);
  42. testPrimitiveValue(false);
  43. testPrimitiveValue(0);
  44. testPrimitiveValue("0");
  45. }
  46. function testCoercibleToNumberNan(test) {
  47. function testPrimitiveValue(value) {
  48. test(value);
  49. // ToPrimitive
  50. testPrimitiveWrappers(value, "number", test);
  51. }
  52. testPrimitiveValue(undefined);
  53. testPrimitiveValue(NaN);
  54. testPrimitiveValue("");
  55. testPrimitiveValue("foo");
  56. testPrimitiveValue("true");
  57. }
  58. function testCoercibleToNumberOne(test) {
  59. function testPrimitiveValue(value) {
  60. test(value);
  61. // ToPrimitive
  62. testPrimitiveWrappers(value, "number", test);
  63. }
  64. testPrimitiveValue(true);
  65. testPrimitiveValue(1);
  66. testPrimitiveValue("1");
  67. }
  68. function testCoercibleToIntegerFromInteger(nominalInteger, test) {
  69. assert(Number.isInteger(nominalInteger));
  70. function testPrimitiveValue(value) {
  71. test(value);
  72. // ToPrimitive
  73. testPrimitiveWrappers(value, "number", test);
  74. // Non-primitive values that coerce to the nominal integer:
  75. // toString() returns a string that parsers to a primitive value.
  76. test([value]);
  77. }
  78. function testPrimitiveNumber(number) {
  79. testPrimitiveValue(number);
  80. // ToNumber: String -> Number
  81. testPrimitiveValue(number.toString());
  82. }
  83. testPrimitiveNumber(nominalInteger);
  84. // ToInteger: floor(abs(number))
  85. if (nominalInteger >= 0) {
  86. testPrimitiveNumber(nominalInteger + 0.9);
  87. }
  88. if (nominalInteger <= 0) {
  89. testPrimitiveNumber(nominalInteger - 0.9);
  90. }
  91. }
  92. function testPrimitiveWrappers(primitiveValue, hint, test) {
  93. if (primitiveValue != null) {
  94. // null and undefined result in {} rather than a proper wrapper,
  95. // so skip this case for those values.
  96. test(Object(primitiveValue));
  97. }
  98. testCoercibleToPrimitiveWithMethod(hint, function() {
  99. return primitiveValue;
  100. }, test);
  101. }
  102. function testCoercibleToPrimitiveWithMethod(hint, method, test) {
  103. var methodNames;
  104. if (hint === "number") {
  105. methodNames = ["valueOf", "toString"];
  106. } else if (hint === "string") {
  107. methodNames = ["toString", "valueOf"];
  108. } else {
  109. throw new Test262Error();
  110. }
  111. // precedence order
  112. test({
  113. [Symbol.toPrimitive]: method,
  114. [methodNames[0]]: function() { throw new Test262Error(); },
  115. [methodNames[1]]: function() { throw new Test262Error(); },
  116. });
  117. test({
  118. [methodNames[0]]: method,
  119. [methodNames[1]]: function() { throw new Test262Error(); },
  120. });
  121. if (hint === "number") {
  122. // The default valueOf returns an object, which is unsuitable.
  123. // The default toString returns a String, which is suitable.
  124. // Therefore this test only works for valueOf falling back to toString.
  125. test({
  126. // this is toString:
  127. [methodNames[1]]: method,
  128. });
  129. }
  130. // GetMethod: if func is undefined or null, return undefined.
  131. test({
  132. [Symbol.toPrimitive]: undefined,
  133. [methodNames[0]]: method,
  134. [methodNames[1]]: method,
  135. });
  136. test({
  137. [Symbol.toPrimitive]: null,
  138. [methodNames[0]]: method,
  139. [methodNames[1]]: method,
  140. });
  141. // if methodNames[0] is not callable, fallback to methodNames[1]
  142. test({
  143. [methodNames[0]]: null,
  144. [methodNames[1]]: method,
  145. });
  146. test({
  147. [methodNames[0]]: 1,
  148. [methodNames[1]]: method,
  149. });
  150. test({
  151. [methodNames[0]]: {},
  152. [methodNames[1]]: method,
  153. });
  154. // if methodNames[0] returns an object, fallback to methodNames[1]
  155. test({
  156. [methodNames[0]]: function() { return {}; },
  157. [methodNames[1]]: method,
  158. });
  159. test({
  160. [methodNames[0]]: function() { return Object(1); },
  161. [methodNames[1]]: method,
  162. });
  163. }
  164. function testNotCoercibleToIndex(test) {
  165. function testPrimitiveValue(value) {
  166. test(RangeError, value);
  167. // ToPrimitive
  168. testPrimitiveWrappers(value, "number", function(value) {
  169. test(RangeError, value);
  170. });
  171. }
  172. // Let integerIndex be ? ToInteger(value).
  173. testNotCoercibleToInteger(test);
  174. // If integerIndex < 0, throw a RangeError exception.
  175. testPrimitiveValue(-1);
  176. testPrimitiveValue(-2.5);
  177. testPrimitiveValue("-2.5");
  178. testPrimitiveValue(-Infinity);
  179. // Let index be ! ToLength(integerIndex).
  180. // If SameValueZero(integerIndex, index) is false, throw a RangeError exception.
  181. testPrimitiveValue(2 ** 53);
  182. testPrimitiveValue(Infinity);
  183. }
  184. function testNotCoercibleToInteger(test) {
  185. // ToInteger only throws from ToNumber.
  186. testNotCoercibleToNumber(test);
  187. }
  188. function testNotCoercibleToNumber(test) {
  189. function testPrimitiveValue(value) {
  190. test(TypeError, value);
  191. // ToPrimitive
  192. testPrimitiveWrappers(value, "number", function(value) {
  193. test(TypeError, value);
  194. });
  195. }
  196. // ToNumber: Symbol -> TypeError
  197. testPrimitiveValue(Symbol("1"));
  198. if (typeof BigInt !== "undefined") {
  199. // ToNumber: BigInt -> TypeError
  200. testPrimitiveValue(BigInt(0));
  201. }
  202. // ToPrimitive
  203. testNotCoercibleToPrimitive("number", test);
  204. }
  205. function testNotCoercibleToPrimitive(hint, test) {
  206. function MyError() {}
  207. // ToPrimitive: input[@@toPrimitive] is not callable (and non-null)
  208. test(TypeError, {[Symbol.toPrimitive]: 1});
  209. test(TypeError, {[Symbol.toPrimitive]: {}});
  210. // ToPrimitive: input[@@toPrimitive] returns object
  211. test(TypeError, {[Symbol.toPrimitive]: function() { return Object(1); }});
  212. test(TypeError, {[Symbol.toPrimitive]: function() { return {}; }});
  213. // ToPrimitive: input[@@toPrimitive] throws
  214. test(MyError, {[Symbol.toPrimitive]: function() { throw new MyError(); }});
  215. // OrdinaryToPrimitive: method throws
  216. testCoercibleToPrimitiveWithMethod(hint, function() {
  217. throw new MyError();
  218. }, function(value) {
  219. test(MyError, value);
  220. });
  221. // OrdinaryToPrimitive: both methods are unsuitable
  222. function testUnsuitableMethod(method) {
  223. test(TypeError, {valueOf:method, toString:method});
  224. }
  225. // not callable:
  226. testUnsuitableMethod(null);
  227. testUnsuitableMethod(1);
  228. testUnsuitableMethod({});
  229. // returns object:
  230. testUnsuitableMethod(function() { return Object(1); });
  231. testUnsuitableMethod(function() { return {}; });
  232. }
  233. function testCoercibleToString(test) {
  234. function testPrimitiveValue(value, expectedString) {
  235. test(value, expectedString);
  236. // ToPrimitive
  237. testPrimitiveWrappers(value, "string", function(value) {
  238. test(value, expectedString);
  239. });
  240. }
  241. testPrimitiveValue(undefined, "undefined");
  242. testPrimitiveValue(null, "null");
  243. testPrimitiveValue(true, "true");
  244. testPrimitiveValue(false, "false");
  245. testPrimitiveValue(0, "0");
  246. testPrimitiveValue(-0, "0");
  247. testPrimitiveValue(Infinity, "Infinity");
  248. testPrimitiveValue(-Infinity, "-Infinity");
  249. testPrimitiveValue(123.456, "123.456");
  250. testPrimitiveValue(-123.456, "-123.456");
  251. testPrimitiveValue("", "");
  252. testPrimitiveValue("foo", "foo");
  253. if (typeof BigInt !== "undefined") {
  254. // BigInt -> TypeError
  255. testPrimitiveValue(BigInt(0), "0");
  256. }
  257. // toString of a few objects
  258. test([], "");
  259. test(["foo", "bar"], "foo,bar");
  260. test({}, "[object Object]");
  261. }
  262. function testNotCoercibleToString(test) {
  263. function testPrimitiveValue(value) {
  264. test(TypeError, value);
  265. // ToPrimitive
  266. testPrimitiveWrappers(value, "string", function(value) {
  267. test(TypeError, value);
  268. });
  269. }
  270. // Symbol -> TypeError
  271. testPrimitiveValue(Symbol("1"));
  272. // ToPrimitive
  273. testNotCoercibleToPrimitive("string", test);
  274. }
  275. function testCoercibleToBooleanTrue(test) {
  276. test(true);
  277. test(1);
  278. test("string");
  279. test(Symbol("1"));
  280. test({});
  281. }
  282. function testCoercibleToBooleanFalse(test) {
  283. test(undefined);
  284. test(null);
  285. test(false);
  286. test(0);
  287. test(-0);
  288. test(NaN);
  289. test("");
  290. }
  291. function testCoercibleToBigIntZero(test) {
  292. function testPrimitiveValue(value) {
  293. test(value);
  294. // ToPrimitive
  295. testPrimitiveWrappers(value, "number", test);
  296. }
  297. testCoercibleToBigIntFromBigInt(BigInt(0), test);
  298. testPrimitiveValue(-BigInt(0));
  299. testPrimitiveValue("-0");
  300. testPrimitiveValue(false);
  301. testPrimitiveValue("");
  302. testPrimitiveValue(" ");
  303. // toString() returns ""
  304. test([]);
  305. // toString() returns "0"
  306. test([0]);
  307. }
  308. function testCoercibleToBigIntOne(test) {
  309. function testPrimitiveValue(value) {
  310. test(value);
  311. // ToPrimitive
  312. testPrimitiveWrappers(value, "number", test);
  313. }
  314. testCoercibleToBigIntFromBigInt(BigInt(1), test);
  315. testPrimitiveValue(true);
  316. // toString() returns "1"
  317. test([1]);
  318. }
  319. function testCoercibleToBigIntFromBigInt(nominalBigInt, test) {
  320. function testPrimitiveValue(value) {
  321. test(value);
  322. // ToPrimitive
  323. testPrimitiveWrappers(value, "number", test);
  324. }
  325. testPrimitiveValue(nominalBigInt);
  326. testPrimitiveValue(nominalBigInt.toString());
  327. testPrimitiveValue("0b" + nominalBigInt.toString(2));
  328. testPrimitiveValue("0o" + nominalBigInt.toString(8));
  329. testPrimitiveValue("0x" + nominalBigInt.toString(16));
  330. testPrimitiveValue(" " + nominalBigInt.toString() + " ");
  331. // toString() returns the decimal string representation
  332. test([nominalBigInt]);
  333. test([nominalBigInt.toString()]);
  334. }
  335. function testNotCoercibleToBigInt(test) {
  336. function testPrimitiveValue(error, value) {
  337. test(error, value);
  338. // ToPrimitive
  339. testPrimitiveWrappers(value, "number", function(value) {
  340. test(error, value);
  341. });
  342. }
  343. // Undefined, Null, Number, Symbol -> TypeError
  344. testPrimitiveValue(TypeError, undefined);
  345. testPrimitiveValue(TypeError, null);
  346. testPrimitiveValue(TypeError, 0);
  347. testPrimitiveValue(TypeError, NaN);
  348. testPrimitiveValue(TypeError, Infinity);
  349. testPrimitiveValue(TypeError, Symbol("1"));
  350. // when a String parses to NaN -> SyntaxError
  351. function testStringValue(string) {
  352. testPrimitiveValue(SyntaxError, string);
  353. testPrimitiveValue(SyntaxError, " " + string);
  354. testPrimitiveValue(SyntaxError, string + " ");
  355. testPrimitiveValue(SyntaxError, " " + string + " ");
  356. }
  357. testStringValue("a");
  358. testStringValue("0b2");
  359. testStringValue("0o8");
  360. testStringValue("0xg");
  361. testStringValue("1n");
  362. }