Skip to content

Commit 8afbf8b

Browse files
committed
Optimize rest parameter emit microsoft#19182
1 parent e934c30 commit 8afbf8b

File tree

200 files changed

+3717
-2109
lines changed

Some content is hidden

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

200 files changed

+3717
-2109
lines changed

src/compiler/transformers/es2015.ts

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -944,7 +944,6 @@ namespace ts {
944944

945945
if (constructor) {
946946
addDefaultValueAssignmentsIfNeeded(statements, constructor);
947-
addRestParameterIfNeeded(statements, constructor, hasSynthesizedSuper);
948947
if (!hasSynthesizedSuper) {
949948
// If no super call has been synthesized, emit custom prologue directives.
950949
statementOffset = addCustomPrologue(statements, constructor.body.statements, statementOffset, visitor);
@@ -969,6 +968,7 @@ namespace ts {
969968
}
970969

971970
addRange(statements, visitNodes(constructor.body.statements, visitor, isStatement, /*start*/ statementOffset));
971+
insertRestParameterIfNeeded(statements, constructor, hasSynthesizedSuper);
972972
}
973973

974974
// Return `_this` unless we're sure enough that it would be pointless to add a return statement.
@@ -1344,6 +1344,38 @@ namespace ts {
13441344
return node && node.dotDotDotToken && node.name.kind === SyntaxKind.Identifier && !inConstructorWithSynthesizedSuper;
13451345
}
13461346

1347+
/**
1348+
* Gets the index of the first statement that contains a reference to a given parameter
1349+
*
1350+
* @param restParameter The ParameterDeclaration to which we are finding references.
1351+
* @param statements An array of Statement nodes to search
1352+
*/
1353+
function findRestParameterReferenceIndex(restParameter: ParameterDeclaration, statements: Statement[]) {
1354+
const originalRestParameter = getOriginalNode(restParameter);
1355+
1356+
return findIndex(statements, containsReferenceToParameter);
1357+
1358+
function containsReferenceToParameter(statement: Statement): boolean {
1359+
return (isIdentifier(statement) && originalRestParameter === resolver.getReferencedValueDeclaration(statement)) ||
1360+
forEachChild(statement, containsReferenceToParameter);
1361+
}
1362+
}
1363+
1364+
/**
1365+
* Gets the index of the first statement that contains a re-definition of "arguments"
1366+
*
1367+
* @param statements An array of Statement nodes to search
1368+
*/
1369+
function findArgumentsRedefinitionIndex(statements: Statement[]) {
1370+
return findIndex(statements, containsArgumentsRedeclaration);
1371+
1372+
function containsArgumentsRedeclaration(statement: Statement): boolean {
1373+
return (isAssignmentExpression(statement) && isIdentifier(statement.left) && statement.left.escapedText === "arguments") ||
1374+
(isVariableDeclaration(statement) && statement.initializer && (<Identifier>statement.name).escapedText === "arguments") ||
1375+
forEachChild(statement, containsArgumentsRedeclaration);
1376+
}
1377+
}
1378+
13471379
/**
13481380
* Adds statements to the body of a function-like node if it contains a rest parameter.
13491381
*
@@ -1353,12 +1385,23 @@ namespace ts {
13531385
* part of a constructor declaration with a
13541386
* synthesized call to `super`
13551387
*/
1356-
function addRestParameterIfNeeded(statements: Statement[], node: FunctionLikeDeclaration, inConstructorWithSynthesizedSuper: boolean): void {
1388+
function insertRestParameterIfNeeded(statements: Statement[], node: FunctionLikeDeclaration, inConstructorWithSynthesizedSuper: boolean): void {
13571389
const parameter = lastOrUndefined(node.parameters);
13581390
if (!shouldAddRestParameter(parameter, inConstructorWithSynthesizedSuper)) {
13591391
return;
13601392
}
13611393

1394+
let referenceIndex = findRestParameterReferenceIndex(parameter, statements);
1395+
if (referenceIndex === -1) {
1396+
return;
1397+
}
1398+
1399+
const argumentsRedefinitionIndex = findArgumentsRedefinitionIndex(statements);
1400+
if (argumentsRedefinitionIndex > -1) {
1401+
// If the "arguments" variable is redefined, need to make sure to emit the rest parameter initialization before then.
1402+
referenceIndex = Math.min(referenceIndex, argumentsRedefinitionIndex);
1403+
}
1404+
13621405
// `declarationName` is the name of the local declaration for the parameter.
13631406
const declarationName = getMutableClone(<Identifier>parameter.name);
13641407
setEmitFlags(declarationName, EmitFlags.NoSourceMap);
@@ -1369,9 +1412,7 @@ namespace ts {
13691412
const temp = createLoopVariable();
13701413

13711414
// var param = [];
1372-
statements.push(
1373-
setEmitFlags(
1374-
setTextRange(
1415+
const variableStatement = setTextRange(
13751416
createVariableStatement(
13761417
/*modifiers*/ undefined,
13771418
createVariableDeclarationList([
@@ -1383,10 +1424,7 @@ namespace ts {
13831424
])
13841425
),
13851426
/*location*/ parameter
1386-
),
1387-
EmitFlags.CustomPrologue
1388-
)
1389-
);
1427+
);
13901428

13911429
// for (var _i = restIndex; _i < arguments.length; _i++) {
13921430
// param[_i - restIndex] = arguments[_i];
@@ -1426,9 +1464,8 @@ namespace ts {
14261464
])
14271465
);
14281466

1429-
setEmitFlags(forStatement, EmitFlags.CustomPrologue);
14301467
startOnNewLine(forStatement);
1431-
statements.push(forStatement);
1468+
statements.splice(referenceIndex, 0, variableStatement, forStatement);
14321469
}
14331470

14341471
/**
@@ -1846,7 +1883,6 @@ namespace ts {
18461883

18471884
addCaptureThisForNodeIfNeeded(statements, node);
18481885
addDefaultValueAssignmentsIfNeeded(statements, node);
1849-
addRestParameterIfNeeded(statements, node, /*inConstructorWithSynthesizedSuper*/ false);
18501886

18511887
// If we added any generated statements, this must be a multi-line block.
18521888
if (!multiLine && statements.length > 0) {
@@ -1895,13 +1931,16 @@ namespace ts {
18951931
closeBraceLocation = body;
18961932
}
18971933

1934+
const statementBodyLength = statements.length;
1935+
insertRestParameterIfNeeded(statements, node, /*inConstructorWithSynthesizedSuper*/ false);
1936+
18981937
const lexicalEnvironment = context.endLexicalEnvironment();
18991938
addRange(statements, lexicalEnvironment);
19001939

19011940
prependCaptureNewTargetIfNeeded(statements, node, /*copyOnWrite*/ false);
19021941

19031942
// If we added any final generated statements, this must be a multi-line block
1904-
if (!multiLine && lexicalEnvironment && lexicalEnvironment.length) {
1943+
if (!multiLine && statements.length !== statementBodyLength) {
19051944
multiLine = true;
19061945
}
19071946

tests/baselines/reference/accessorWithRestParam.js

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,12 @@ var C = /** @class */ (function () {
99
function C() {
1010
}
1111
Object.defineProperty(C.prototype, "X", {
12-
set: function () {
13-
var v = [];
14-
for (var _i = 0; _i < arguments.length; _i++) {
15-
v[_i] = arguments[_i];
16-
}
17-
},
12+
set: function () { },
1813
enumerable: true,
1914
configurable: true
2015
});
2116
Object.defineProperty(C, "X", {
22-
set: function () {
23-
var v2 = [];
24-
for (var _i = 0; _i < arguments.length; _i++) {
25-
v2[_i] = arguments[_i];
26-
}
27-
},
17+
set: function () { },
2818
enumerable: true,
2919
configurable: true
3020
});

tests/baselines/reference/arrayLiteralInNonVarArgParameter.js

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,5 @@ panic([], 'one', 'two');
55

66

77
//// [arrayLiteralInNonVarArgParameter.js]
8-
function panic(val) {
9-
var opt = [];
10-
for (var _i = 1; _i < arguments.length; _i++) {
11-
opt[_i - 1] = arguments[_i];
12-
}
13-
}
8+
function panic(val) { }
149
panic([], 'one', 'two');

tests/baselines/reference/assignmentCompatWithCallSignaturesWithRestParameters.js

Lines changed: 7 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -49,49 +49,19 @@ var a4: (x?: number, y?: string, ...z: number[]) => number;
4949
// call signatures in derived types must have the same or fewer optional parameters as the target for assignment
5050
var a; // ok, same number of required params
5151
a = function () { return 1; }; // ok, same number of required params
52-
a = function () {
53-
var args = [];
54-
for (var _i = 0; _i < arguments.length; _i++) {
55-
args[_i] = arguments[_i];
56-
}
57-
return 1;
58-
}; // ok, same number of required params
59-
a = function () {
60-
var args = [];
61-
for (var _i = 0; _i < arguments.length; _i++) {
62-
args[_i] = arguments[_i];
63-
}
64-
return 1;
65-
}; // error, type mismatch
52+
a = function () { return 1; }; // ok, same number of required params
53+
a = function () { return 1; }; // error, type mismatch
6654
a = function (x) { return 1; }; // ok, same number of required params
6755
a = function (x, y, z) { return 1; }; // ok, same number of required params
6856
a = function (x) { return 1; }; // ok, rest param corresponds to infinite number of params
6957
a = function (x) { return 1; }; // error, incompatible type
7058
var a2;
7159
a2 = function () { return 1; }; // ok, fewer required params
72-
a2 = function () {
73-
var args = [];
74-
for (var _i = 0; _i < arguments.length; _i++) {
75-
args[_i] = arguments[_i];
76-
}
77-
return 1;
78-
}; // ok, fewer required params
60+
a2 = function () { return 1; }; // ok, fewer required params
7961
a2 = function (x) { return 1; }; // ok, fewer required params
8062
a2 = function (x) { return 1; }; // ok, same number of required params
81-
a2 = function (x) {
82-
var args = [];
83-
for (var _i = 1; _i < arguments.length; _i++) {
84-
args[_i - 1] = arguments[_i];
85-
}
86-
return 1;
87-
}; // ok, same number of required params
88-
a2 = function (x) {
89-
var args = [];
90-
for (var _i = 1; _i < arguments.length; _i++) {
91-
args[_i - 1] = arguments[_i];
92-
}
93-
return 1;
94-
}; // should be type mismatch error
63+
a2 = function (x) { return 1; }; // ok, same number of required params
64+
a2 = function (x) { return 1; }; // should be type mismatch error
9565
a2 = function (x, y) { return 1; }; // ok, rest param corresponds to infinite number of params
9666
a2 = function (x, y) { return 1; }; // ok, same number of required params
9767
var a3;
@@ -100,24 +70,12 @@ a3 = function (x) { return 1; }; // ok, fewer required params
10070
a3 = function (x) { return 1; }; // ok, same number of required params
10171
a3 = function (x, y) { return 1; }; // ok, all present params match
10272
a3 = function (x, y, z) { return 1; }; // error
103-
a3 = function (x) {
104-
var z = [];
105-
for (var _i = 1; _i < arguments.length; _i++) {
106-
z[_i - 1] = arguments[_i];
107-
}
108-
return 1;
109-
}; // error
73+
a3 = function (x) { return 1; }; // error
11074
a3 = function (x, y, z) { return 1; }; // error
11175
var a4;
11276
a4 = function () { return 1; }; // ok, fewer required params
11377
a4 = function (x, y) { return 1; }; // error, type mismatch
11478
a4 = function (x) { return 1; }; // ok, all present params match
11579
a4 = function (x, y) { return 1; }; // error, second param has type mismatch
11680
a4 = function (x, y) { return 1; }; // ok, same number of required params with matching types
117-
a4 = function (x) {
118-
var args = [];
119-
for (var _i = 1; _i < arguments.length; _i++) {
120-
args[_i - 1] = arguments[_i];
121-
}
122-
return 1;
123-
}; // error, rest params have type mismatch
81+
a4 = function (x) { return 1; }; // error, rest params have type mismatch

tests/baselines/reference/baseTypeAfterDerivedType.js

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,6 @@ interface Base2 {
2020
var Derived2 = /** @class */ (function () {
2121
function Derived2() {
2222
}
23-
Derived2.prototype.method = function () {
24-
var args = [];
25-
for (var _i = 0; _i < arguments.length; _i++) {
26-
args[_i] = arguments[_i];
27-
}
28-
};
23+
Derived2.prototype.method = function () { };
2924
return Derived2;
3025
}());

tests/baselines/reference/callWithSpread.js

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,6 @@ var __extends = (this && this.__extends) || (function () {
7070
};
7171
})();
7272
function foo(x, y) {
73-
var z = [];
74-
for (var _i = 2; _i < arguments.length; _i++) {
75-
z[_i - 2] = arguments[_i];
76-
}
7773
}
7874
var a;
7975
var z;
@@ -100,18 +96,14 @@ xa[1].foo(1, 2, "abc");
10096
(_g = xa[1]).foo.apply(_g, [1, 2, "abc"]);
10197
var C = /** @class */ (function () {
10298
function C(x, y) {
99+
this.foo(x, y);
103100
var z = [];
104101
for (var _i = 2; _i < arguments.length; _i++) {
105102
z[_i - 2] = arguments[_i];
106103
}
107-
this.foo(x, y);
108104
this.foo.apply(this, [x, y].concat(z));
109105
}
110106
C.prototype.foo = function (x, y) {
111-
var z = [];
112-
for (var _i = 2; _i < arguments.length; _i++) {
113-
z[_i - 2] = arguments[_i];
114-
}
115107
};
116108
return C;
117109
}());

tests/baselines/reference/capturedLetConstInLoop13.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,17 @@ var Main = /** @class */ (function () {
2929
}
3030
Main.prototype.register = function () {
3131
var _this = this;
32-
var names = [];
33-
for (var _i = 0; _i < arguments.length; _i++) {
34-
names[_i] = arguments[_i];
35-
}
3632
var _loop_1 = function (name) {
3733
this_1.bar((_a = {},
3834
_a[name + ".a"] = function () { _this.foo(name); },
3935
_a));
4036
var _a;
4137
};
4238
var this_1 = this;
39+
var names = [];
40+
for (var _i = 0; _i < arguments.length; _i++) {
41+
names[_i] = arguments[_i];
42+
}
4343
for (var _a = 0, names_1 = names; _a < names_1.length; _a++) {
4444
var name = names_1[_a];
4545
_loop_1(name);

tests/baselines/reference/checkSuperCallBeforeThisAccessing5.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,6 @@ var __extends = (this && this.__extends) || (function () {
2020
})();
2121
var Based = /** @class */ (function () {
2222
function Based() {
23-
var arg = [];
24-
for (var _i = 0; _i < arguments.length; _i++) {
25-
arg[_i] = arguments[_i];
26-
}
2723
}
2824
return Based;
2925
}());

tests/baselines/reference/checkSuperCallBeforeThisAccessing6.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,6 @@ var __extends = (this && this.__extends) || (function () {
2323
})();
2424
var Base = /** @class */ (function () {
2525
function Base() {
26-
var arg = [];
27-
for (var _i = 0; _i < arguments.length; _i++) {
28-
arg[_i] = arguments[_i];
29-
}
3026
}
3127
return Base;
3228
}());

tests/baselines/reference/checkSuperCallBeforeThisAccessing8.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,6 @@ var __extends = (this && this.__extends) || (function () {
2323
})();
2424
var Base = /** @class */ (function () {
2525
function Base() {
26-
var arg = [];
27-
for (var _i = 0; _i < arguments.length; _i++) {
28-
arg[_i] = arguments[_i];
29-
}
3026
}
3127
return Base;
3228
}());

0 commit comments

Comments
 (0)