Skip to content

Commit

Permalink
fix(parser): dedupe some logic for perf reasons
Browse files Browse the repository at this point in the history
  • Loading branch information
KFlash committed May 9, 2019
1 parent c9545fe commit fd7f2d8
Show file tree
Hide file tree
Showing 13 changed files with 431 additions and 92 deletions.
5 changes: 3 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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": "0.1.2",
"version": "0.1.3",
"description": "Fast and lightweight, standard-compliant javascript parser written in ECMAScript",
"main": "dist/meriyah.umd.js",
"module": "dist/meriyah.esm.js",
Expand Down
173 changes: 96 additions & 77 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2359,11 +2359,12 @@ export function parseMemberOrUpdateExpression(
};
}

context = (context | Context.DisallowInContext) ^ Context.DisallowInContext;

if ((parser.token & Token.IsMemberOrCallExpression) === Token.IsMemberOrCallExpression) {
if (parser.token === Token.Period) {
/* Property */
nextToken(parser, context);

if ((parser.token & (Token.IsIdentifier | Token.Keyword)) === 0 && parser.token !== Token.PrivateField)
report(parser, Errors.Unexpected);
parser.assignable = AssignmentKind.Assignable;
Expand Down Expand Up @@ -2450,57 +2451,29 @@ export function parsePrimaryExpressionExtended(
// TemplateLiteral
// do Block
// AsyncFunctionLiteral
// YieldExpression
// AwaitExpression

const { token } = parser;

if ((token & Token.IsIdentifier) === Token.IsIdentifier) {
if (token === Token.YieldKeyword) {
if (!assignable) {
validateIdentifier(parser, context, type, token);
parser.assignable = AssignmentKind.Assignable;
return parseIdentifier(parser, context);
}

return parseYieldExpressionOrIdentifier(parser, context);
}

if (parser.token === Token.LetKeyword) {
if (context & Context.Strict) report(parser, Errors.StrictInvalidLetInExprPos);
if (type & (BindingType.Let | BindingType.Const)) report(parser, Errors.InvalidLetBoundName);
// falls through
}

if (token === Token.AsyncKeyword || (token === Token.EscapedReserved && parser.tokenValue === 'async')) {
return parseAsyncExpression(parser, context, inNewExpression, assignable);
}

if (parser.token === Token.EscapedReserved) report(parser, Errors.InvalidEscapedKeyword);

parser.assignable = AssignmentKind.Assignable;

if (token === Token.AwaitKeyword) return parseAwaitExpressionOrIdentifier(parser, context, inNewExpression);

const expr = parseIdentifier(parser, context);

if ((token & Token.IsEvalOrArguments) === Token.IsEvalOrArguments) {
if (parser.token === Token.Arrow) {
if (context & Context.Strict) report(parser, Errors.InvalidEscapedKeyword);
parser.flags |= Flags.SimpleParameterList;
parser.assignable = AssignmentKind.NotAssignable;
return parseArrowFunctionExpression(parser, context, [expr], /* isAsync */ 0);
}

if (context & Context.Strict) {
parser.assignable = AssignmentKind.NotAssignable;
}
/**
* https://tc39.github.io/ecma262/#sec-unary-operators
*
* UnaryExpression :
* 1. LeftHandSideExpression
* 2. void UnaryExpression
* 3. typeof UnaryExpression
* 4. + UnaryExpression
* 5. - UnaryExpression
* 6. ! UnaryExpression
*
*/
if ((token & Token.IsUnaryOp) === Token.IsUnaryOp) {
if (inNewExpression && (token !== Token.VoidKeyword || token !== Token.TypeofKeyword)) {
report(parser, Errors.InvalidNewUnary);
}

return parseIdentifierOrArrow(parser, context, expr, assignable);
}

if ((token & Token.IsStringOrNumber) === Token.IsStringOrNumber) {
parser.assignable = AssignmentKind.NotAssignable;
return parseLiteral(parser, context);
return parseUnaryExpression(parser, context);
}

/**
Expand Down Expand Up @@ -2535,23 +2508,75 @@ export function parsePrimaryExpressionExtended(
}

/**
* https://tc39.github.io/ecma262/#sec-unary-operators
*
* UnaryExpression :
* 1. LeftHandSideExpression
* 2. void UnaryExpression
* 3. typeof UnaryExpression
* 4. + UnaryExpression
* 5. - UnaryExpression
* 6. ! UnaryExpression
*
* YieldExpression[In, Await]:
* yield
* yield[no LineTerminator here]AssignmentExpression[?In, +Yield, ?Await]
* yield[no LineTerminator here]*AssignmentExpression[?In, +Yield, ?Await]
*/
if ((token & Token.IsUnaryOp) === Token.IsUnaryOp) {
if (inNewExpression && (token !== Token.VoidKeyword || token !== Token.TypeofKeyword)) {
report(parser, Errors.InvalidNewUnary);

if (token === Token.YieldKeyword) {
if (!assignable) {
validateIdentifier(parser, context, type, token);
parser.assignable = AssignmentKind.Assignable;
return parseIdentifier(parser, context);
}

return parseYieldExpressionOrIdentifier(parser, context);
}

/**
* AwaitExpression[Yield]:
* awaitUnaryExpression[?Yield, +Await]
*/
if (token === Token.AwaitKeyword) {
return parseAwaitExpressionOrIdentifier(parser, context, inNewExpression);
}

/**
* LexicalBinding[In, Yield, Await]:
* BindingIdentifier[?Yield, ?Await]Initializer[?In, ?Yield, ?Await]opt
* BindingPattern[?Yield, ?Await]Initializer[?In, ?Yield, ?Await]
*/

if (parser.token === Token.LetKeyword) {
if (context & Context.Strict) report(parser, Errors.StrictInvalidLetInExprPos);
if (type & (BindingType.Let | BindingType.Const)) report(parser, Errors.InvalidLetBoundName);
// falls through
}

if ((token & Token.IsIdentifier) === Token.IsIdentifier) {
const expr = parseIdentifier(parser, context);

if (token === Token.AsyncKeyword) {
return parseAsyncExpression(parser, context, token, expr, inNewExpression, assignable);
}

if (token === Token.EscapedReserved) report(parser, Errors.InvalidEscapedKeyword);

const IsEvalOrArguments = (token & Token.IsEvalOrArguments) === Token.IsEvalOrArguments;

if (parser.token === Token.Arrow) {
if (IsEvalOrArguments) {
if (context & Context.Strict) report(parser, Errors.StrictEvalArguments);
parser.flags |= Flags.SimpleParameterList;
}

if (!assignable) report(parser, Errors.InvalidAssignmentTarget);

return parseArrowFunctionExpression(parser, context, [expr], /* isAsync */ 0);
}

parser.assignable =
context & Context.Strict && IsEvalOrArguments
? (parser.assignable = AssignmentKind.NotAssignable)
: AssignmentKind.Assignable;

return expr;
}

if ((token & Token.IsStringOrNumber) === Token.IsStringOrNumber) {
parser.assignable = AssignmentKind.NotAssignable;
return parseUnaryExpression(parser, context);
return parseLiteral(parser, context);
}

switch (token) {
Expand Down Expand Up @@ -4302,13 +4327,7 @@ export function parseIdentifierOrArrow(
assignable: 0 | 1
): ESTree.Identifier | ESTree.ArrowFunctionExpression {
if (parser.token === Token.Arrow) {
parser.flags = (parser.flags | Flags.SimpleParameterList) ^ Flags.SimpleParameterList;
if (parser.flags & Flags.NewLine) {
report(parser, Errors.InvalidLineBreak);
}
if (!assignable) {
report(parser, Errors.InvalidAssignmentTarget);
}
if (!assignable) report(parser, Errors.InvalidAssignmentTarget);
return parseArrowFunctionExpression(parser, context, [expr], /* isAsync */ 0);
}
return expr;
Expand Down Expand Up @@ -4494,11 +4513,13 @@ export function parseNewExpression(
context: Context
): ESTree.NewExpression | ESTree.Expression | ESTree.MetaProperty {
const id = parseIdentifier(parser, context | Context.AllowRegExp);
if (consumeOpt(parser, context, Token.Period)) {
if ((context & Context.AllowNewTarget) === 0 || parser.tokenValue !== 'target')
report(parser, Errors.InvalidNewTarget);
parser.assignable = AssignmentKind.NotAssignable;
return parseMetaProperty(parser, context, id);
if (parser.token === Token.Period) {
nextToken(parser, context);
if (context & Context.AllowNewTarget && parser.token === Token.Target) {
parser.assignable = AssignmentKind.NotAssignable;
return parseMetaProperty(parser, context, id);
}
report(parser, Errors.InvalidNewTarget);
}
parser.assignable = AssignmentKind.NotAssignable;
const callee = parseMemberOrUpdateExpression(
Expand Down Expand Up @@ -4547,13 +4568,11 @@ export function parseMetaProperty(parser: ParserState, context: Context, meta: E
export function parseAsyncExpression(
parser: ParserState,
context: Context,
token: Token,
expr: ESTree.Identifier,
inNewExpression: 0 | 1,
assignable: 0 | 1
): ESTree.Expression {
const { token } = parser;

const expr: ESTree.Identifier = parseIdentifier(parser, context);

const isNewLine = parser.flags & Flags.NewLine;

if (!isNewLine) {
Expand Down
4 changes: 3 additions & 1 deletion src/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ export const enum Token {
PrivateField = 131,
Template = 132,
Decorator = 133,
Target = 134 | IsIdentifier
}

export const KeywordDescTable = [
Expand Down Expand Up @@ -224,7 +225,7 @@ export const KeywordDescTable = [
/* Others */
'enum', 'eval', 'arguments', 'escaped reserved', 'escaped future reserved', 'reserved if strict', '#',

'BigIntLiteral', 'WhiteSpace', 'Illegal', 'LineTerminator', 'PrivateField', 'Template', '@',
'BigIntLiteral', 'WhiteSpace', 'Illegal', 'LineTerminator', 'PrivateField', 'Template', '@', 'target'
];

// Normal object is much faster than Object.create(null), even with typeof check to avoid Object.prototype interference
Expand Down Expand Up @@ -284,4 +285,5 @@ export const descKeywordTable: { [key: string]: Token } = Object.create(null, {
eval: { value: Token.Eval },
as: { value: Token.AsKeyword },
arguments: { value: Token.Arguments },
target: { value: Token.Target },
});
2 changes: 1 addition & 1 deletion test/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
## Meriyah test suite

Runs ~62 000 tests
Runs ~67 000 tests
Loading

0 comments on commit fd7f2d8

Please sign in to comment.