Skip to content

Commit 3f09011

Browse files
committed
Optimize array operations to reduce memory footprint
1 parent 3ea031c commit 3f09011

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
}
@@ -2802,13 +2799,12 @@ namespace ts {
28022799
parseOptional(operator);
28032800
let type = parseConstituentType();
28042801
if (token() === operator) {
2805-
const types = createNodeArray<TypeNode>([type], type.pos);
2802+
const types = [type];
28062803
while (parseOptional(operator)) {
28072804
types.push(parseConstituentType());
28082805
}
2809-
types.end = getNodeEnd();
28102806
const node = <UnionOrIntersectionTypeNode>createNode(kind, type.pos);
2811-
node.types = types;
2807+
node.types = createNodeArray(types, type.pos);
28122808
type = finishNode(node);
28132809
}
28142810
return type;
@@ -3174,8 +3170,7 @@ namespace ts {
31743170
parameter.name = identifier;
31753171
finishNode(parameter);
31763172

3177-
node.parameters = createNodeArray<ParameterDeclaration>([parameter], parameter.pos);
3178-
node.parameters.end = parameter.end;
3173+
node.parameters = createNodeArray<ParameterDeclaration>([parameter], parameter.pos, parameter.end);
31793174

31803175
node.equalsGreaterThanToken = parseExpectedToken(SyntaxKind.EqualsGreaterThanToken, /*reportAtCurrentPosition*/ false, Diagnostics._0_expected, "=>");
31813176
node.body = parseArrowFunctionExpressionBody(/*isAsync*/ !!asyncModifier);
@@ -4025,7 +4020,8 @@ namespace ts {
40254020
}
40264021

40274022
function parseJsxChildren(openingTagName: LeftHandSideExpression): NodeArray<JsxChild> {
4028-
const result = createNodeArray<JsxChild>();
4023+
const list = [];
4024+
const listPos = getNodePos();
40294025
const saveParsingContext = parsingContext;
40304026
parsingContext |= 1 << ParsingContext.JsxChildren;
40314027

@@ -4046,15 +4042,13 @@ namespace ts {
40464042
}
40474043
const child = parseJsxChild();
40484044
if (child) {
4049-
result.push(child);
4045+
list.push(child);
40504046
}
40514047
}
40524048

4053-
result.end = scanner.getTokenPos();
4054-
40554049
parsingContext = saveParsingContext;
40564050

4057-
return result;
4051+
return createNodeArray(list, listPos);
40584052
}
40594053

40604054
function parseJsxAttributes(): JsxAttributes {
@@ -5447,27 +5441,19 @@ namespace ts {
54475441
}
54485442

54495443
function parseDecorators(): NodeArray<Decorator> {
5450-
let decorators: NodeArray<Decorator> & Decorator[];
5444+
let list: Decorator[];
5445+
const listPos = getNodePos();
54515446
while (true) {
54525447
const decoratorStart = getNodePos();
54535448
if (!parseOptional(SyntaxKind.AtToken)) {
54545449
break;
54555450
}
5456-
54575451
const decorator = <Decorator>createNode(SyntaxKind.Decorator, decoratorStart);
54585452
decorator.expression = doInDecoratorContext(parseLeftHandSideExpressionOrHigher);
54595453
finishNode(decorator);
5460-
if (!decorators) {
5461-
decorators = createNodeArray<Decorator>([decorator], decoratorStart);
5462-
}
5463-
else {
5464-
decorators.push(decorator);
5465-
}
5466-
}
5467-
if (decorators) {
5468-
decorators.end = getNodeEnd();
5454+
(list || (list = [])).push(decorator);
54695455
}
5470-
return decorators;
5456+
return list && createNodeArray(list, listPos);
54715457
}
54725458

54735459
/*
@@ -5478,7 +5464,8 @@ namespace ts {
54785464
* In such situations, 'permitInvalidConstAsModifier' should be set to true.
54795465
*/
54805466
function parseModifiers(permitInvalidConstAsModifier?: boolean): NodeArray<Modifier> | undefined {
5481-
let modifiers: MutableNodeArray<Modifier> | undefined;
5467+
let list: Modifier[];
5468+
const listPos = getNodePos();
54825469
while (true) {
54835470
const modifierStart = scanner.getStartPos();
54845471
const modifierKind = token();
@@ -5497,17 +5484,9 @@ namespace ts {
54975484
}
54985485

54995486
const modifier = finishNode(<Modifier>createNode(modifierKind, modifierStart));
5500-
if (!modifiers) {
5501-
modifiers = createNodeArray<Modifier>([modifier], modifierStart);
5502-
}
5503-
else {
5504-
modifiers.push(modifier);
5505-
}
5506-
}
5507-
if (modifiers) {
5508-
modifiers.end = scanner.getStartPos();
5487+
(list || (list = [])).push(modifier);
55095488
}
5510-
return modifiers;
5489+
return list && createNodeArray(list, listPos);
55115490
}
55125491

55135492
function parseModifiersForArrowFunction(): NodeArray<Modifier> {
@@ -5518,9 +5497,7 @@ namespace ts {
55185497
nextToken();
55195498
const modifier = finishNode(<Modifier>createNode(modifierKind, modifierStart));
55205499
modifiers = createNodeArray<Modifier>([modifier], modifierStart);
5521-
modifiers.end = scanner.getStartPos();
55225500
}
5523-
55245501
return modifiers;
55255502
}
55265503

@@ -6222,7 +6199,9 @@ namespace ts {
62226199
Debug.assert(start <= end);
62236200
Debug.assert(end <= content.length);
62246201

6225-
let tags: MutableNodeArray<JSDocTag>;
6202+
let tags: JSDocTag[];
6203+
let tagsPos: number;
6204+
let tagsEnd: number;
62266205
const comments: string[] = [];
62276206
let result: JSDoc;
62286207

@@ -6355,7 +6334,7 @@ namespace ts {
63556334

63566335
function createJSDocComment(): JSDoc {
63576336
const result = <JSDoc>createNode(SyntaxKind.JSDocComment, start);
6358-
result.tags = tags;
6337+
result.tags = tags && createNodeArray(tags, tagsPos, tagsEnd);
63596338
result.comment = comments.length ? comments.join("") : undefined;
63606339
return finishNode(result, end);
63616340
}
@@ -6495,12 +6474,13 @@ namespace ts {
64956474
tag.comment = comments.join("");
64966475

64976476
if (!tags) {
6498-
tags = createNodeArray([tag], tag.pos);
6477+
tags = [tag];
6478+
tagsPos = tag.pos;
64996479
}
65006480
else {
65016481
tags.push(tag);
65026482
}
6503-
tags.end = tag.end;
6483+
tagsEnd = tag.end;
65046484
}
65056485

65066486
function tryParseTypeExpression(): JSDocTypeExpression | undefined {
@@ -6800,7 +6780,8 @@ namespace ts {
68006780
}
68016781

68026782
// Type parameter list looks like '@template T,U,V'
6803-
const typeParameters = createNodeArray<TypeParameterDeclaration>();
6783+
const typeParameters = [];
6784+
const typeParametersPos = getNodePos();
68046785

68056786
while (true) {
68066787
const name = parseJSDocIdentifierName();
@@ -6828,9 +6809,8 @@ namespace ts {
68286809
const result = <JSDocTemplateTag>createNode(SyntaxKind.JSDocTemplateTag, atToken.pos);
68296810
result.atToken = atToken;
68306811
result.tagName = tagName;
6831-
result.typeParameters = typeParameters;
6812+
result.typeParameters = createNodeArray(typeParameters, typeParametersPos);
68326813
finishNode(result);
6833-
typeParameters.end = result.end;
68346814
return result;
68356815
}
68366816

0 commit comments

Comments
 (0)