Merge pull request #18153 from Microsoft/optimizeArrays

Optimize array operations to reduce memory footprint
This commit is contained in:
Anders Hejlsberg 2017-09-07 22:26:48 +01:00 committed by GitHub
commit 8c64937888
2 changed files with 51 additions and 69 deletions

View File

@ -203,9 +203,11 @@ namespace ts {
node.symbol = symbol;
if (!symbol.declarations) {
symbol.declarations = [];
symbol.declarations = [node];
}
else {
symbol.declarations.push(node);
}
symbol.declarations.push(node);
if (symbolFlags & SymbolFlags.HasExports && !symbol.exports) {
symbol.exports = createSymbolTable();

View File

@ -940,10 +940,6 @@ namespace ts {
return scanner.getStartPos();
}
function getNodeEnd(): number {
return scanner.getStartPos();
}
// Use this function to access the current token instead of reading the currentToken
// variable. Since function results aren't narrowed in control flow analysis, this ensures
// that the type checker doesn't make wrong assumptions about the type of the current
@ -1135,13 +1131,14 @@ namespace ts {
new TokenConstructor(kind, pos, pos);
}
function createNodeArray<T extends Node>(elements?: T[], pos?: number): MutableNodeArray<T> {
const array = <MutableNodeArray<T>>(elements || []);
if (!(pos >= 0)) {
pos = getNodePos();
}
function createNodeArray<T extends Node>(elements: T[], pos: number, end?: number): NodeArray<T> {
// Since the element list of a node array is typically created by starting with an empty array and
// repeatedly calling push(), the list may not have the optimal memory layout. We invoke slice() for
// small arrays (1 to 4 elements) to give the VM a chance to allocate an optimal representation.
const length = elements.length;
const array = <MutableNodeArray<T>>(length >= 1 && length <= 4 ? elements.slice() : elements);
array.pos = pos;
array.end = pos;
array.end = end === undefined ? scanner.getStartPos() : end;
return array;
}
@ -1527,12 +1524,13 @@ namespace ts {
function parseList<T extends Node>(kind: ParsingContext, parseElement: () => T): NodeArray<T> {
const saveParsingContext = parsingContext;
parsingContext |= 1 << kind;
const result = createNodeArray<T>();
const list = [];
const listPos = getNodePos();
while (!isListTerminator(kind)) {
if (isListElement(kind, /*inErrorRecovery*/ false)) {
const element = parseListElement(kind, parseElement);
result.push(element);
list.push(element);
continue;
}
@ -1542,9 +1540,8 @@ namespace ts {
}
}
result.end = getNodeEnd();
parsingContext = saveParsingContext;
return result;
return createNodeArray(list, listPos);
}
function parseListElement<T extends Node>(parsingContext: ParsingContext, parseElement: () => T): T {
@ -1874,13 +1871,14 @@ namespace ts {
function parseDelimitedList<T extends Node>(kind: ParsingContext, parseElement: () => T, considerSemicolonAsDelimiter?: boolean): NodeArray<T> {
const saveParsingContext = parsingContext;
parsingContext |= 1 << kind;
const result = createNodeArray<T>();
const list = [];
const listPos = getNodePos();
let commaStart = -1; // Meaning the previous token was not a comma
while (true) {
if (isListElement(kind, /*inErrorRecovery*/ false)) {
const startPos = scanner.getStartPos();
result.push(parseListElement(kind, parseElement));
list.push(parseListElement(kind, parseElement));
commaStart = scanner.getTokenPos();
if (parseOptional(SyntaxKind.CommaToken)) {
@ -1924,6 +1922,8 @@ namespace ts {
}
}
parsingContext = saveParsingContext;
const result = createNodeArray(list, listPos);
// Recording the trailing comma is deliberately done after the previous
// loop, and not just if we see a list terminator. This is because the list
// may have ended incorrectly, but it is still important to know if there
@ -1933,14 +1933,11 @@ namespace ts {
// Always preserve a trailing comma by marking it on the NodeArray
result.hasTrailingComma = true;
}
result.end = getNodeEnd();
parsingContext = saveParsingContext;
return result;
}
function createMissingList<T extends Node>(): NodeArray<T> {
return createNodeArray<T>();
return createNodeArray<T>([], getNodePos());
}
function parseBracketedList<T extends Node>(kind: ParsingContext, parseElement: () => T, open: SyntaxKind, close: SyntaxKind): NodeArray<T> {
@ -2015,15 +2012,15 @@ namespace ts {
template.head = parseTemplateHead();
Debug.assert(template.head.kind === SyntaxKind.TemplateHead, "Template head has wrong token kind");
const templateSpans = createNodeArray<TemplateSpan>();
const list = [];
const listPos = getNodePos();
do {
templateSpans.push(parseTemplateSpan());
list.push(parseTemplateSpan());
}
while (lastOrUndefined(templateSpans).literal.kind === SyntaxKind.TemplateMiddle);
while (lastOrUndefined(list).literal.kind === SyntaxKind.TemplateMiddle);
templateSpans.end = getNodeEnd();
template.templateSpans = templateSpans;
template.templateSpans = createNodeArray(list, listPos);
return finishNode(template);
}
@ -2807,13 +2804,12 @@ namespace ts {
parseOptional(operator);
let type = parseConstituentType();
if (token() === operator) {
const types = createNodeArray<TypeNode>([type], type.pos);
const types = [type];
while (parseOptional(operator)) {
types.push(parseConstituentType());
}
types.end = getNodeEnd();
const node = <UnionOrIntersectionTypeNode>createNode(kind, type.pos);
node.types = types;
node.types = createNodeArray(types, type.pos);
type = finishNode(node);
}
return type;
@ -3179,8 +3175,7 @@ namespace ts {
parameter.name = identifier;
finishNode(parameter);
node.parameters = createNodeArray<ParameterDeclaration>([parameter], parameter.pos);
node.parameters.end = parameter.end;
node.parameters = createNodeArray<ParameterDeclaration>([parameter], parameter.pos, parameter.end);
node.equalsGreaterThanToken = parseExpectedToken(SyntaxKind.EqualsGreaterThanToken, /*reportAtCurrentPosition*/ false, Diagnostics._0_expected, "=>");
node.body = parseArrowFunctionExpressionBody(/*isAsync*/ !!asyncModifier);
@ -4030,7 +4025,8 @@ namespace ts {
}
function parseJsxChildren(openingTagName: LeftHandSideExpression): NodeArray<JsxChild> {
const result = createNodeArray<JsxChild>();
const list = [];
const listPos = getNodePos();
const saveParsingContext = parsingContext;
parsingContext |= 1 << ParsingContext.JsxChildren;
@ -4051,15 +4047,13 @@ namespace ts {
}
const child = parseJsxChild();
if (child) {
result.push(child);
list.push(child);
}
}
result.end = scanner.getTokenPos();
parsingContext = saveParsingContext;
return result;
return createNodeArray(list, listPos);
}
function parseJsxAttributes(): JsxAttributes {
@ -5452,27 +5446,19 @@ namespace ts {
}
function parseDecorators(): NodeArray<Decorator> {
let decorators: NodeArray<Decorator> & Decorator[];
let list: Decorator[];
const listPos = getNodePos();
while (true) {
const decoratorStart = getNodePos();
if (!parseOptional(SyntaxKind.AtToken)) {
break;
}
const decorator = <Decorator>createNode(SyntaxKind.Decorator, decoratorStart);
decorator.expression = doInDecoratorContext(parseLeftHandSideExpressionOrHigher);
finishNode(decorator);
if (!decorators) {
decorators = createNodeArray<Decorator>([decorator], decoratorStart);
}
else {
decorators.push(decorator);
}
(list || (list = [])).push(decorator);
}
if (decorators) {
decorators.end = getNodeEnd();
}
return decorators;
return list && createNodeArray(list, listPos);
}
/*
@ -5483,7 +5469,8 @@ namespace ts {
* In such situations, 'permitInvalidConstAsModifier' should be set to true.
*/
function parseModifiers(permitInvalidConstAsModifier?: boolean): NodeArray<Modifier> | undefined {
let modifiers: MutableNodeArray<Modifier> | undefined;
let list: Modifier[];
const listPos = getNodePos();
while (true) {
const modifierStart = scanner.getStartPos();
const modifierKind = token();
@ -5502,17 +5489,9 @@ namespace ts {
}
const modifier = finishNode(<Modifier>createNode(modifierKind, modifierStart));
if (!modifiers) {
modifiers = createNodeArray<Modifier>([modifier], modifierStart);
}
else {
modifiers.push(modifier);
}
(list || (list = [])).push(modifier);
}
if (modifiers) {
modifiers.end = scanner.getStartPos();
}
return modifiers;
return list && createNodeArray(list, listPos);
}
function parseModifiersForArrowFunction(): NodeArray<Modifier> {
@ -5523,9 +5502,7 @@ namespace ts {
nextToken();
const modifier = finishNode(<Modifier>createNode(modifierKind, modifierStart));
modifiers = createNodeArray<Modifier>([modifier], modifierStart);
modifiers.end = scanner.getStartPos();
}
return modifiers;
}
@ -6227,7 +6204,9 @@ namespace ts {
Debug.assert(start <= end);
Debug.assert(end <= content.length);
let tags: MutableNodeArray<JSDocTag>;
let tags: JSDocTag[];
let tagsPos: number;
let tagsEnd: number;
const comments: string[] = [];
let result: JSDoc;
@ -6360,7 +6339,7 @@ namespace ts {
function createJSDocComment(): JSDoc {
const result = <JSDoc>createNode(SyntaxKind.JSDocComment, start);
result.tags = tags;
result.tags = tags && createNodeArray(tags, tagsPos, tagsEnd);
result.comment = comments.length ? comments.join("") : undefined;
return finishNode(result, end);
}
@ -6500,12 +6479,13 @@ namespace ts {
tag.comment = comments.join("");
if (!tags) {
tags = createNodeArray([tag], tag.pos);
tags = [tag];
tagsPos = tag.pos;
}
else {
tags.push(tag);
}
tags.end = tag.end;
tagsEnd = tag.end;
}
function tryParseTypeExpression(): JSDocTypeExpression | undefined {
@ -6805,7 +6785,8 @@ namespace ts {
}
// Type parameter list looks like '@template T,U,V'
const typeParameters = createNodeArray<TypeParameterDeclaration>();
const typeParameters = [];
const typeParametersPos = getNodePos();
while (true) {
const name = parseJSDocIdentifierName();
@ -6833,9 +6814,8 @@ namespace ts {
const result = <JSDocTemplateTag>createNode(SyntaxKind.JSDocTemplateTag, atToken.pos);
result.atToken = atToken;
result.tagName = tagName;
result.typeParameters = typeParameters;
result.typeParameters = createNodeArray(typeParameters, typeParametersPos);
finishNode(result);
typeParameters.end = result.end;
return result;
}