| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
- function p(x) {
- WScript.Echo(x);
- }
- var tests = [
- {
- name: "Testing spread array literals (all should be the same)",
- body: function () {
- var a = [1, 2];
- var b = [3];
- var c = [];
- var d = [4, 5, 6];
- var joined = [];
- joined[0] = [1, 2, 3, 4, 5, 6];
- joined[1] = [...a, ...b, ...c, ...d];
- joined[2] = [1, 2, ...b, ...d];
- joined[3] = [...[1, 2], ...b, ...[], ...[...d]];
- joined[4] = [...[], ...joined[2], ...[], ...c];
- joined[5] = [...a, ...b, ...c, 4, 5, 6];
- for (var i = 0; i < joined.length; ++i) {
- assert.areEqual(joined[i], joined[0], "joined[" + i + "] + === + joined[" + 0 + "]");
- }
- }
- },
- {
- name: "Testing call with spread args (all should be the same)",
- body: function() {
- var a = [1, 2];
- var b = [3];
- var c = [];
- var d = [4, 5, 6];
- function quad(a, b, c, x) {
- return a*x*x + b*x + c;
- }
- var result = [];
- result[0] = quad(1, 2, 3, 4);
- result[1] = quad(...a, ...b, ...c, 4);
- result[2] = quad(...a, ...b, ...[4]);
- result[3] = quad(...[...a, ...b, ...c], 4);
- for (var j = 0; j < result.length; ++j) {
- assert.areEqual(result[j], result[0], "result[" + j + "] === result[0]");
- }
- }
- },
- {
- name: "Testing spread with a lot of padding numbers",
- body: function () {
- function quad(a, b, c, x) {
- return a*x*x + b*x + c;
- }
- var e = [7, 8];
- var f = [12, 13];
- var largeLiteral = [1, 2, 3, 4, 5, 6, ...e, 9, 10, 11, ...f, 14, 15, 16, 17, 18, 19, 20];
- var largeLiteralFull = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
- assert.areEqual(largeLiteral, largeLiteralFull, "Array literals equal");
- assert.areEqual(quad(1, 2, 3, 4, 5, 6, ...e, 9, 10, 11, ...f, 14, 15, 16, 17, 18, 19, 20), quad(1, 2, 3, 4, 5, 6), "Calls equal");
- }
- },
- {
- name: "Testing array literals with gaps",
- body: function () {
- assert.areEqual([1, undefined, 3], [...[1,,3]], "Spreading missing values gives undefined in the new array elements");
- assert.areEqual([undefined, undefined, 1, undefined, 2, 3, 1, 2, 3, 3], [...[...[,,1,,2,3], ...[1, 2, 3], 3]], "Spreading missing values gives undefined in the new array elements");
- }
- },
- {
- name: "Testing call spread args with gaps",
- body: function () {
- function spreadValid1(a, b, c, d) {
- assert.areEqual(1, a, "Spreading call arguments with array gaps: a is 1");
- assert.areEqual(undefined, b, "Spreading call arguments with array gaps: b is undefined");
- assert.areEqual(3, c, "Spreading call arguments with array gaps: c is 3");
- assert.areEqual(undefined, d, "Spreading call arguments with array gaps: d is undefined");
- }
- spreadValid1(...[1, , 3,]);
- function spreadValid2(e, f, g, h) {
- assert.areEqual(4, e, "Spreading nested call arguments with array gaps: e is 4");
- assert.areEqual(undefined, f, "Spreading nested call arguments with array gaps: f is undefined");
- assert.areEqual(undefined, g, "Spreading nested call arguments with array gaps: g is undefined");
- assert.areEqual(8, h, "Spreading nested call arguments with array gaps: h is 8");
- }
- spreadValid2(...[4, , ...[, 8]]);
- }
- },
- {
- name: "Testing spread with string literals",
- body: function () {
- assert.areEqual(["s", "t", "r", "i", "n", "g"], [..."string"], "Spreading a string creates a character array of the string");
- }
- },
- {
- name: "BLUE 491118: Spread outside of a call or array literal",
- body: function () {
- assert.throws(function () { eval("var a = []; ...a") }, SyntaxError, "Spread outside of a call or array literal");
- }
- },
- {
- name: "BLUE 511620: Calling spread operation inside string template results in crash",
- body: function () {
- function foo(a,b){}
- var demo = "hello";
- assert.throws(function() { eval("var strAdd = foo`${...demo}`;"); }, SyntaxError, "Spread inside string template");
- }
- },
- {
- name: "BLUE 490915: ... is a valid operator but ...... (2 spreads combined) is not",
- body: function () {
- assert.throws(function () { eval("var a = []; [......a];") }, SyntaxError, "Two spreads combined is a syntax error");
- assert.throws(function () { eval("function foo(){};var a = []; foo(......a);") }, SyntaxError, "Two spreads combined is a syntax error");
- }
- },
- {
- name: "BLUE 522366: Spreading null or undefined should throw TypeError instead of RuntimeError",
- body: function () {
- function foo(bar) { return bar; }
- assert.throws(function () { foo(...null); }, TypeError);
- assert.throws(function () { [...null]; }, TypeError);
- assert.throws(function () { foo(...undefined); }, TypeError);
- assert.throws(function () { [...undefined]; }, TypeError);
- }
- },
- {
- name: "BLUE 522630: Assignment expresion should be evaluated fisrt and then the results should be spread",
- body: function () {
- let b = { get x() { return "abc"; } }
- a = [1, ...b.x + ""];
- assert.areEqual([1,"a","b","c"], a);
- }
- },
- {
- name: "Spread functionality in non-standard call types",
- body: function () {
- var shortArray = [1,2,3]; // Doesn't use _alloca()
- var longArray = [5,3,8,2,4,8,5,3,1,912341234,33543,12987,3476,-2134124,3245235]; // Uses _alloca()
- // Ctor tests
- assert.areEqual(shortArray, new Array(...shortArray));
- assert.areEqual(longArray, new Array(...longArray));
- function Storage(a,b,c,d,e,f,g,h,i) {
- this.a = a;
- this.b = b;
- this.c = c;
- this.d = d;
- this.e = e;
- this.f = f;
- this.g = g;
- this.h = h;
- this.i = i;
- }
- assert.areEqual(new Storage(1,2,3), new Storage(...shortArray));
- assert.areEqual(new Storage(5,3,8,2,4,8,5,3,1), new Storage(...longArray));
- // Eval
- assert.areEqual("hello", eval(...['"hello"']));
- assert.areEqual(longArray[0], eval(...longArray));
- }
- },
- {
- name: "BLUE: 521584:[ES6][Spread] - Spread inside eval is considered valid without quotes. Currently crashes",
- body: function () {
- assert.areEqual(undefined, eval(...[]));
- assert.areEqual(undefined, eval(...[], ...[]));
- assert.areEqual(undefined, eval(...[], ...[], ...[]));
- assert.areEqual(undefined, eval(...[], ...[], ...[], ...[]));
- assert.areEqual(123123, eval(...[], ...["123123"]));
- assert.areEqual(123123, eval(...[], ...["123123"], ...[]));
- assert.areEqual(123123, eval(...[], ...[], ...["123123"], ...[]));
- assert.areEqual(123123, eval(...[], ...[], ...[], ...[], ...["123123"]));
- assert.areEqual(123123, eval(...["123123"], ...[], ...[], ...[], ...[]));
- assert.areEqual(undefined, eval.call(...[]));
- assert.areEqual(undefined, eval.call(...[], ...["123123"]));
- }
- },
- {
- name: "Spread with side effects",
- body: function () {
- var i = 0;
- eval(...["i++"]);
- assert.areEqual(1, i);
- eval(...[], ...["i += 1"]);
- assert.areEqual(2, i);
- eval(...[], ...["i = i - i"], ...[]);
- assert.areEqual(0, i);
- }
- },
- {
- name: "BLUE 584814: Instr::HasFixedFunctionAddressTarget() assertion",
- body: function () {
- function badFunc() {
- function shapeyConstructor(iijcze) {
- if (((NaN += /x/).b)) iijcze.d = (new((4277))(new[1, , ](/x/, ''), ...y));
- }
- for (var z in [/x/, /x/, true, ]) {
- let drjotv = shapeyConstructor(z);
- }
- }
- badFunc(); badFunc();
- }
- },
- {
- name: "BLUE 582720: Spread calls with invalid functions crash in native",
- body: function () {
- function badFunc() {
- (12345)(...[]);
- }
- assert.throws(function () { badFunc(); badFunc(); });
- }
- },
- {
- name: "BLUE 589583: CallIPut with spread causes an assert",
- body: function () {
- function a() {};
- var x = [];
- assert.throws(function() { eval('a(...x)--'); }, ReferenceError, "Spread with CallIPut throws a ReferenceError");
- }
- },
- /*
- A fix for an unsafe optimization makes this portion of the test time out.
- {
- name: "BLUE 596934, 597412: Incorrect spread argument length handling",
- body: function () {
- function a() {}
- // The implemented version of the spec allows overflow of the length when converting to UInt32.
- assert.throws(function () { a(...new Array (0x50505050)); }, RangeError, "Very large array throws RangeError");
- try {
- a(...new Array(1 << 24 - 1)); // No RangeError throw (max call args)
- } catch (e) {
- // This many args will blow out the stack. But it shouldn't be a length limitation.
- assert.areNotEqual(e.constructor, RangeError, "Max call args should not throw a RangeError");
- }
- assert.throws(function () { a(...new Array(1 << 24)); }, RangeError, "Array size greater than max call arg count throws RangeError");
- assert.throws(function () { a(...new Array(3), ...new Array(1 << 32 - 2)); }, RangeError, "Total spread size greater than max call arg count throws RangeError");
- }
- },
- */
- {
- name: "MSRC 34309: Guard against getter in prototype",
- body: function () {
- var x = [0x40];
- x.length = 0x9;
-
- Object.defineProperty(Array.prototype, 1, {
- get: function() {
- x.length = 0;
- }
- });
- var f = function(){
- assert.areEqual(arguments.length, 2, "Changing length of x during spreading should truncate the spread.");
- }
- f(...x);
- }
- },
- {
- name: "BLUE 611774: Spread with a prefix operator is allowed anywhere",
- body: function () {
- assert.throws(function () { eval('++...window, z;'); }, SyntaxError, "Invalid use of the ... operator");
- }
- },
- {
- name: "OS 5204357: Corner case: user changes %ArrayIteratorPrototype%.next property and we should call it",
- body: function () {
- var overrideCalled = false;
- var arrayIteratorProto = Object.getPrototypeOf([][Symbol.iterator]());
- var arrayIteratorProtoNext = arrayIteratorProto.next;
- function testArrayIteratorProto() {
- var a = [1];
- function f() {}
- f(...a);
- assert.isTrue(overrideCalled, "Spread of a in call to f should have invoked the overridden %ArrayIteratorPrototype%.next method");
- overrideCalled = false;
- var b = [...a];
- assert.isTrue(overrideCalled, "Spread of a in array initializer should have invoked the overridden %ArrayIteratorPrototype%.next method");
- overrideCalled = false;
- }
-
- var overrideFunc = function () {
- overrideCalled = true;
- return arrayIteratorProtoNext.apply(this, arguments);
- };
-
- arrayIteratorProto.next = overrideFunc;
- testArrayIteratorProto();
- // Restore the original prototype .next
- arrayIteratorProto.next = arrayIteratorProtoNext;
- function getIterableObjNextDesc() {
- return {
- get: function next() {
- overrideCalled = true;
- return function () {
- return {
- value: 0,
- done: 1
- };
- };
- }
- };
- }
- // Change built-in array iterator's next getter function
- var builtinArrayPrototypeIteratorNextDesc = Object.getOwnPropertyDescriptor(arrayIteratorProto, "next");
- Object.defineProperty(arrayIteratorProto, "next", getIterableObjNextDesc());
- testArrayIteratorProto();
- Object.defineProperty(arrayIteratorProto, "next", builtinArrayPrototypeIteratorNextDesc);
- }
- },
- {
- name: "Corner case: Spread of an array with an accessor property (ES5Array) should call that getter and recognize a change in length during iteration",
- body: function () {
- var result = [];
- function foo(a, b, c, d) {
- result.push(a);
- result.push(b);
- result.push(c);
- result.push(d);
- }
- var a = [5, 6, 7];
- a.two = 7;
- Object.defineProperty(a, '2', { get: function () { this[3] = 8; return this.two; } });
- foo(...a);
- assert.areEqual(4, result.length, "Spread for the function argument called the getter and caused the length to be updated to four");
- assert.areEqual(5, result[0], "Spread for the function argument passed the first element of a as the first argument");
- assert.areEqual(6, result[1], "Spread for the function argument passed the second element of a as the second argument");
- assert.areEqual(7, result[2], "Spread for the function argument called the getter of the third element of a and passed the result as the third argument");
- assert.areEqual(8, result[3], "Spread for the function argument recognized the new length of four and passed the fourth element of a as the fourth argument");
- a = [5, 6, 7];
- a.two = 7;
- Object.defineProperty(a, '2', { get: function () { this[3] = 8; return this.two; } });
- var b = [...a];
- result = b;
- assert.areEqual(4, result.length, "Spread for the array initializer called the getter and caused the length to be updated to four");
- assert.areEqual(5, result[0], "Spread for the array initializer copied the first element of a to the first element of b");
- assert.areEqual(6, result[1], "Spread for the array initializer copied the second element of a to the second element of b");
- assert.areEqual(7, result[2], "Spread for the array initializer called the getter of the third element of a and copied the result to the third element of b");
- assert.areEqual(8, result[3], "Spread for the array initializer recognized the new length of four and copied the fourth element of a to the fourth element of b");
- }
- },
- {
- name: "Corner case: Spread of an arguments object with an accessor property (ES5HeapArgumentsObject) should call that getter and recognize a change in length during iteration",
- body: function () {
- var result = [];
- function foo(a, b, c, d) {
- result.push(a);
- result.push(b);
- result.push(c);
- result.push(d);
- }
- function bar() {
- arguments.two = 7;
- // Note arguments object does not change its length like an array when new elements are added,
- // but its length is writable and we can change it ourselves
- Object.defineProperty(arguments, '2', { get: function () { this[3] = 8; this.length++; return this.two; } });
- foo(...arguments);
- }
- bar(5, 6, 7);
- assert.areEqual(4, result.length, "Spread for the function argument called the getter and caused the length to be updated to four");
- assert.areEqual(5, result[0], "Spread for the function argument passed the first element of a as the first argument");
- assert.areEqual(6, result[1], "Spread for the function argument passed the second element of a as the second argument");
- assert.areEqual(7, result[2], "Spread for the function argument called the getter of the third element of a and passed the result as the third argument");
- assert.areEqual(8, result[3], "Spread for the function argument recognized the new length of four and passed the fourth element of a as the fourth argument");
- result = [];
- function fuz() {
- arguments.two = 7;
- // Note arguments object does not change its length like an array when new elements are added,
- // but its length is writable and we can change it ourselves
- Object.defineProperty(arguments, '2', { get: function () { this[3] = 8; this.length++; return this.two; } });
- var b = [...arguments];
- result = b;
- }
- fuz(5, 6, 7);
- assert.areEqual(4, result.length, "Spread for the array initializer called the getter and caused the length to be updated to four");
- assert.areEqual(5, result[0], "Spread for the array initializer copied the first element of a to the first element of b");
- assert.areEqual(6, result[1], "Spread for the array initializer copied the second element of a to the second element of b");
- assert.areEqual(7, result[2], "Spread for the array initializer called the getter of the third element of a and copied the result to the third element of b");
- assert.areEqual(8, result[3], "Spread for the array initializer recognized the new length of four and copied the fourth element of a to the fourth element of b");
- }
- },
- {
- name: "OS 2970825: Spread should not be allowed with unary operators",
- body: function () {
- function foo() {}
- assert.throws(function () { eval("foo(++...[1,2,3]);"); }, SyntaxError, "Spread with unary operator throws a syntax error", "Unexpected ... operator");
- assert.throws(function () { eval("foo(typeof ...[1,2,3]);"); }, SyntaxError, "Spread with keyword unary operator throws a syntax error", "Unexpected ... operator");
- assert.throws(function () { eval("foo(!!...[1,2,3]);"); }, SyntaxError, "Spread with chained unary operators throws a syntax error", "Unexpected ... operator");
- }
- },
- {
- name: "call scenario - second spread is changing the first spread's length",
- body: function () {
- function foo() {
- var args = [...arguments];
- assert.areEqual([101, 102, 201], args, "2 values from the first spread and 1 value from the second spread is expected");
- }
-
- var first = [101, 102];
-
- var obj = {};
- Object.defineProperty(obj, '2', {get : function() {
- assert.fail('this should not have called')
- return 103;
- }});
- var second = [];
- second.length = 1;
- var getterCalled = false;
- Object.defineProperty(second, '0', {get : function() {
- // Changing the state of the first spread
- first.__proto__ = obj;
- first.length = 3;
- getterCalled = true;
- return 201;
- }});
- foo(...first, ...second);
- assert.isTrue(getterCalled, "getter of the second spread is executed");
- }
- },
- {
- name: "array scenario - second spread is changing the first spread's length",
- body: function () {
-
- var first = [101, 102];
-
- var obj = {};
- Object.defineProperty(obj, '2', {get : function() {
- assert.fail('this should not have called')
- return 103;
- }});
- var second = [];
- second.length = 1;
- var getterCalled = false;
- Object.defineProperty(second, '0', {get : function() {
- // Changing the state of the first spread
- first.__proto__ = obj;
- first.length = 3;
- getterCalled = true;
- return 201;
- }});
- var result = [...first, ...second];
- assert.areEqual([101, 102, 201], result, "2 values from the first spread and 1 value from the second spread is expected");
- assert.isTrue(getterCalled, "getter of the second spread is executed");
- }
- },
- {
- name: "typedarray scenario - second spread is changing value of first spread which is a typedarray",
- body: function () {
-
- var first = new Uint32Array([101, 102]);
-
- var second = [];
- second.length = 1;
- var getterCalled = false;
- Object.defineProperty(second, '0', {get : function() {
- // Changing the state of the first spread
- first[0] = 11; // This should not affect the resultant spread.
- getterCalled = true;
- return 201;
- }});
- var result = [...first, ...second];
- assert.areEqual([101, 102, 201], result, "2 values from the first spread and 1 value from the second spread is expected");
- assert.isTrue(getterCalled, "getter of the second spread is executed");
- }
- }
- ];
- testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });
|