Skip to content

Commit 8c64937

Browse files
authored
Merge pull request #18153 from Microsoft/optimizeArrays
Optimize array operations to reduce memory footprint
2 parents 9e30642 + 3f09011 commit 8c64937

File tree

2 files changed

+51
-69
lines changed

2 files changed

+51
-69
lines changed

src/compiler/binder.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,9 +203,11 @@ namespace ts {
203203
node.symbol = symbol;
204204

205205
if (!symbol.declarations) {
206-
symbol.declarations = [];
206+
symbol.declarations = [node];
207+
}
208+
else {
209+
symbol.declarations.push(node);
207210
}
208-
symbol.declarations.push(node);
209211

210212
if (symbolFlags & SymbolFlags.HasExports && !symbol.exports) {
211213
symbol.exports = createSymbolTable();

src/compiler/parser.ts

Lines changed: 47 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -940,10 +940,6 @@ namespace ts {
940940
return scanner.getStartPos();
941941
}
942942

943-
function getNodeEnd(): number {
944-
return scanner.getStartPos();
945-
}
946-
947943
// Use this function to access the current token instead of reading the currentToken
948944
// variable. Since function results aren't narrowed in control flow analysis, this ensures
949945
// that the type checker doesn't make wrong assumptions about the type of the current
@@ -1135,13 +1131,14 @@ namespace ts {
11351131
new TokenConstructor(kind, pos, pos);
11361132
}
11371133

1138-
function createNodeArray<T extends Node>(elements?: T[], pos?: number): MutableNodeArray<T> {
1139-
const array = <MutableNodeArray<T>>(elements || []);
1140-
if (!(pos >= 0)) {
1141-
pos = getNodePos();
1142-
}
1134+
function createNodeArray<T extends Node>(elements: T[], pos: number, end?: number): NodeArray<T> {
1135+
// Since the element list of a node array is typically created by starting with an empty array and
1136+
// repeatedly calling push(), the list may not have the optimal memory layout. We invoke slice() for
1137+
// small arrays (1 to 4 elements) to give the VM a chance to allocate an optimal representation.
1138+
const length = elements.length;
1139+
const array = <MutableNodeArray<T>>(length >= 1 && length <= 4 ? elements.slice() : elements);
11431140
array.pos = pos;
1144-
array.end = pos;
1141+
array.end = end === undefined ? scanner.getStartPos() : end;
11451142
return array;
11461143
}
11471144

@@ -1527,12 +1524,13 @@ namespace ts {
15271524
function parseList<T extends Node>(kind: ParsingContext, parseElement: () => T): NodeArray<T> {
15281525
const saveParsingContext = parsingContext;
15291526
parsingContext |= 1 << kind;
1530-
const result = createNodeArray<T>();
1527+
const list = [];
1528+
const listPos = getNodePos();
15311529

15321530
while (!isListTerminator(kind)) {
15331531
if (isListElement(kind, /*inErrorRecovery*/ false)) {
15341532
const element = parseListElement(kind, parseElement);
1535-
result.push(element);
1533+
list.push(element);
15361534

15371535
continue;
15381536
}
@@ -1542,9 +1540,8 @@ namespace ts {
15421540
}
15431541
}
15441542

1545-
result.end = getNodeEnd();
15461543
parsingContext = saveParsingContext;
1547-
return result;
1544+
return createNodeArray(list, listPos);
15481545
}
15491546

15501547
function parseListElement<T extends Node>(parsingContext: ParsingContext, parseElement: () => T): T {
@@ -1874,13 +1871,14 @@ namespace ts {
18741871
function parseDelimitedList<T extends Node>(kind: ParsingContext, parseElement: () => T, considerSemicolonAsDelimiter?: boolean): NodeArray<T> {
18751872
const saveParsingContext = parsingContext;
18761873
parsingContext |= 1 << kind;
1877-
const result = createNodeArray<T>();
1874+
const list = [];
1875+
const listPos = getNodePos();
18781876

18791877
let commaStart = -1; // Meaning the previous token was not a comma
18801878
while (true) {
18811879
if (isListElement(kind, /*inErrorRecovery*/ false)) {
18821880
const startPos = scanner.getStartPos();
1883-
result.push(parseListElement(kind, parseElement));
1881+
list.push(parseListElement(kind, parseElement));
18841882
commaStart = scanner.getTokenPos();
18851883

18861884
if (parseOptional(SyntaxKind.CommaToken)) {
@@ -1924,6 +1922,8 @@ namespace ts {
19241922
}
19251923
}
19261924

1925+
parsingContext = saveParsingContext;
1926+
const result = createNodeArray(list, listPos);
19271927
// Recording the trailing comma is deliberately done after the previous
19281928
// loop, and not just if we see a list terminator. This is because the list
19291929
// may have ended incorrectly, but it is still important to know if there
@@ -1933,14 +1933,11 @@ namespace ts {
19331933
// Always preserve a trailing comma by marking it on the NodeArray
19341934
result.hasTrailingComma = true;
19351935
}
1936-
1937-
result.end = getNodeEnd();
1938-
parsingContext = saveParsingContext;
19391936
return result;
19401937
}
19411938

19421939
function createMissingList<T extends Node>(): NodeArray<T> {
1943-
return createNodeArray<T>();
1940+
return createNodeArray<T>([], getNodePos());
19441941
}
19451942

19461943
function parseBracketedList<T extends Node>(kind: ParsingContext, parseElement: () => T, open: SyntaxKind, close: SyntaxKind): NodeArray<T> {
@@ -2015,15 +2012,15 @@ namespace ts {
20152012
template.head = parseTemplateHead();
20162013
Debug.assert(template.head.kind === SyntaxKind.TemplateHead, "Template head has wrong token kind");
20172014

2018-
const templateSpans = createNodeArray<TemplateSpan>();
2015+
const list = [];
2016+
const listPos = getNodePos();
20192017

20202018
do {
2021-
templateSpans.push(parseTemplateSpan());
2019+
list.push(parseTemplateSpan());
20222020
}
2023-
while (lastOrUndefined(templateSpans).literal.kind === SyntaxKind.TemplateMiddle);
2021+
while (lastOrUndefined(list).literal.kind === SyntaxKind.TemplateMiddle);
20242022

2025-
templateSpans.end = getNodeEnd();
2026-
template.templateSpans = templateSpans;
2023+
template.templateSpans = createNodeArray(list, listPos);
20272024

20282025
return finishNode(template);
20292026
}
@@ -2807,13 +2804,12 @@ namespace ts {
28072804
parseOptional(operator);
28082805
let type = parseConstituentType();
28092806
if (token() === operator) {
2810-
const types = createNodeArray<TypeNode>([type], type.pos);
2807+
const types = [type];
28112808
while (parseOptional(operator)) {
28122809
types.push(parseConstituentType());
28132810
}
2814-
types.end = getNodeEnd();
28152811
const node = <UnionOrIntersectionTypeNode>createNode(kind, type.pos);
2816-
node.types = types;
2812+
node.types = createNodeArray(types, type.pos);
28172813
type = finishNode(node);
28182814
}
28192815
return type;
@@ -3179,8 +3175,7 @@ namespace ts {
31793175
parameter.name = identifier;
31803176
finishNode(parameter);
31813177

3182-
node.parameters = createNodeArray<ParameterDeclaration>([parameter], parameter.pos);
3183-
node.parameters.end = parameter.end;
3178+
node.parameters = createNodeArray<ParameterDeclaration>([parameter], parameter.pos, parameter.end);
31843179

31853180
node.equalsGreaterThanToken = parseExpectedToken(SyntaxKind.EqualsGreaterThanToken, /*reportAtCurrentPosition*/ false, Diagnostics._0_expected, "=>");
31863181
node.body = parseArrowFunctionExpressionBody(/*isAsync*/ !!asyncModifier);
@@ -4030,7 +4025,8 @@ namespace ts {
40304025
}
40314026

40324027
function parseJsxChildren(openingTagName: LeftHandSideExpression): NodeArray<JsxChild> {
4033-
const result = createNodeArray<JsxChild>();
4028+
const list = [];
4029+
const listPos = getNodePos();
40344030
const saveParsingContext = parsingContext;
40354031
parsingContext |= 1 << ParsingContext.JsxChildren;
40364032

@@ -4051,15 +4047,13 @@ namespace ts {
40514047
}
40524048
const child = parseJsxChild();
40534049
if (child) {
4054-
result.push(child);
4050+
list.push(child);
40554051
}
40564052
}
40574053

4058-
result.end = scanner.getTokenPos();
4059-
40604054
parsingContext = saveParsingContext;
40614055

4062-
return result;
4056+
return createNodeArray(list, listPos);
40634057
}
40644058

40654059
function parseJsxAttributes(): JsxAttributes {
@@ -5452,27 +5446,19 @@ namespace ts {
54525446
}
54535447

54545448
function parseDecorators(): NodeArray<Decorator> {
5455-
let decorators: NodeArray<Decorator> & Decorator[];
5449+
let list: Decorator[];
5450+
const listPos = getNodePos();
54565451
while (true) {
54575452
const decoratorStart = getNodePos();
54585453
if (!parseOptional(SyntaxKind.AtToken)) {
54595454
break;
54605455
}
5461-
54625456
const decorator = <Decorator>createNode(SyntaxKind.Decorator, decoratorStart);
54635457
decorator.expression = doInDecoratorContext(parseLeftHandSideExpressionOrHigher);
54645458
finishNode(decorator);
5465-
if (!decorators) {
5466-
decorators = createNodeArray<Decorator>([decorator], decoratorStart);
5467-
}
5468-
else {
5469-
decorators.push(decorator);
5470-
}
5471-
}
5472-
if (decorators) {
5473-
decorators.end = getNodeEnd();
5459+
(list || (list = [])).push(decorator);
54745460
}
5475-
return decorators;
5461+
return list && createNodeArray(list, listPos);
54765462
}
54775463

54785464
/*
@@ -5483,7 +5469,8 @@ namespace ts {
54835469
* In such situations, 'permitInvalidConstAsModifier' should be set to true.
54845470
*/
54855471
function parseModifiers(permitInvalidConstAsModifier?: boolean): NodeArray<Modifier> | undefined {
5486-
let modifiers: MutableNodeArray<Modifier> | undefined;
5472+
let list: Modifier[];
5473+
const listPos = getNodePos();
54875474
while (true) {
54885475
const modifierStart = scanner.getStartPos();
54895476
const modifierKind = token();
@@ -5502,17 +5489,9 @@ namespace ts {
55025489
}
55035490

55045491
const modifier = finishNode(<Modifier>createNode(modifierKind, modifierStart));
5505-
if (!modifiers) {
5506-
modifiers = createNodeArray<Modifier>([modifier], modifierStart);
5507-
}
5508-
else {
5509-
modifiers.push(modifier);
5510-
}
5511-
}
5512-
if (modifiers) {
5513-
modifiers.end = scanner.getStartPos();
5492+
(list || (list = [])).push(modifier);
55145493
}
5515-
return modifiers;
5494+
return list && createNodeArray(list, listPos);
55165495
}
55175496

55185497
function parseModifiersForArrowFunction(): NodeArray<Modifier> {
@@ -5523,9 +5502,7 @@ namespace ts {
55235502
nextToken();
55245503
const modifier = finishNode(<Modifier>createNode(modifierKind, modifierStart));
55255504
modifiers = createNodeArray<Modifier>([modifier], modifierStart);
5526-
modifiers.end = scanner.getStartPos();
55275505
}
5528-
55295506
return modifiers;
55305507
}
55315508

@@ -6227,7 +6204,9 @@ namespace ts {
62276204
Debug.assert(start <= end);
62286205
Debug.assert(end <= content.length);
62296206

6230-
let tags: MutableNodeArray<JSDocTag>;
6207+
let tags: JSDocTag[];
6208+
let tagsPos: number;
6209+
let tagsEnd: number;
62316210
const comments: string[] = [];
62326211
let result: JSDoc;
62336212

@@ -6360,7 +6339,7 @@ namespace ts {
63606339

63616340
function createJSDocComment(): JSDoc {
63626341
const result = <JSDoc>createNode(SyntaxKind.JSDocComment, start);
6363-
result.tags = tags;
6342+
result.tags = tags && createNodeArray(tags, tagsPos, tagsEnd);
63646343
result.comment = comments.length ? comments.join("") : undefined;
63656344
return finishNode(result, end);
63666345
}
@@ -6500,12 +6479,13 @@ namespace ts {
65006479
tag.comment = comments.join("");
65016480

65026481
if (!tags) {
6503-
tags = createNodeArray([tag], tag.pos);
6482+
tags = [tag];
6483+
tagsPos = tag.pos;
65046484
}
65056485
else {
65066486
tags.push(tag);
65076487
}
6508-
tags.end = tag.end;
6488+
tagsEnd = tag.end;
65096489
}
65106490

65116491
function tryParseTypeExpression(): JSDocTypeExpression | undefined {
@@ -6805,7 +6785,8 @@ namespace ts {
68056785
}
68066786

68076787
// Type parameter list looks like '@template T,U,V'
6808-
const typeParameters = createNodeArray<TypeParameterDeclaration>();
6788+
const typeParameters = [];
6789+
const typeParametersPos = getNodePos();
68096790

68106791
while (true) {
68116792
const name = parseJSDocIdentifierName();
@@ -6833,9 +6814,8 @@ namespace ts {
68336814
const result = <JSDocTemplateTag>createNode(SyntaxKind.JSDocTemplateTag, atToken.pos);
68346815
result.atToken = atToken;
68356816
result.tagName = tagName;
6836-
result.typeParameters = typeParameters;
6817+
result.typeParameters = createNodeArray(typeParameters, typeParametersPos);
68376818
finishNode(result);
6838-
typeParameters.end = result.end;
68396819
return result;
68406820
}
68416821

0 commit comments

Comments
 (0)