From aa06ce478051d2880054bd151a20683145ba8d6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Thu, 10 Jun 2021 11:39:01 -0400 Subject: [PATCH 1/3] refactor: unify tc.brace and tc.templateQuasi --- .../babel-parser/src/plugins/jsx/index.js | 10 +------ .../babel-parser/src/tokenizer/context.js | 29 ++++++++++--------- packages/babel-parser/src/tokenizer/state.js | 6 ++-- 3 files changed, 19 insertions(+), 26 deletions(-) diff --git a/packages/babel-parser/src/plugins/jsx/index.js b/packages/babel-parser/src/plugins/jsx/index.js index ca06064682dc..1dc9ec15616b 100644 --- a/packages/babel-parser/src/plugins/jsx/index.js +++ b/packages/babel-parser/src/plugins/jsx/index.js @@ -616,15 +616,7 @@ export default (superClass: Class): Class => updateContext(prevType: TokenType): void { super.updateContext(prevType); const { context, type } = this.state; - if (type === tt.braceL) { - const curContext = context[context.length - 1]; - if (curContext === tc.j_oTag) { - context.push(tc.brace); - } else if (curContext === tc.j_expr) { - context.push(tc.templateQuasi); - } - this.state.exprAllowed = true; - } else if (type === tt.slash && prevType === tt.jsxTagStart) { + if (type === tt.slash && prevType === tt.jsxTagStart) { context.length -= 2; // do not consider JSX expr -> JSX open tag -> ... anymore context.push(tc.j_cTag); // reconsider as closing tag context this.state.exprAllowed = false; diff --git a/packages/babel-parser/src/tokenizer/context.js b/packages/babel-parser/src/tokenizer/context.js index 4543b2e19aa3..47aee4df879b 100644 --- a/packages/babel-parser/src/tokenizer/context.js +++ b/packages/babel-parser/src/tokenizer/context.js @@ -1,8 +1,7 @@ // @flow -// The token context is used to track whether `}` matches -// a template quasi `${` or other tokens containing `{`: -// namely tt.braceL `{` and tt.braceHashL `#{` +// The token context is used to track whether the apostrophe "`" +// starts or ends a string template import { types as tt } from "./types"; @@ -20,7 +19,6 @@ export const types: { [key: string]: TokContext, } = { brace: new TokContext("{"), - templateQuasi: new TokContext("${"), template: new TokContext("`", true), }; @@ -35,19 +33,22 @@ export const types: { // `this.prodParam` still has `[Yield]` production because it is not yet updated tt.braceR.updateContext = context => { - if (context.length > 1) { - context.pop(); - } + context.pop(); }; // we don't need to update context for tt.braceBarL because we do not pop context for tt.braceBarR -tt.braceL.updateContext = tt.braceHashL.updateContext = context => { - context.push(types.brace); -}; - -tt.dollarBraceL.updateContext = context => { - context.push(types.templateQuasi); -}; +// ideally only dollarBraceL "${" needs a non-template context +// in order to indicate that the last "`" in `${`" starts a new string template +// inside a template element within outer string template. +// but when we popped such context in `}`, we lost track of whether this +// `}` matches a `${` or other tokens matching `}`, so we have to push +// such context in every token that `}` will match. +tt.braceL.updateContext = + tt.braceHashL.updateContext = + tt.dollarBraceL.updateContext = + context => { + context.push(types.brace); + }; tt.backQuote.updateContext = context => { if (context[context.length - 1] === types.template) { diff --git a/packages/babel-parser/src/tokenizer/state.js b/packages/babel-parser/src/tokenizer/state.js index a8c8f80e4a65..c6775ede56eb 100644 --- a/packages/babel-parser/src/tokenizer/state.js +++ b/packages/babel-parser/src/tokenizer/state.js @@ -127,10 +127,10 @@ export default class State { lastTokStart: number = 0; lastTokEnd: number = 0; - // The context stack is used to superficially track syntactic - // context to predict whether a regular expression is allowed in a - // given position. + // The context stack is used to track whether the apostrophe "`" starts + // or ends a string template context: Array = [ct.brace]; + // Used to track whether a JSX element is allowed to form exprAllowed: boolean = true; // Used to signal to callers of `readWord1` whether the word From d9783d3a2278cad597ce673bfb53906a0fbab2f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Thu, 10 Jun 2021 11:40:56 -0400 Subject: [PATCH 2/3] refactor: remove unused context check --- packages/babel-parser/src/parser/expression.js | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index de731275ec8f..9531a7a79e1b 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -19,7 +19,6 @@ // [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser import { types as tt, type TokenType } from "../tokenizer/types"; -import { types as ct } from "../tokenizer/context"; import * as N from "../types"; import LValParser from "./lval"; import { @@ -2338,19 +2337,6 @@ export default class ExpressionParser extends LValParser { name = this.state.value; } else if (type.keyword) { name = type.keyword; - - // `class` and `function` keywords push function-type token context into this.context. - // But there is no chance to pop the context if the keyword is consumed - // as an identifier such as a property name. - if (type === tt._class || type === tt._function) { - const curContext = this.curContext(); - if ( - curContext === ct.functionStatement || - curContext === ct.functionExpression - ) { - this.state.context.pop(); - } - } } else { throw this.unexpected(); } From 112e1f831a5fe5fa1f52846454ea8563888f0268 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Thu, 10 Jun 2021 13:09:19 -0400 Subject: [PATCH 3/3] perf: reduce arrayPrototype call and hoist variables --- packages/babel-parser/src/plugins/flow/index.js | 5 +++-- packages/babel-parser/src/plugins/jsx/index.js | 11 +++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/babel-parser/src/plugins/flow/index.js b/packages/babel-parser/src/plugins/flow/index.js index d4aab948b0d1..d7c6f829836b 100644 --- a/packages/babel-parser/src/plugins/flow/index.js +++ b/packages/babel-parser/src/plugins/flow/index.js @@ -2849,9 +2849,10 @@ export default (superClass: Class): Class => // by parsing `jsxTagStart` to stop the JSX plugin from // messing with the tokens const { context } = this.state; - if (context[context.length - 1] === tc.j_oTag) { + const curContext = context[context.length - 1]; + if (curContext === tc.j_oTag) { context.length -= 2; - } else if (context[context.length - 1] === tc.j_expr) { + } else if (curContext === tc.j_expr) { context.length -= 1; } } diff --git a/packages/babel-parser/src/plugins/jsx/index.js b/packages/babel-parser/src/plugins/jsx/index.js index 1dc9ec15616b..df3dbcbd0663 100644 --- a/packages/babel-parser/src/plugins/jsx/index.js +++ b/packages/babel-parser/src/plugins/jsx/index.js @@ -55,8 +55,10 @@ tt.jsxTagStart = new TokenType("jsxTagStart", { startsExpr: true }); tt.jsxTagEnd = new TokenType("jsxTagEnd"); tt.jsxTagStart.updateContext = context => { - context.push(tc.j_expr); // treat as beginning of JSX expression - context.push(tc.j_oTag); // start opening tag context + context.push( + tc.j_expr, // treat as beginning of JSX expression + tc.j_oTag, // start opening tag context + ); }; function isFragment(object: ?N.JSXElement): boolean { @@ -617,8 +619,9 @@ export default (superClass: Class): Class => super.updateContext(prevType); const { context, type } = this.state; if (type === tt.slash && prevType === tt.jsxTagStart) { - context.length -= 2; // do not consider JSX expr -> JSX open tag -> ... anymore - context.push(tc.j_cTag); // reconsider as closing tag context + // do not consider JSX expr -> JSX open tag -> ... anymore + // reconsider as closing tag context + context.splice(-2, 2, tc.j_cTag); this.state.exprAllowed = false; } else if (type === tt.jsxTagEnd) { const out = context.pop();