Skip to content

Commit

Permalink
fix(parser): avoid 'push' in module parsing (performance)
Browse files Browse the repository at this point in the history
  • Loading branch information
KFlash committed Nov 9, 2019
1 parent 3619374 commit e99a8a8
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 29 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "meriyah",
"version": "1.9.0",
"version": "1.9.1",
"description": "A 100% compliant, self-hosted javascript parser with high focus on both performance and stability",
"main": "dist/meriyah.umd.js",
"module": "dist/meriyah.esm.js",
Expand Down
23 changes: 15 additions & 8 deletions src/lexer/comments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,21 @@ export const CommentTypes = ['SingleLine', 'MultiLine', 'HTMLOpen', 'HTMLClose',
export function skipHashBang(parser: ParserState): void {
// HashbangComment ::
// #! SingleLineCommentChars_opt
if (parser.currentChar === Chars.Hash && parser.source.charCodeAt(parser.index + 1) === Chars.Exclamation) {
skipSingleLineComment(parser, LexerState.None, CommentType.HashBang);
const source = parser.source;
if (parser.currentChar === Chars.Hash && source.charCodeAt(parser.index + 1) === Chars.Exclamation) {
skipSingleLineComment(parser, source, LexerState.None, CommentType.HashBang);
}
}

export function skipSingleHTMLComment(
parser: ParserState,
source: string,
state: LexerState,
context: Context,
type: CommentType
): LexerState {
if (context & Context.Module) report(parser, Errors.Unexpected);
return skipSingleLineComment(parser, state, type);
return skipSingleLineComment(parser, source, state, type);
}

/**
Expand All @@ -42,14 +44,19 @@ export function skipSingleHTMLComment(
* @param parser Parser object
* @param state Lexer state
*/
export function skipSingleLineComment(parser: ParserState, state: LexerState, type: CommentType): LexerState {
export function skipSingleLineComment(
parser: ParserState,
source: string,
state: LexerState,
type: CommentType
): LexerState {
const { index } = parser;
while (parser.index < parser.end) {
if (CharTypes[parser.currentChar] & CharFlags.LineTerminator) {
const isCR = parser.currentChar === Chars.CarriageReturn;
scanNewLine(parser);
if (isCR && parser.index < parser.end && parser.currentChar === Chars.LineFeed)
parser.currentChar = parser.source.charCodeAt(++parser.index);
parser.currentChar = source.charCodeAt(++parser.index);
break;
} else if ((parser.currentChar ^ Chars.LineSeparator) <= 1) {
scanNewLine(parser);
Expand All @@ -58,7 +65,7 @@ export function skipSingleLineComment(parser: ParserState, state: LexerState, ty
advanceChar(parser);
}
if (parser.onComment)
parser.onComment(CommentTypes[type & 0xff], parser.source.slice(index, parser.index), index, parser.index);
parser.onComment(CommentTypes[type & 0xff], source.slice(index, parser.index), index, parser.index);
return state | LexerState.NewLine;
}

Expand All @@ -68,7 +75,7 @@ export function skipSingleLineComment(parser: ParserState, state: LexerState, ty
* @param parser Parser object
* @param state Lexer state
*/
export function skipMultiLineComment(parser: ParserState, state: LexerState): LexerState | void {
export function skipMultiLineComment(parser: ParserState, source: string, state: LexerState): LexerState | void {
const { index } = parser;
while (parser.index < parser.end) {
if (parser.currentChar < 0x2b) {
Expand All @@ -83,7 +90,7 @@ export function skipMultiLineComment(parser: ParserState, state: LexerState): Le
if (parser.onComment)
parser.onComment(
CommentTypes[CommentType.Multi & 0xff],
parser.source.slice(index, parser.index - 2),
source.slice(index, parser.index - 2),
index,
parser.index
);
Expand Down
26 changes: 14 additions & 12 deletions src/lexer/scan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,8 @@ export function nextToken(parser: ParserState, context: Context): void {
export function scanSingleToken(parser: ParserState, context: Context, state: LexerState): Token {
const isStartOfLine = parser.index === 0;

let source = parser.source;

while (parser.index < parser.end) {
parser.tokenPos = parser.index;
parser.colPos = parser.column;
Expand Down Expand Up @@ -234,13 +236,13 @@ export function scanSingleToken(parser: ParserState, context: Context, state: Le
// Treat HTML begin-comment as comment-till-end-of-line.
const index = parser.index + 1;
if (
index + 1 < parser.source.length &&
parser.source.charCodeAt(index) === Chars.Hyphen &&
parser.source.charCodeAt(index + 1) == Chars.Hyphen
index + 1 < parser.end &&
source.charCodeAt(index) === Chars.Hyphen &&
source.charCodeAt(index + 1) == Chars.Hyphen
) {
parser.column += 3;
parser.currentChar = parser.source.charCodeAt((parser.index += 3));
state = skipSingleHTMLComment(parser, state, context, CommentType.HTMLOpen);
parser.currentChar = source.charCodeAt((parser.index += 3));
state = skipSingleHTMLComment(parser, source, state, context, CommentType.HTMLOpen);
continue;
}
return Token.LessThan;
Expand All @@ -251,7 +253,7 @@ export function scanSingleToken(parser: ParserState, context: Context, state: Le

// Check that it's not a comment start.
if (index < parser.end) {
ch = parser.source.charCodeAt(index);
ch = source.charCodeAt(index);
if (ch === Chars.Asterisk || ch === Chars.Slash) break;
}
advanceChar(parser);
Expand Down Expand Up @@ -356,7 +358,7 @@ export function scanSingleToken(parser: ParserState, context: Context, state: Le
if ((state & LexerState.NewLine || isStartOfLine) && parser.currentChar === Chars.GreaterThan) {
if ((context & Context.OptionsWebCompat) === 0) report(parser, Errors.HtmlCommentInWebCompat);
advanceChar(parser);
state = skipSingleHTMLComment(parser, state, context, CommentType.HTMLClose);
state = skipSingleHTMLComment(parser, source, state, context, CommentType.HTMLClose);
continue;
}

Expand All @@ -378,12 +380,12 @@ export function scanSingleToken(parser: ParserState, context: Context, state: Le
const ch = parser.currentChar;
if (ch === Chars.Slash) {
advanceChar(parser);
state = skipSingleLineComment(parser, state, CommentType.Single);
state = skipSingleLineComment(parser, source, state, CommentType.Single);
continue;
}
if (ch === Chars.Asterisk) {
advanceChar(parser);
state = skipMultiLineComment(parser, state) as LexerState;
state = skipMultiLineComment(parser, source, state) as LexerState;
continue;
}
if (context & Context.AllowRegExp) {
Expand Down Expand Up @@ -477,9 +479,9 @@ export function scanSingleToken(parser: ParserState, context: Context, state: Le
return scanNumber(parser, context, NumberKind.Float | NumberKind.Decimal);
if (next === Chars.Period) {
const index = parser.index + 1;
if (index < parser.source.length && parser.source.charCodeAt(index) === Chars.Period) {
if (index < parser.end && source.charCodeAt(index) === Chars.Period) {
parser.column += 2;
parser.currentChar = parser.source.charCodeAt((parser.index += 2));
parser.currentChar = source.charCodeAt((parser.index += 2));
return Token.Ellipsis;
}
}
Expand All @@ -498,7 +500,7 @@ export function scanSingleToken(parser: ParserState, context: Context, state: Le
const index = parser.index + 1;
// Check that it's not followed by any numbers
if (index < parser.end) {
ch = parser.source.charCodeAt(index);
ch = source.charCodeAt(index);
if (!(ch >= Chars.Zero && ch <= Chars.Nine)) {
advanceChar(parser);
return Token.QuestionMarkPeriod;
Expand Down
2 changes: 1 addition & 1 deletion src/meriyah.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ export function parse(source: string, options?: Options): ESTree.Program {
export { Options, ESTree };

// Current version
export const version = '1.9.0';
export const version = '1.9.1';
14 changes: 7 additions & 7 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2422,20 +2422,20 @@ function parseImportDeclaration(

const { tokenPos, linePos, colPos } = parser;

const specifiers: (ESTree.ImportSpecifier | ESTree.ImportDefaultSpecifier | ESTree.ImportNamespaceSpecifier)[] = [];
let specifiers: (ESTree.ImportSpecifier | ESTree.ImportDefaultSpecifier | ESTree.ImportNamespaceSpecifier)[] = [];

// 'import' ModuleSpecifier ';'
if (parser.token === Token.StringLiteral) {
source = parseLiteral(parser, context);
} else {
if (parser.token & Token.IsIdentifier) {
const local = parseRestrictedIdentifier(parser, context, scope);
specifiers.push(
specifiers = [
finishNode(parser, context, tokenPos, linePos, colPos, {
type: 'ImportDefaultSpecifier',
local
})
);
];

// NameSpaceImport
if (consumeOpt(parser, context, Token.Comma)) {
Expand All @@ -2456,7 +2456,7 @@ function parseImportDeclaration(
// Parse NameSpaceImport or NamedImports if present
switch (parser.token) {
case Token.Multiply:
specifiers.push(parseImportNamespaceSpecifier(parser, context, scope));
specifiers = [parseImportNamespaceSpecifier(parser, context, scope)];
break;
case Token.LeftBrace:
parseImportSpecifierOrNamedImports(parser, context, scope, specifiers);
Expand Down Expand Up @@ -2737,7 +2737,7 @@ function parseExportDeclaration(
// https://tc39.github.io/ecma262/#sec-exports
nextToken(parser, context | Context.AllowRegExp);

const specifiers: (ESTree.ExportSpecifier | ESTree.ExportNamespaceSpecifier)[] = [];
let specifiers: (ESTree.ExportSpecifier | ESTree.ExportNamespaceSpecifier)[] = [];

let declaration: ESTree.ExportDeclaration | ESTree.Expression | null = null;
let source: ESTree.Literal | null = null;
Expand Down Expand Up @@ -2882,12 +2882,12 @@ function parseExportDeclaration(

if (isNamedDeclaration) {
if (scope) declareUnboundVariable(parser, parser.tokenValue);
specifiers.push(
specifiers = [
finishNode(parser, context, parser.tokenPos, parser.linePos, parser.colPos, {
type: 'ExportNamespaceSpecifier',
specifier: parseIdentifier(parser, context, 0)
})
);
];
}

consume(parser, context, Token.FromKeyword);
Expand Down

0 comments on commit e99a8a8

Please sign in to comment.