perf: replace lookahead by lookaheadCharCode (#10371)

* perf: replace lookahead by lookaheadCharCode

* fix: flow ignore

* refactor: add nextTokenStart method

* refactor: duplicated isNewLine code

* refactor: remove lookahead usage from babylon core
This commit is contained in:
Huáng Jùnliàng 2019-10-08 13:09:05 -04:00 committed by Nicolò Ribaudo
parent bc0966a46f
commit 0856618ed5
5 changed files with 62 additions and 49 deletions

View File

@ -613,7 +613,7 @@ export default class ExpressionParser extends LValParser {
} else if (this.match(tt.questionDot)) { } else if (this.match(tt.questionDot)) {
this.expectPlugin("optionalChaining"); this.expectPlugin("optionalChaining");
state.optionalChainMember = true; state.optionalChainMember = true;
if (noCalls && this.lookahead().type === tt.parenL) { if (noCalls && this.lookaheadCharCode() === charCodes.leftParenthesis) {
state.stop = true; state.stop = true;
return base; return base;
} }

View File

@ -8,7 +8,7 @@ import {
isIdentifierStart, isIdentifierStart,
keywordRelationalOperator, keywordRelationalOperator,
} from "../util/identifier"; } from "../util/identifier";
import { lineBreak, skipWhiteSpace } from "../util/whitespace"; import { lineBreak } from "../util/whitespace";
import * as charCodes from "charcodes"; import * as charCodes from "charcodes";
import { import {
BIND_CLASS, BIND_CLASS,
@ -105,10 +105,7 @@ export default class StatementParser extends ExpressionParser {
if (!this.isContextual("let")) { if (!this.isContextual("let")) {
return false; return false;
} }
skipWhiteSpace.lastIndex = this.state.pos; const next = this.nextTokenStart();
const skip = skipWhiteSpace.exec(this.input);
// $FlowIgnore
const next = this.state.pos + skip[0].length;
const nextCh = this.input.charCodeAt(next); const nextCh = this.input.charCodeAt(next);
// For ambiguous cases, determine if a LexicalDeclaration (or only a // For ambiguous cases, determine if a LexicalDeclaration (or only a
// Statement) is allowed here. If context is not empty then only a Statement // Statement) is allowed here. If context is not empty then only a Statement
@ -170,7 +167,7 @@ export default class StatementParser extends ExpressionParser {
case tt._for: case tt._for:
return this.parseForStatement(node); return this.parseForStatement(node);
case tt._function: case tt._function:
if (this.lookahead().type === tt.dot) break; if (this.lookaheadCharCode() === charCodes.dot) break;
if (context) { if (context) {
if (this.state.strict) { if (this.state.strict) {
this.raise( this.raise(
@ -223,8 +220,11 @@ export default class StatementParser extends ExpressionParser {
return this.parseEmptyStatement(node); return this.parseEmptyStatement(node);
case tt._export: case tt._export:
case tt._import: { case tt._import: {
const nextToken = this.lookahead(); const nextTokenCharCode = this.lookaheadCharCode();
if (nextToken.type === tt.parenL || nextToken.type === tt.dot) { if (
nextTokenCharCode === charCodes.leftParenthesis ||
nextTokenCharCode === charCodes.dot
) {
break; break;
} }
@ -1746,11 +1746,11 @@ export default class StatementParser extends ExpressionParser {
maybeParseExportDeclaration(node: N.Node): boolean { maybeParseExportDeclaration(node: N.Node): boolean {
if (this.shouldParseExportDeclaration()) { if (this.shouldParseExportDeclaration()) {
if (this.isContextual("async")) { if (this.isContextual("async")) {
const next = this.lookahead(); const next = this.nextTokenStart();
// export async; // export async;
if (next.type !== tt._function) { if (!this.isUnparsedContextual(next, "function")) {
this.unexpected(next.start, `Unexpected token, expected "function"`); this.unexpected(next, `Unexpected token, expected "function"`);
} }
} }
@ -1765,21 +1765,10 @@ export default class StatementParser extends ExpressionParser {
isAsyncFunction(): boolean { isAsyncFunction(): boolean {
if (!this.isContextual("async")) return false; if (!this.isContextual("async")) return false;
const next = this.nextTokenStart();
const { pos } = this.state;
skipWhiteSpace.lastIndex = pos;
const skip = skipWhiteSpace.exec(this.input);
if (!skip || !skip.length) return false;
const next = pos + skip[0].length;
return ( return (
!lineBreak.test(this.input.slice(pos, next)) && !lineBreak.test(this.input.slice(this.state.pos, next)) &&
this.input.slice(next, next + 8) === "function" && this.isUnparsedContextual(next, "function")
(next + 8 === this.length ||
!isIdentifierChar(this.input.charCodeAt(next + 8)))
); );
} }
@ -1841,10 +1830,10 @@ export default class StatementParser extends ExpressionParser {
return false; return false;
} }
const lookahead = this.lookahead(); const next = this.nextTokenStart();
return ( return (
lookahead.type === tt.comma || this.input.charCodeAt(next) === charCodes.comma ||
(lookahead.type === tt.name && lookahead.value === "from") this.isUnparsedContextual(next, "from")
); );
} }

View File

@ -4,6 +4,8 @@ import { types as tt, type TokenType } from "../tokenizer/types";
import Tokenizer from "../tokenizer"; import Tokenizer from "../tokenizer";
import type { Node } from "../types"; import type { Node } from "../types";
import { lineBreak, skipWhiteSpace } from "../util/whitespace"; import { lineBreak, skipWhiteSpace } from "../util/whitespace";
import { isIdentifierChar } from "../util/identifier";
import * as charCodes from "charcodes";
const literal = /^('|")((?:\\?.)*?)\1/; const literal = /^('|")((?:\\?.)*?)\1/;
@ -26,8 +28,15 @@ export default class UtilParser extends Tokenizer {
} }
isLookaheadRelational(op: "<" | ">"): boolean { isLookaheadRelational(op: "<" | ">"): boolean {
const l = this.lookahead(); const next = this.nextTokenStart();
return l.type === tt.relational && l.value === op; if (this.input.charAt(next) === op) {
if (next + 1 === this.input.length) {
return true;
}
const afterNext = this.input.charCodeAt(next + 1);
return afterNext !== op.charCodeAt(0) && afterNext !== charCodes.equalsTo;
}
return false;
} }
// TODO // TODO
@ -60,9 +69,18 @@ export default class UtilParser extends Tokenizer {
); );
} }
isUnparsedContextual(nameStart: number, name: string): boolean {
const nameEnd = nameStart + name.length;
return (
this.input.slice(nameStart, nameEnd) === name &&
(nameEnd === this.input.length ||
!isIdentifierChar(this.input.charCodeAt(nameEnd)))
);
}
isLookaheadContextual(name: string): boolean { isLookaheadContextual(name: string): boolean {
const l = this.lookahead(); const next = this.nextTokenStart();
return l.type === tt.name && l.value === name; return this.isUnparsedContextual(next, name);
} }
// Consumes contextual keyword if possible. // Consumes contextual keyword if possible.

View File

@ -19,6 +19,7 @@ import {
BIND_CLASS, BIND_CLASS,
} from "../../util/scopeflags"; } from "../../util/scopeflags";
import TypeScriptScopeHandler from "./scope"; import TypeScriptScopeHandler from "./scope";
import * as charCodes from "charcodes";
type TsModifier = type TsModifier =
| "readonly" | "readonly"
@ -657,7 +658,10 @@ export default (superClass: Class<Parser>): Class<Parser> =>
: this.match(tt._null) : this.match(tt._null)
? "TSNullKeyword" ? "TSNullKeyword"
: keywordTypeFromName(this.state.value); : keywordTypeFromName(this.state.value);
if (type !== undefined && this.lookahead().type !== tt.dot) { if (
type !== undefined &&
this.lookaheadCharCode() !== charCodes.dot
) {
const node: N.TsKeywordType = this.startNode(); const node: N.TsKeywordType = this.startNode();
this.next(); this.next();
return this.finishNode(node, type); return this.finishNode(node, type);
@ -1203,7 +1207,8 @@ export default (superClass: Class<Parser>): Class<Parser> =>
tsIsExternalModuleReference(): boolean { tsIsExternalModuleReference(): boolean {
return ( return (
this.isContextual("require") && this.lookahead().type === tt.parenL this.isContextual("require") &&
this.lookaheadCharCode() === charCodes.leftParenthesis
); );
} }

View File

@ -13,6 +13,7 @@ import {
lineBreakG, lineBreakG,
isNewLine, isNewLine,
isWhitespace, isWhitespace,
skipWhiteSpace,
} from "../util/whitespace"; } from "../util/whitespace";
import State from "./state"; import State from "./state";
@ -168,6 +169,18 @@ export default class Tokenizer extends LocationParser {
return curr; return curr;
} }
nextTokenStart(): number {
const thisTokEnd = this.state.pos;
skipWhiteSpace.lastIndex = thisTokEnd;
const skip = skipWhiteSpace.exec(this.input);
// $FlowIgnore: The skipWhiteSpace ensures to match any string
return thisTokEnd + skip[0].length;
}
lookaheadCharCode(): number {
return this.input.charCodeAt(this.nextTokenStart());
}
// Toggle strict mode. Re-reads the next number or string to please // Toggle strict mode. Re-reads the next number or string to please
// pedantic tests (`"use strict"; 010;` should fail). // pedantic tests (`"use strict"; 010;` should fail).
@ -267,13 +280,7 @@ export default class Tokenizer extends LocationParser {
const startLoc = this.state.curPosition(); const startLoc = this.state.curPosition();
let ch = this.input.charCodeAt((this.state.pos += startSkip)); let ch = this.input.charCodeAt((this.state.pos += startSkip));
if (this.state.pos < this.length) { if (this.state.pos < this.length) {
while ( while (!isNewLine(ch) && ++this.state.pos < this.length) {
ch !== charCodes.lineFeed &&
ch !== charCodes.carriageReturn &&
ch !== charCodes.lineSeparator &&
ch !== charCodes.paragraphSeparator &&
++this.state.pos < this.length
) {
ch = this.input.charCodeAt(this.state.pos); ch = this.input.charCodeAt(this.state.pos);
} }
} }
@ -441,13 +448,7 @@ export default class Tokenizer extends LocationParser {
let ch = this.input.charCodeAt(this.state.pos); let ch = this.input.charCodeAt(this.state.pos);
if (ch !== charCodes.exclamationMark) return false; if (ch !== charCodes.exclamationMark) return false;
while ( while (!isNewLine(ch) && ++this.state.pos < this.length) {
ch !== charCodes.lineFeed &&
ch !== charCodes.carriageReturn &&
ch !== charCodes.lineSeparator &&
ch !== charCodes.paragraphSeparator &&
++this.state.pos < this.length
) {
ch = this.input.charCodeAt(this.state.pos); ch = this.input.charCodeAt(this.state.pos);
} }