Skip to content

Commit f9a05eb

Browse files
committed
Fix array spread with sideeffects
1 parent 7a5aadc commit f9a05eb

File tree

52 files changed

+4025
-5399
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+4025
-5399
lines changed

src/compiler/checker.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24695,7 +24695,7 @@ namespace ts {
2469524695

2469624696
function checkSpreadExpression(node: SpreadElement, checkMode?: CheckMode): Type {
2469724697
if (languageVersion < ScriptTarget.ES2015) {
24698-
checkExternalEmitHelpers(node, compilerOptions.downlevelIteration ? ExternalEmitHelpers.SpreadIncludes : ExternalEmitHelpers.SpreadArrays);
24698+
checkExternalEmitHelpers(node, compilerOptions.downlevelIteration ? ExternalEmitHelpers.SpreadIncludes : ExternalEmitHelpers.SpreadArray);
2469924699
}
2470024700

2470124701
const arrayOrIterableType = checkExpression(node.expression, checkMode);
@@ -24723,7 +24723,7 @@ namespace ts {
2472324723
const e = elements[i];
2472424724
if (e.kind === SyntaxKind.SpreadElement) {
2472524725
if (languageVersion < ScriptTarget.ES2015) {
24726-
checkExternalEmitHelpers(e, compilerOptions.downlevelIteration ? ExternalEmitHelpers.SpreadIncludes : ExternalEmitHelpers.SpreadArrays);
24726+
checkExternalEmitHelpers(e, compilerOptions.downlevelIteration ? ExternalEmitHelpers.SpreadIncludes : ExternalEmitHelpers.SpreadArray);
2472724727
}
2472824728
const spreadType = checkExpression((<SpreadElement>e).expression, checkMode, forceTuple);
2472924729
if (isArrayLikeType(spreadType)) {
@@ -39048,8 +39048,7 @@ namespace ts {
3904839048
case ExternalEmitHelpers.Generator: return "__generator";
3904939049
case ExternalEmitHelpers.Values: return "__values";
3905039050
case ExternalEmitHelpers.Read: return "__read";
39051-
case ExternalEmitHelpers.Spread: return "__spread";
39052-
case ExternalEmitHelpers.SpreadArrays: return "__spreadArrays";
39051+
case ExternalEmitHelpers.SpreadArray: return "__spreadArray";
3905339052
case ExternalEmitHelpers.Await: return "__await";
3905439053
case ExternalEmitHelpers.AsyncGenerator: return "__asyncGenerator";
3905539054
case ExternalEmitHelpers.AsyncDelegator: return "__asyncDelegator";

src/compiler/factory/emitHelpers.ts

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ namespace ts {
1919
// ES2015 Helpers
2020
createExtendsHelper(name: Identifier): Expression;
2121
createTemplateObjectHelper(cooked: ArrayLiteralExpression, raw: ArrayLiteralExpression): Expression;
22-
createSpreadHelper(argumentList: readonly Expression[]): Expression;
23-
createSpreadArraysHelper(argumentList: readonly Expression[]): Expression;
22+
createSpreadArrayHelper(to: Expression, from: Expression): Expression;
2423
// ES2015 Destructuring Helpers
2524
createValuesHelper(expression: Expression): Expression;
2625
createReadHelper(iteratorRecord: Expression, count: number | undefined): Expression;
@@ -58,8 +57,7 @@ namespace ts {
5857
// ES2015 Helpers
5958
createExtendsHelper,
6059
createTemplateObjectHelper,
61-
createSpreadHelper,
62-
createSpreadArraysHelper,
60+
createSpreadArrayHelper,
6361
// ES2015 Destructuring Helpers
6462
createValuesHelper,
6563
createReadHelper,
@@ -284,22 +282,12 @@ namespace ts {
284282
);
285283
}
286284

287-
function createSpreadHelper(argumentList: readonly Expression[]) {
288-
context.requestEmitHelper(readHelper);
289-
context.requestEmitHelper(spreadHelper);
290-
return factory.createCallExpression(
291-
getUnscopedHelperName("__spread"),
292-
/*typeArguments*/ undefined,
293-
argumentList
294-
);
295-
}
296-
297-
function createSpreadArraysHelper(argumentList: readonly Expression[]) {
298-
context.requestEmitHelper(spreadArraysHelper);
285+
function createSpreadArrayHelper(to: Expression, from: Expression) {
286+
context.requestEmitHelper(spreadArrayHelper);
299287
return factory.createCallExpression(
300-
getUnscopedHelperName("__spreadArrays"),
288+
getUnscopedHelperName("__spreadArray"),
301289
/*typeArguments*/ undefined,
302-
argumentList
290+
[to, from]
303291
);
304292
}
305293

@@ -629,6 +617,7 @@ namespace ts {
629617
};`
630618
};
631619

620+
/** @deprecated To be removed in TS >= 4.3, as it may be referenced in a tsbuildinfo */
632621
export const spreadHelper: UnscopedEmitHelper = {
633622
name: "typescript:spread",
634623
importName: "__spread",
@@ -641,6 +630,7 @@ namespace ts {
641630
};`
642631
};
643632

633+
/** @deprecated To be removed in TS >= 4.3, as it may be referenced in a tsbuildinfo */
644634
export const spreadArraysHelper: UnscopedEmitHelper = {
645635
name: "typescript:spreadArrays",
646636
importName: "__spreadArrays",
@@ -655,6 +645,18 @@ namespace ts {
655645
};`
656646
};
657647

648+
export const spreadArrayHelper: UnscopedEmitHelper = {
649+
name: "typescript:spreadArray",
650+
importName: "__spreadArray",
651+
scoped: false,
652+
text: `
653+
var __spreadArray = (this && this.__spreadArray) || function (to, from) {
654+
for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
655+
to[j] = from[i];
656+
return to;
657+
};`
658+
};
659+
658660
// ES2015 Destructuring Helpers
659661

660662
export const valuesHelper: UnscopedEmitHelper = {
@@ -888,6 +890,7 @@ namespace ts {
888890
templateObjectHelper,
889891
spreadHelper,
890892
spreadArraysHelper,
893+
spreadArrayHelper,
891894
valuesHelper,
892895
readHelper,
893896
generatorHelper,

src/compiler/transformers/es2015.ts

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3895,14 +3895,27 @@ namespace ts {
38953895
* @param multiLine A value indicating whether the result should be emitted on multiple lines.
38963896
*/
38973897
function transformAndSpreadElements(elements: NodeArray<Expression>, needsUniqueCopy: boolean, multiLine: boolean, hasTrailingComma: boolean): Expression {
3898+
// When there is no leading SpreadElement:
3899+
//
38983900
// [source]
38993901
// [a, ...b, c]
39003902
//
39013903
// [output (downlevelIteration)]
3902-
// __spread([a], b, [c])
3904+
// __spreadArray(__spreadArray([a], __read(b)), [c])
3905+
//
3906+
// [output]
3907+
// __spreadArray(__spreadArray([a], b), [c])
3908+
//
3909+
// When there *is* a leading SpreadElement:
3910+
//
3911+
// [source]
3912+
// [...a, b]
3913+
//
3914+
// [output (downlevelIteration)]
3915+
// __spreadArray(__spreadArray([], __read(a)), [b])
39033916
//
39043917
// [output]
3905-
// __spreadArrays([a], b, [c])
3918+
// __spreadArray(__spreadArray([], a), [b])
39063919

39073920
// Map spans of spread expressions into their expressions and spans of other
39083921
// expressions into an array literal.
@@ -3913,28 +3926,29 @@ namespace ts {
39133926
)
39143927
);
39153928

3916-
if (compilerOptions.downlevelIteration) {
3917-
if (segments.length === 1) {
3918-
const firstSegment = segments[0];
3919-
if (isCallToHelper(firstSegment, "___spread" as __String)) {
3920-
return segments[0];
3921-
}
3929+
const helpers = emitHelpers();
3930+
if (segments.length === 1) {
3931+
const firstSegment = segments[0];
3932+
if (!needsUniqueCopy
3933+
|| isPackedArrayLiteral(firstSegment)
3934+
|| isCallToHelper(firstSegment, "___spreadArray" as __String)) {
3935+
return segments[0];
39223936
}
3923-
3924-
return emitHelpers().createSpreadHelper(segments);
39253937
}
3926-
else {
3927-
if (segments.length === 1) {
3928-
const firstSegment = segments[0];
3929-
if (!needsUniqueCopy
3930-
|| isPackedArrayLiteral(firstSegment)
3931-
|| isCallToHelper(firstSegment, "___spreadArrays" as __String)) {
3932-
return segments[0];
3933-
}
3934-
}
39353938

3936-
return emitHelpers().createSpreadArraysHelper(segments);
3939+
const startsWithSpread = isSpreadElement(elements[0]);
3940+
let expression: Expression =
3941+
startsWithSpread ? factory.createArrayLiteralExpression() :
3942+
segments[0];
3943+
for (let i = startsWithSpread ? 0 : 1; i < segments.length; i++) {
3944+
expression = helpers.createSpreadArrayHelper(
3945+
expression,
3946+
compilerOptions.downlevelIteration && !isArrayLiteralExpression(segments[i]) ?
3947+
helpers.createReadHelper(segments[i], /*count*/ undefined) :
3948+
segments[i]);
39373949
}
3950+
3951+
return expression;
39383952
}
39393953

39403954
function isPackedElement(node: Expression) {

src/compiler/types.ts

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6605,19 +6605,18 @@ namespace ts {
66056605
Generator = 1 << 7, // __generator (used by ES2015 generator transformation)
66066606
Values = 1 << 8, // __values (used by ES2015 for..of and yield* transformations)
66076607
Read = 1 << 9, // __read (used by ES2015 iterator destructuring transformation)
6608-
Spread = 1 << 10, // __spread (used by ES2015 array spread and argument list spread transformations)
6609-
SpreadArrays = 1 << 11, // __spreadArrays (used by ES2015 array spread and argument list spread transformations)
6610-
Await = 1 << 12, // __await (used by ES2017 async generator transformation)
6611-
AsyncGenerator = 1 << 13, // __asyncGenerator (used by ES2017 async generator transformation)
6612-
AsyncDelegator = 1 << 14, // __asyncDelegator (used by ES2017 async generator yield* transformation)
6613-
AsyncValues = 1 << 15, // __asyncValues (used by ES2017 for..await..of transformation)
6614-
ExportStar = 1 << 16, // __exportStar (used by CommonJS/AMD/UMD module transformation)
6615-
ImportStar = 1 << 17, // __importStar (used by CommonJS/AMD/UMD module transformation)
6616-
ImportDefault = 1 << 18, // __importStar (used by CommonJS/AMD/UMD module transformation)
6617-
MakeTemplateObject = 1 << 19, // __makeTemplateObject (used for constructing template string array objects)
6618-
ClassPrivateFieldGet = 1 << 20, // __classPrivateFieldGet (used by the class private field transformation)
6619-
ClassPrivateFieldSet = 1 << 21, // __classPrivateFieldSet (used by the class private field transformation)
6620-
CreateBinding = 1 << 22, // __createBinding (use by the module transform for (re)exports and namespace imports)
6608+
SpreadArray = 1 << 10, // __spreadArray (used by ES2015 array spread and argument list spread transformations)
6609+
Await = 1 << 11, // __await (used by ES2017 async generator transformation)
6610+
AsyncGenerator = 1 << 12, // __asyncGenerator (used by ES2017 async generator transformation)
6611+
AsyncDelegator = 1 << 13, // __asyncDelegator (used by ES2017 async generator yield* transformation)
6612+
AsyncValues = 1 << 14, // __asyncValues (used by ES2017 for..await..of transformation)
6613+
ExportStar = 1 << 15, // __exportStar (used by CommonJS/AMD/UMD module transformation)
6614+
ImportStar = 1 << 16, // __importStar (used by CommonJS/AMD/UMD module transformation)
6615+
ImportDefault = 1 << 17, // __importStar (used by CommonJS/AMD/UMD module transformation)
6616+
MakeTemplateObject = 1 << 18, // __makeTemplateObject (used for constructing template string array objects)
6617+
ClassPrivateFieldGet = 1 << 19, // __classPrivateFieldGet (used by the class private field transformation)
6618+
ClassPrivateFieldSet = 1 << 20, // __classPrivateFieldSet (used by the class private field transformation)
6619+
CreateBinding = 1 << 21, // __createBinding (use by the module transform for (re)exports and namespace imports)
66216620
FirstEmitHelper = Extends,
66226621
LastEmitHelper = CreateBinding,
66236622

@@ -6634,8 +6633,7 @@ namespace ts {
66346633
AsyncDelegatorIncludes = Await | AsyncDelegator | AsyncValues,
66356634

66366635
// Helpers included by ES2015 spread
6637-
SpreadIncludes = Read | Spread,
6638-
6636+
SpreadIncludes = Read | SpreadArray,
66396637
}
66406638

66416639
export const enum EmitHint {

tests/baselines/reference/argumentExpressionContextualTyping.js

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,10 @@ baz(["string", 1, true, ...array]); // Error
1919
foo(o); // Error because x has an array type namely (string|number)[]
2020

2121
//// [argumentExpressionContextualTyping.js]
22-
var __spreadArrays = (this && this.__spreadArrays) || function () {
23-
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
24-
for (var r = Array(s), k = 0, i = 0; i < il; i++)
25-
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
26-
r[k] = a[j];
27-
return r;
22+
var __spreadArray = (this && this.__spreadArray) || function (to, from) {
23+
for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
24+
to[j] = from[i];
25+
return to;
2826
};
2927
// In a typed function call, argument expressions are contextually typed by their corresponding parameter types.
3028
function foo(_a) {
@@ -43,5 +41,5 @@ var tuple = ["string", 1, true];
4341
baz(tuple);
4442
baz(["string", 1, true]);
4543
baz(array); // Error
46-
baz(__spreadArrays(["string", 1, true], array)); // Error
44+
baz(__spreadArray(["string", 1, true], array)); // Error
4745
foo(o); // Error because x has an array type namely (string|number)[]

tests/baselines/reference/arrayLiteralExpressionContextualTyping.js

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,10 @@ var spr2:[number, number, number] = [1, 2, 3, ...tup]; // Error
1616

1717

1818
//// [arrayLiteralExpressionContextualTyping.js]
19-
var __spreadArrays = (this && this.__spreadArrays) || function () {
20-
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
21-
for (var r = Array(s), k = 0, i = 0; i < il; i++)
22-
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
23-
r[k] = a[j];
24-
return r;
19+
var __spreadArray = (this && this.__spreadArray) || function (to, from) {
20+
for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
21+
to[j] = from[i];
22+
return to;
2523
};
2624
// In a contextually typed array literal expression containing no spread elements, an element expression at index N is contextually typed by
2725
// the type of the property with the numeric name N in the contextual type, if any, or otherwise
@@ -33,6 +31,6 @@ var tup1 = [1, 2, 3, "string"];
3331
var tup2 = [1, 2, 3, "string"]; // Error
3432
// In a contextually typed array literal expression containing one or more spread elements,
3533
// an element expression at index N is contextually typed by the numeric index type of the contextual type, if any.
36-
var spr = __spreadArrays([1, 2, 3], array);
37-
var spr1 = __spreadArrays([1, 2, 3], tup);
38-
var spr2 = __spreadArrays([1, 2, 3], tup); // Error
34+
var spr = __spreadArray([1, 2, 3], array);
35+
var spr1 = __spreadArray([1, 2, 3], tup);
36+
var spr2 = __spreadArray([1, 2, 3], tup); // Error

tests/baselines/reference/arrayLiteralSpread.js

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,27 +24,25 @@ function f2() {
2424

2525

2626
//// [arrayLiteralSpread.js]
27-
var __spreadArrays = (this && this.__spreadArrays) || function () {
28-
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
29-
for (var r = Array(s), k = 0, i = 0; i < il; i++)
30-
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
31-
r[k] = a[j];
32-
return r;
27+
var __spreadArray = (this && this.__spreadArray) || function (to, from) {
28+
for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
29+
to[j] = from[i];
30+
return to;
3331
};
3432
function f0() {
3533
var a = [1, 2, 3];
36-
var a1 = __spreadArrays(a);
37-
var a2 = __spreadArrays([1], a);
38-
var a3 = __spreadArrays([1, 2], a);
39-
var a4 = __spreadArrays(a, [1]);
40-
var a5 = __spreadArrays(a, [1, 2]);
41-
var a6 = __spreadArrays([1, 2], a, [1, 2]);
42-
var a7 = __spreadArrays([1], a, [2], a);
43-
var a8 = __spreadArrays(a, a, a);
34+
var a1 = __spreadArray([], a);
35+
var a2 = __spreadArray([1], a);
36+
var a3 = __spreadArray([1, 2], a);
37+
var a4 = __spreadArray(__spreadArray([], a), [1]);
38+
var a5 = __spreadArray(__spreadArray([], a), [1, 2]);
39+
var a6 = __spreadArray(__spreadArray([1, 2], a), [1, 2]);
40+
var a7 = __spreadArray(__spreadArray(__spreadArray([1], a), [2]), a);
41+
var a8 = __spreadArray(__spreadArray(__spreadArray([], a), a), a);
4442
}
4543
function f1() {
4644
var a = [1, 2, 3];
47-
var b = __spreadArrays(["hello"], a, [true]);
45+
var b = __spreadArray(__spreadArray(["hello"], a), [true]);
4846
var b;
4947
}
5048
function f2() {

tests/baselines/reference/arrayLiteralSpreadES5iterable.js

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,27 +40,28 @@ var __read = (this && this.__read) || function (o, n) {
4040
}
4141
return ar;
4242
};
43-
var __spread = (this && this.__spread) || function () {
44-
for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i]));
45-
return ar;
43+
var __spreadArray = (this && this.__spreadArray) || function (to, from) {
44+
for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
45+
to[j] = from[i];
46+
return to;
4647
};
4748
function f0() {
4849
var a = [1, 2, 3];
49-
var a1 = __spread(a);
50-
var a2 = __spread([1], a);
51-
var a3 = __spread([1, 2], a);
52-
var a4 = __spread(a, [1]);
53-
var a5 = __spread(a, [1, 2]);
54-
var a6 = __spread([1, 2], a, [1, 2]);
55-
var a7 = __spread([1], a, [2], a);
56-
var a8 = __spread(a, a, a);
50+
var a1 = __spreadArray([], __read(a));
51+
var a2 = __spreadArray([1], __read(a));
52+
var a3 = __spreadArray([1, 2], __read(a));
53+
var a4 = __spreadArray(__spreadArray([], __read(a)), [1]);
54+
var a5 = __spreadArray(__spreadArray([], __read(a)), [1, 2]);
55+
var a6 = __spreadArray(__spreadArray([1, 2], __read(a)), [1, 2]);
56+
var a7 = __spreadArray(__spreadArray(__spreadArray([1], __read(a)), [2]), __read(a));
57+
var a8 = __spreadArray(__spreadArray(__spreadArray([], __read(a)), __read(a)), __read(a));
5758
}
5859
function f1() {
5960
var a = [1, 2, 3];
60-
var b = __spread(["hello"], a, [true]);
61+
var b = __spreadArray(__spreadArray(["hello"], __read(a)), [true]);
6162
var b;
6263
}
6364
function f2() {
64-
var a = __spread([]);
65-
var b = __spread([5]);
65+
var a = [];
66+
var b = [5];
6667
}

0 commit comments

Comments
 (0)