ES6ArrayAPI.js 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. // ES6 Array extension tests -- verifies the API shape and basic functionality
  6. WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
  7. var tests = [
  8. {
  9. name: "Array constructor has correct functions",
  10. body: function() {
  11. assert.isTrue(Array.hasOwnProperty('from'), "Array.hasOwnProperty('from');");
  12. assert.areEqual('function', typeof Array.from, "typeof Array.from === 'function'");
  13. assert.areEqual(1, Array.from.length, "Array.from.length === 0");
  14. assert.isTrue(Array.hasOwnProperty('of'), "Array.hasOwnProperty('of');");
  15. assert.areEqual('function', typeof Array.of, "typeof Array.of === 'function'");
  16. assert.areEqual(0, Array.of.length, "Array.of.length === 0");
  17. }
  18. },
  19. {
  20. name: "[0].indexOf(-0.0) should return 0",
  21. body: function() {
  22. assert.areEqual(0, [0].indexOf(-0.0), "[0].indexOf(-0.0) should return 0");
  23. }
  24. },
  25. {
  26. name: "Array.from basic behavior",
  27. body: function() {
  28. assert.areEqual([], Array.from([]), "Array.from simplest usage is copying empty array");
  29. assert.areEqual([], Array.from([], undefined), "Array.from disables mapping function when the param is explicitly passed as undefined");
  30. assert.areEqual([0,1,2,3], Array.from([0,1,2,3]), "Array.from basic behavior with an iterable object");
  31. assert.areEqual([0,1,2,3], Array.from({ 0: 0, 1: 1, 2: 2, 3: 3, length: 4 }), "Array.from basic behavior with an object with length but no iterator");
  32. }
  33. },
  34. {
  35. name: "Array.from special behaviors",
  36. body: function() {
  37. var fromFnc = Array.from;
  38. var b = fromFnc.call(Array, "string");
  39. assert.areEqual('object', typeof b, "Array.from.call(Array, 'string') returns an array");
  40. assert.areEqual(['s','t','r','i','n','g'], b, "Array.from.call(Array, 'string') == ['s','t','r','i','n','g']");
  41. assert.areEqual(6, b.length, "Array.from.call(Array, 'string').length === 6");
  42. assert.isFalse(ArrayBuffer.isView(b), "Array.from.call(Array, 'string') is not a TypedArray");
  43. var b = fromFnc.call(String, [0,1,2,3]);
  44. assert.areEqual('object', typeof b, "Array.from.call(String, [0,1,2,3]) returns a String object");
  45. assert.areEqual('', b.toString(), "Array.from.call(String, [0,1,2,3]).toString() == '4'");
  46. assert.areEqual(0, b.length, "Array.from.call(String, [0,1,2,3]).length === 1");
  47. assert.isFalse(ArrayBuffer.isView(b), "Array.from.call(String, [0,1,2,3]) is not a TypedArray");
  48. assert.areEqual(0, b[0], "Integer-indexed properties are still added to the string");
  49. assert.areEqual(1, b[1], "Integer-indexed properties are still added to the string");
  50. assert.areEqual(2, b[2], "Integer-indexed properties are still added to the string");
  51. assert.areEqual(3, b[3], "Integer-indexed properties are still added to the string");
  52. var a = { 0: 0, 1: 1, 2: 2, 3: 3, length: 4 };
  53. assert.throws(function () { fromFnc.call(String, a); }, TypeError, "String's properties are non-configurable", "Cannot redefine property '0'");
  54. assert.throws(function() { fromFnc.call(Uint8Array, { 0: 0, 1: 1, 2: 2, length: 5 }); }, TypeError, "Array.from tries to set length of the object returned from the constructor which will throw for TypedArrays", "Cannot define property: object is not extensible");
  55. var a = { 0: 0, 1: 1, 3: 3, length: 5 };
  56. var b = fromFnc.call(Array, a);
  57. assert.areEqual('object', typeof b, "Array.from.call(Array, objectWithLengthProperty) returns an object");
  58. assert.areEqual('0,1,,3,', b.toString(), "Array.from.call(String, [0,1,2,3]).toString() == '4'");
  59. assert.areEqual(5, b.length, "Array.from.call(Array, objectWithLengthProperty) returns a new array with length = a.length");
  60. assert.isFalse(ArrayBuffer.isView(b), "Array.from.call(Array, objectWithLengthProperty) is not a TypedArray (ArrayBuffer.isView)");
  61. assert.areEqual([0,1,undefined,3,undefined], b, "Array.from.call(Array, objectWithLengthProperty) has missing values set to undefined");
  62. var a = { 0: 0, 1: 1 };
  63. var b = fromFnc.call(Array, a);
  64. assert.areEqual('object', typeof b, "Array.from.call(Array, objectWithLengthNoProperty) returns an object");
  65. assert.areEqual(0, b.length, "Array.from.call(Array, objectWithLengthNoProperty) returns a new array with length = 0");
  66. assert.isFalse(ArrayBuffer.isView(b), "Array.from.call(Array, objectWithLengthNoProperty) is not a TypedArray (ArrayBuffer.isView)");
  67. assert.areEqual([], b, "Array.from.call(Array, objectWithLengthNoProperty) is an empty array");
  68. assert.areEqual([0,1,2], fromFnc.call(undefined, [0,1,2]), "Calling Array.from with undefined this argument produces an array");
  69. assert.areEqual([0,1,2], fromFnc.call(null, [0,1,2]), "Calling Array.from with null this argument produces an array");
  70. assert.areEqual([0,1,2], fromFnc.call({}, [0,1,2]), "Calling Array.from with a non-function this argument produces an array");
  71. assert.areEqual([0,1,2], fromFnc.call(Math.sin, [0,1,2]), "Calling Array.from with a non-constructor function this argument produces an array");
  72. }
  73. },
  74. {
  75. name: "Array.from error conditions",
  76. body: function() {
  77. assert.throws(function () { Array.from(); }, TypeError, "Calling Array.from with non-object items argument throws TypeError", "Array.from: argument is not an Object");
  78. assert.throws(function () { Array.from(undefined); }, TypeError, "Calling Array.from with non-object items argument throws TypeError", "Array.from: argument is not an Object");
  79. assert.throws(function () { Array.from(null); }, TypeError, "Calling Array.from with non-object items argument throws TypeError", "Array.from: argument is not an Object");
  80. assert.throws(function () { Array.from({}, null); }, TypeError, "Calling Array.from with non-object mapFn argument throws TypeError", "Array.from: argument is not a Function object");
  81. assert.throws(function () { Array.from({}, 'string'); }, TypeError, "Calling Array.from with non-object mapFn argument throws TypeError", "Array.from: argument is not a Function object");
  82. assert.throws(function () { Array.from({}, {}); }, TypeError, "Calling Array.from with non-function mapFn argument throws TypeError", "Array.from: argument is not a Function object");
  83. }
  84. },
  85. {
  86. name: "Array.from behavior with a map function",
  87. body: function() {
  88. var i = 0;
  89. function mapFunction(val, k) {
  90. assert.areEqual(i, k, "Array.from called with a mapping function, we should get the elements in order. Setting item[" + k + "] = " + val);
  91. assert.areEqual(val, k, "Array.from called with a mapping function, Value and index should be same for this test");
  92. assert.areEqual(2, arguments.length, "Array.from called with a mapping function, only 2 elements should be passed as arguments");
  93. // increment expected index
  94. i++;
  95. }
  96. var objectWithoutIterator = {
  97. 0: 0,
  98. 1: 1,
  99. 2: 2,
  100. 3: 3,
  101. length: 4
  102. };
  103. // Verify mapFunction gets called with correct arguments
  104. Array.from(objectWithoutIterator, mapFunction);
  105. }
  106. },
  107. {
  108. name: "Array.from behavior with a map function passing this argument",
  109. body: function() {
  110. var thisArg = 'thisArg';
  111. function mapFunction(val, k) {
  112. // this will be wrapped as an Object
  113. assert.areEqual(Object(thisArg), this, "thisArg passed into Array.from should flow into mapFunction");
  114. }
  115. var objectWithoutIterator = {
  116. 0: 0,
  117. 1: 1,
  118. 2: 2,
  119. 3: 3,
  120. length: 4
  121. };
  122. // Verify mapFunction gets called with thisArg passed as this
  123. Array.from(objectWithoutIterator, mapFunction, thisArg);
  124. }
  125. },
  126. {
  127. name: "Array.from behavior with a map function that mutates source object",
  128. body: function() {
  129. var objectWithoutIterator = {
  130. 0: 0,
  131. 1: 1,
  132. 2: 2,
  133. 3: 3,
  134. 4: 4,
  135. length: 5
  136. };
  137. function mapFunction(val, k) {
  138. switch (k) {
  139. case 0:
  140. // change the objectWithoutIterator length value - we should still fetch the rest of the indexed-elements anyway
  141. objectWithoutIterator.length = 0;
  142. return val;
  143. case 1:
  144. // change the value of the next indexed value - the new value should end up in the return object
  145. objectWithoutIterator[2] = 200;
  146. return val;
  147. case 2:
  148. // change the value of a previous indexed value - the old value should end up in the return object
  149. objectWithoutIterator[0] = -100;
  150. return val;
  151. case 3:
  152. // delete the next indexed value - return object should have undefined there
  153. delete objectWithoutIterator[4];
  154. return val;
  155. }
  156. // otherwise
  157. return val;
  158. }
  159. assert.areEqual([0,1,200,3,undefined], Array.from(objectWithoutIterator, mapFunction), "Array.from called with a map function that mutates the source object");
  160. }
  161. },
  162. {
  163. name: "Array.from behavior with iterator and a map function",
  164. body: function() {
  165. var j = 0;
  166. var checkThisArg = false;
  167. var thisArg = 'string';
  168. var objectWithIterator = {
  169. [Symbol.iterator]: function() {
  170. return {
  171. i: 0,
  172. next: function () {
  173. return {
  174. done: this.i == 5,
  175. value: this.i++
  176. };
  177. }
  178. };
  179. }
  180. };
  181. function mapFunction(val, k) {
  182. assert.areEqual(j, val, "Array.from called with a mapping function, we should get the elements in order. Setting item[" + j + "] = " + val);
  183. assert.areEqual(val, k, "Array.from called with a mapping function, index should match the value passed in");
  184. assert.areEqual(2, arguments.length, "Array.from called with a mapping function, only 2 elements should be passed as arguments");
  185. // increment expected value
  186. j++;
  187. if (checkThisArg) {
  188. // this will be wrapped as an Object
  189. assert.areEqual(Object(thisArg), this, "thisArg passed into Array.from should flow into mapFunction");
  190. }
  191. }
  192. // Verify mapFunction gets called with correct arguments
  193. Array.from(objectWithIterator, mapFunction);
  194. j = 0;
  195. checkThisArg = true;
  196. // Verify mapFunction gets called with thisArg passed as this
  197. Array.from(objectWithIterator, mapFunction, thisArg);
  198. }
  199. },
  200. {
  201. name: "Array.from behavior with iterator and a map function which mutates the iterator state",
  202. body: function() {
  203. var iterator_val = 0;
  204. var objectWithIterator = {
  205. [Symbol.iterator]: function() {
  206. return {
  207. next: function () {
  208. return {
  209. done: iterator_val == 5,
  210. value: iterator_val++
  211. };
  212. }
  213. };
  214. }
  215. };
  216. var reset = false;
  217. function mapFunction(val, k) {
  218. if (val == 2 && !reset)
  219. {
  220. reset = true;
  221. iterator_val = 0;
  222. }
  223. return val;
  224. }
  225. assert.areEqual([0,1,2,0,1,2,3,4], Array.from(objectWithIterator, mapFunction), "Array.from called with map function which alters iterator state");
  226. }
  227. },
  228. {
  229. name: "Array.from behavior with badly formed iterator objects",
  230. body: function() {
  231. var objectWithIteratorThatIsNotAFunction = { [Symbol.iterator]: 'a string' };
  232. var objectWithIteratorWhichDoesNotReturnObjects = { [Symbol.iterator]: function() { return undefined; } };
  233. var objectWithIteratorNextIsNotAFunction = { [Symbol.iterator]: function() { return { next: undefined }; } };
  234. var objectWithIteratorNextDoesNotReturnObjects = { [Symbol.iterator]: function() { return { next: function() { return undefined; } }; } };
  235. assert.throws(function() { Array.from(objectWithIteratorThatIsNotAFunction); }, TypeError, "obj[@@iterator] must be a function", "Function expected");
  236. assert.throws(function() { Array.from(objectWithIteratorWhichDoesNotReturnObjects); }, TypeError, "obj[@@iterator] must return an object", "Object expected");
  237. assert.throws(function() { Array.from(objectWithIteratorNextIsNotAFunction); }, TypeError, "obj[@@iterator].next must be a function", "Function expected");
  238. assert.throws(function() { Array.from(objectWithIteratorNextDoesNotReturnObjects); }, TypeError, "obj[@@iterator].next must return an object", "Object expected");
  239. var objectWithUndefinedIterator = { 0: "a", 1: "b", length: 2, [Symbol.iterator]: undefined };
  240. var objectWithNullIterator = { 0: "a", 1: "b", length: 2, [Symbol.iterator]: null };
  241. assert.areEqual([ "a", "b" ], Array.from(objectWithUndefinedIterator), "'@@iterator: undefined' is ignored");
  242. assert.areEqual([ "a", "b" ], Array.from(objectWithNullIterator), "'@@iterator: null' is ignored");
  243. }
  244. },
  245. {
  246. name: "Array.of basic behavior",
  247. body: function() {
  248. assert.areEqual([], Array.of(), "Array.of basic behavior with no arguments");
  249. assert.areEqual([3], Array.of(3), "Array.of basic behavior with a single argument");
  250. assert.areEqual([0,1,2,3], Array.of(0, 1, 2, 3), "Array.of basic behavior with a list of arguments");
  251. }
  252. },
  253. {
  254. name: "Array.of extended behavior",
  255. body: function() {
  256. var ofFnc = Array.of;
  257. assert.throws(function() { ofFnc.call(Uint8ClampedArray, 0, -1, 2, 300, 4); }, TypeError, "Array.of tries to set length of the object returned from the constructor which will throw for TypedArrays", "Cannot define property: object is not extensible");
  258. var b = ofFnc.call(Array, 'string', 'other string', 5, { 0: 'string val',length:10 });
  259. assert.areEqual('object', typeof b, "Array.of.call(Array, ...someStringsAndObjects) returns an array");
  260. assert.areEqual(['string','other string',5,{ 0: 'string val',length:10 }], b, "Array.of.call(Array, ...someStringsAndObjects) == ['string','other string',5,{ 0: 'string val',length:10 }]");
  261. assert.areEqual(4, b.length, "Array.of.call(Array, ...someStringsAndObjects).length === 4");
  262. assert.isFalse(ArrayBuffer.isView(b), "Array.of.call(Array, ...someStringsAndObjects) is not a TypedArray");
  263. assert.throws(function () { ofFnc.call(String, 0, 1, 2, 3); }, TypeError, "String's properties are non-configurable", "Cannot redefine property '0'");
  264. assert.areEqual([], ofFnc.call(Array), "Array.of.call(Array) returns empty array");
  265. assert.areEqual([], ofFnc.call(), "Array.of.call() returns empty array");
  266. }
  267. },
  268. {
  269. name: "OS:840217 - Array.from, Array#fill, Array#lastIndexOf should use ToLength instead of ToUint32 on their parameter's length property",
  270. body: function() {
  271. // ToLength(-1) should be 0 which means we won't execute any iterations in the below calls.
  272. Array.from({length: -1});
  273. Array.prototype.fill.call({length: -1}, 'a');
  274. Array.prototype.lastIndexOf.call({length: -1}, 'a');
  275. }
  276. },
  277. {
  278. name: "Array.from called with an items object that has length > 2^32-1 and is not iterable",
  279. body: function() {
  280. var o = {length: 4294967301};
  281. assert.throws(function() { Array.from(o); }, RangeError, "Array.from uses abstract operation ArrayCreate which throws RangeError when requested length > 2^32-1", "Array length must be a finite positive integer");
  282. }
  283. },
  284. {
  285. name: "Array.from doesn't get @@iterator twice",
  286. body: function () {
  287. let count = 0;
  288. Array.from({
  289. get [Symbol.iterator]() {
  290. count++;
  291. return [][Symbol.iterator];
  292. }
  293. });
  294. assert.areEqual(count, 1, "Array.from calls @@iterator getter once");
  295. count = 0;
  296. Array.from(new Proxy({}, {
  297. get(target, property) {
  298. if (property === Symbol.iterator) {
  299. count++;
  300. return [][Symbol.iterator];
  301. }
  302. return Reflect.get(target, property);
  303. }
  304. }));
  305. assert.areEqual(count, 1, "Array.from calls proxy's getter with @@iterator as parameter only once");
  306. }
  307. },
  308. {
  309. name: "Array#fill called with an object that has length > 2^32-1",
  310. body: function() {
  311. var e = {length: 4294967301, 4294967297: 1234};
  312. Array.prototype.fill.call(e, 5678, 4294967298, 4294967300);
  313. assert.areEqual(1234, e[4294967297], "Array.prototype.fill called on an object with length > 2^32 and a fill range existing completely past 2^32 does not fill elements outside the request range");
  314. assert.areEqual(5678, e[4294967298], "Array.prototype.fill called on an object with length > 2^32 and a fill range existing completely past 2^32 is able to fill elements with indices > 2^32");
  315. assert.areEqual(5678, e[4294967299], "Array.prototype.fill called on an object with length > 2^32 and a fill range existing completely past 2^32 is able to fill elements with indices > 2^32");
  316. assert.areEqual(undefined, e[4294967300], "Array.prototype.fill called on an object with length > 2^32 and a fill range existing completely past 2^32 does not fill elements outside the request range");
  317. var e = {length: 4294967301, 4294967292: 1234};
  318. Array.prototype.fill.call(e, 5678, 4294967293, 4294967300);
  319. assert.areEqual(1234, e[4294967292], "Array.prototype.fill called on an object with length > 2^32 and a fill range starting before 2^32 but ending after 2^32 does not fill elements outside the request range");
  320. assert.areEqual(5678, e[4294967293], "Array.prototype.fill called on an object with length > 2^32 and a fill range starting before 2^32 but ending after 2^32 is able to fill elements with indices > 2^32");
  321. assert.areEqual(5678, e[4294967294], "Array.prototype.fill called on an object with length > 2^32 and a fill range starting before 2^32 but ending after 2^32 is able to fill elements with indices > 2^32");
  322. assert.areEqual(5678, e[4294967295], "Array.prototype.fill called on an object with length > 2^32 and a fill range starting before 2^32 but ending after 2^32 is able to fill elements with indices > 2^32");
  323. assert.areEqual(5678, e[4294967299], "Array.prototype.fill called on an object with length > 2^32 and a fill range starting before 2^32 but ending after 2^32 is able to fill elements with indices > 2^32");
  324. assert.areEqual(undefined, e[4294967300], "Array.prototype.fill called on an object with length > 2^32 and a fill range starting before 2^32 but ending after 2^32 does not fill elements outside the request range");
  325. }
  326. },
  327. {
  328. name: "Array#lastIndexOf called with an object that has length > 2^32-1",
  329. body: function() {
  330. var e = {length: 4294967301, 4294967291: 1234, 4294967294: 5678, 4294967295: 5678, 4294967296: 5678, 4294967297: 9};
  331. assert.areEqual(4294967291, Array.prototype.lastIndexOf.call(e, 1234, 4294967300), "Array.prototype.lastIndexOf called on an object with length > 2^32 and a fromIndex also > 2^32 finds the element when expected index is < 2^32");
  332. assert.areEqual(4294967296, Array.prototype.lastIndexOf.call(e, 5678, 4294967300), "Array.prototype.lastIndexOf called on an object with length > 2^32 and a fromIndex also > 2^32 finds the element when expected index is > 2^32");
  333. var e = [1,2,3,4];
  334. assert.areEqual(0, Array.prototype.lastIndexOf.call(e, 1, 4294967296), "Array.prototype.lastIndexOf is able to find the element when it exists at index 0 when fromIndex > 2^32");
  335. assert.areEqual(0, Array.prototype.lastIndexOf.call(e, 1), "Array.prototype.lastIndexOf is able to find the element when it exists at index 0 when fromIndex not defined");
  336. }
  337. },
  338. {
  339. name: "Array#copyWithin called with an object that has length > 2^32-1 and parameters also > 2^32-1",
  340. body: function() {
  341. var e = {length: 4294967301, 4294967292: 4294967292, 4294967293: 4294967293, 4294967294: 4294967294};
  342. Array.prototype.copyWithin.call(e, 5, 4294967292, 4294967294);
  343. assert.areEqual(4294967292, e[5], "Array.prototype.copyWithin called on an object with length > 2^32 and a source and destination range completely < 2^32");
  344. assert.areEqual(4294967293, e[6], "Array.prototype.copyWithin called on an object with length > 2^32 and a source and destination range completely < 2^32");
  345. var e = {length: 4294967304, 4294967300: 4294967300, 4294967301: 4294967301, 4294967302: 4294967302, 4294967303: 4294967303};
  346. Array.prototype.copyWithin.call(e, 4294967297, 4294967300, 4294967303);
  347. assert.areEqual(4294967300, e[4294967297], "Array.prototype.copyWithin called on an object with length > 2^32 and a source and destination range completely > 2^32");
  348. assert.areEqual(4294967301, e[4294967298], "Array.prototype.copyWithin called on an object with length > 2^32 and a source and destination range completely > 2^32");
  349. assert.areEqual(4294967302, e[4294967299], "Array.prototype.copyWithin called on an object with length > 2^32 and a source and destination range completely > 2^32");
  350. var e = {length: 4294967301, 4294967297: 4294967297, 4294967298: 4294967298, 4294967299: 4294967299};
  351. Array.prototype.copyWithin.call(e, 5, 4294967297, 4294967300);
  352. assert.areEqual(4294967297, e[5], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range completely > 2^32 and destination range completely < 2^32");
  353. assert.areEqual(4294967298, e[6], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range completely > 2^32 and destination range completely < 2^32");
  354. assert.areEqual(4294967299, e[7], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range completely > 2^32 and destination range completely < 2^32");
  355. var e = {length: 4294967301, 0: 0, 1: 1, 2: 2, 3: 3, 4: 4};
  356. Array.prototype.copyWithin.call(e, 4294967297, 0, 5);
  357. assert.areEqual(0, e[4294967297], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range completely < 2^32 and destination range completely > 2^32");
  358. assert.areEqual(1, e[4294967298], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range completely < 2^32 and destination range completely > 2^32");
  359. assert.areEqual(2, e[4294967299], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range completely < 2^32 and destination range completely > 2^32");
  360. assert.areEqual(3, e[4294967300], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range completely < 2^32 and destination range completely > 2^32");
  361. var e = {length: 4294967301, 4294967292: 4294967292, 4294967293: 4294967293, 4294967294: 4294967294, 4294967295: 4294967295, 4294967296: 4294967296, 4294967297: 4294967297, 4294967298: 4294967298, 4294967299: 4294967299, 4294967300: 4294967300};
  362. Array.prototype.copyWithin.call(e, 5, 4294967292, 4294967301);
  363. assert.areEqual(4294967292, e[5], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range crossing 2^32 and destination range completely < 2^32");
  364. assert.areEqual(4294967293, e[6], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range crossing 2^32 and destination range completely < 2^32");
  365. assert.areEqual(4294967294, e[7], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range crossing 2^32 and destination range completely < 2^32");
  366. assert.areEqual(4294967295, e[8], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range crossing 2^32 and destination range completely < 2^32");
  367. assert.areEqual(4294967296, e[9], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range crossing 2^32 and destination range completely < 2^32");
  368. assert.areEqual(4294967297, e[10], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range crossing 2^32 and destination range completely < 2^32");
  369. assert.areEqual(4294967298, e[11], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range crossing 2^32 and destination range completely < 2^32");
  370. assert.areEqual(4294967299, e[12], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range crossing 2^32 and destination range completely < 2^32");
  371. assert.areEqual(4294967300, e[13], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range crossing 2^32 and destination range completely < 2^32");
  372. var e = {length: 4294967400, 4294967292: 4294967292, 4294967293: 4294967293, 4294967294: 4294967294, 4294967295: 4294967295, 4294967296: 4294967296, 4294967297: 4294967297, 4294967298: 4294967298, 4294967299: 4294967299, 4294967300: 4294967300};
  373. Array.prototype.copyWithin.call(e, 4294967350, 4294967292, 4294967301);
  374. assert.areEqual(4294967292, e[4294967350], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range crossing 2^32 and destination range completely > 2^32");
  375. assert.areEqual(4294967293, e[4294967351], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range crossing 2^32 and destination range completely > 2^32");
  376. assert.areEqual(4294967294, e[4294967352], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range crossing 2^32 and destination range completely > 2^32");
  377. assert.areEqual(4294967295, e[4294967353], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range crossing 2^32 and destination range completely > 2^32");
  378. assert.areEqual(4294967296, e[4294967354], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range crossing 2^32 and destination range completely > 2^32");
  379. assert.areEqual(4294967297, e[4294967355], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range crossing 2^32 and destination range completely > 2^32");
  380. assert.areEqual(4294967298, e[4294967356], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range crossing 2^32 and destination range completely > 2^32");
  381. assert.areEqual(4294967299, e[4294967357], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range crossing 2^32 and destination range completely > 2^32");
  382. assert.areEqual(4294967300, e[4294967358], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range crossing 2^32 and destination range completely > 2^32");
  383. var e = {length: 4294967301, 0: 0, 1: 1, 2: 2, 3: 3, 4: 4};
  384. Array.prototype.copyWithin.call(e, 4294967293, 0, 5);
  385. assert.areEqual(0, e[4294967293], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range completely < 2^32 and destination range crossing 2^32");
  386. assert.areEqual(1, e[4294967294], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range completely < 2^32 and destination range crossing 2^32");
  387. assert.areEqual(2, e[4294967295], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range completely < 2^32 and destination range crossing 2^32");
  388. assert.areEqual(3, e[4294967296], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range completely < 2^32 and destination range crossing 2^32");
  389. assert.areEqual(4, e[4294967297], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range completely < 2^32 and destination range crossing 2^32");
  390. var e = {length: 4294967420, 4294967400: 4294967400, 4294967401: 4294967401, 4294967402: 4294967402, 4294967403: 4294967403, 4294967404: 4294967404};
  391. Array.prototype.copyWithin.call(e, 4294967293, 4294967400, 4294967405);
  392. assert.areEqual(4294967400, e[4294967293], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range completely > 2^32 and destination range crossing 2^32");
  393. assert.areEqual(4294967401, e[4294967294], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range completely > 2^32 and destination range crossing 2^32");
  394. assert.areEqual(4294967402, e[4294967295], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range completely > 2^32 and destination range crossing 2^32");
  395. assert.areEqual(4294967403, e[4294967296], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range completely > 2^32 and destination range crossing 2^32");
  396. assert.areEqual(4294967404, e[4294967297], "Array.prototype.copyWithin called on an object with length > 2^32 and a source range completely > 2^32 and destination range crossing 2^32");
  397. }
  398. },
  399. {
  400. name: "Array#lastIndexOf called with a fromIndex < 0 && abs(fromIndex) > length (OS #1185913)",
  401. body: function() {
  402. var a = [0, 1];
  403. var r = a.lastIndexOf(0, -3);
  404. assert.areEqual(-1, r, "Array.prototype.lastIndexOf returns -1 when the search element is not found");
  405. }
  406. },
  407. {
  408. name: "Array.of() called with the a bound function without a constructor should not throw an exception",
  409. body: function () {
  410. var val = Math.cos.bind(Math);
  411. assert.isTrue(Array.isArray(Array.of.call(val)));
  412. }
  413. },
  414. {
  415. name: "Array.of() should not invoke setter",
  416. body: function () {
  417. function Bag() {}
  418. Bag.of = Array.of;
  419. Object.defineProperty(Bag.prototype, "0", {set: function(v) { /* no-op */ }});
  420. assert.areEqual(1, Bag.of(1)[0]);
  421. }
  422. },
  423. {
  424. name: "Array.from() should not invoke setter in iterable case",
  425. body: function () {
  426. function Bag() {}
  427. Bag.from = Array.from;
  428. Object.defineProperty(Bag.prototype, "0", {set: function(v) { throw "Fail"; }});
  429. var a = [1,2,3];
  430. assert.areEqual(1, Bag.from(a)[0]);
  431. }
  432. },
  433. {
  434. name: "Array.from() should not invoke setter in array like case",
  435. body: function () {
  436. function Bag() {}
  437. Bag.from = Array.from;
  438. Object.defineProperty(Bag.prototype, "0", {set: function(v) { throw "Fail"; }});
  439. var a = {};
  440. a[0] = 1;
  441. a[1] = 2;
  442. a[2] = 3;
  443. a.length = 3;
  444. assert.areEqual(1, Bag.from(a)[0]);
  445. }
  446. },
  447. {
  448. name: "Array.filter() should not invoke setter even with substituted constructor",
  449. body: function () {
  450. var a = [1,2,3];
  451. a.constructor = function()
  452. {
  453. function Bag() {};
  454. Object.defineProperty(Bag.prototype, "0", { set: function(v){ throw "Fail"; } });
  455. return new Bag();
  456. };
  457. assert.areEqual(1, a.filter(function(v){return v % 2 == 1;})[0]);
  458. }
  459. },
  460. {
  461. name: "Array.map() should not invoke setter even with substituted constructor",
  462. body: function () {
  463. var a = [1,2,3];
  464. a.constructor = function()
  465. {
  466. function Bag() {};
  467. Object.defineProperty(Bag.prototype, "0", { set: function(v){ throw "Fail"; } });
  468. return new Bag();
  469. };
  470. assert.areEqual(1, a.map(function(v){return v % 2;})[0]);
  471. }
  472. },
  473. {
  474. name: "Array.slice() should not invoke setter even with substituted constructor",
  475. body: function () {
  476. var a = [1,2,3];
  477. a.constructor = function()
  478. {
  479. function Bag() {};
  480. Object.defineProperty(Bag.prototype, "0", { set: function(v){ throw "Fail"; } });
  481. return new Bag();
  482. };
  483. assert.areEqual(2, a.slice(1, 3)[0]);
  484. }
  485. },
  486. {
  487. name: "Array.splice() should not invoke setter even with substituted constructor",
  488. body: function () {
  489. var a = [1,2,3];
  490. a.constructor = function()
  491. {
  492. function Bag() {};
  493. Object.defineProperty(Bag.prototype, "0", { set: function(v){ throw "Fail"; } });
  494. return new Bag();
  495. };
  496. assert.areEqual(1, a.splice(0, 1, 'x')[0]);
  497. }
  498. },
  499. {
  500. name: "Array.fill() should throw when applied on frozen array",
  501. body: function () {
  502. var x = [0];
  503. Object.freeze(x);
  504. assert.throws(function() { Array.prototype.fill.call(x) }, TypeError, "We should get a TypeError when fill is applied to a frozen array");
  505. }
  506. },
  507. {
  508. name: "Array.copyWithin() should throw when applied on frozen array",
  509. body: function () {
  510. var x = [1,2,3,4,5];
  511. Object.freeze(x);
  512. assert.throws(function() { Array.prototype.fill.copyWithin(x, 1, 2) }, TypeError, "We should get a TypeError when fill is applied to a frozen array");
  513. }
  514. },
  515. {
  516. name: "Array.concat() should always box the first item",
  517. body: function () {
  518. assert.isTrue(typeof Array.prototype.concat.call(101)[0] === "object");
  519. }
  520. },
  521. {
  522. name: "Boolean primitive should never be considered concat spreadable",
  523. body: function () {
  524. try
  525. {
  526. Boolean.prototype[Symbol.isConcatSpreadable] = true;
  527. Boolean.prototype[0] = 1;
  528. Boolean.prototype[1] = 2;
  529. Boolean.prototype[2] = 3;
  530. Boolean.prototype.length = 3;
  531. assert.isTrue([].concat(true).length === 1); /** True is added to the array as an literal, not spreaded */
  532. }
  533. finally
  534. {
  535. delete Boolean.prototype[Symbol.isConcatSpreadable];
  536. delete Boolean.prototype[0];
  537. delete Boolean.prototype[1];
  538. delete Boolean.prototype[2];
  539. delete Boolean.prototype.length;
  540. }
  541. }
  542. },
  543. {
  544. name: "String primitive should never be considered concat spreadable",
  545. body: function () {
  546. try
  547. {
  548. String.prototype[Symbol.isConcatSpreadable] = true;
  549. String.prototype[0] = 1;
  550. String.prototype[1] = 2;
  551. String.prototype[2] = 3;
  552. String.prototype.length = 3;
  553. assert.isTrue([].concat("Hello").length === 1); /** True is added to the array as an literal, not spreaded */
  554. }
  555. finally
  556. {
  557. delete String.prototype[Symbol.isConcatSpreadable];
  558. delete String.prototype[0];
  559. delete String.prototype[1];
  560. delete String.prototype[2];
  561. delete String.prototype.length;
  562. }
  563. }
  564. },
  565. {
  566. name: "Array methods trying to create a data property on non-configurable slot and fail",
  567. body: function () {
  568. var returnedArr = {};
  569. Object.defineProperty(returnedArr, '1', { configurable: false});
  570. var arr = [11, 21];
  571. Object.defineProperty(arr.constructor, Symbol.species, { get : function () { return function() {
  572. return returnedArr;
  573. } } } );
  574. function test(arr, desc) {
  575. desc = desc + " has species which gives an array with non-config property, validating on ";
  576. var error = "Cannot redefine property '1'";
  577. assert.throws(function () { Array.prototype.map.call(arr, function (a) { return a;}); }, TypeError, desc + "map", error);
  578. assert.throws(function () { Array.prototype.filter.call(arr, function (a) { return true;}); }, TypeError,desc + "filter", error);
  579. assert.throws(function () { Array.prototype.slice.call(arr, 0); }, TypeError, desc + "slice", error);
  580. assert.throws(function () { Array.prototype.concat.call(arr, [1, 2]); }, TypeError, desc + "concat", error);
  581. }
  582. test(arr, "var array");
  583. var arr2 = [11];
  584. Object.defineProperty(arr2, '1', {get : function () { return 33; } });
  585. Object.defineProperty(arr2.constructor, Symbol.species, { get : function () { return function() {
  586. return returnedArr;
  587. } } } );
  588. test(arr2, "es5 var array");
  589. function Arr() {
  590. Object.defineProperty(this, "0", {
  591. configurable: false
  592. });
  593. }
  594. assert.throws(function () { Array.of.call(Arr, "a"); }, TypeError, "of constructs an array with non-config property", "Cannot redefine property '0'");
  595. assert.throws(function () { Array.from.call(Arr, "a"); }, TypeError, "of constructs an array with non-config property", "Cannot redefine property '0'");
  596. }
  597. }
  598. ];
  599. testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });