Skip to content

Commit a2d3596

Browse files
authored
perf(es/parser): Use bitflags to reduce parser context size (#10367)
`Context` size: 30 bytes -> 4 bytes
1 parent d3fb992 commit a2d3596

20 files changed

+363
-568
lines changed

Diff for: Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: crates/swc_ecma_parser/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ typescript = []
2525
verify = ["swc_ecma_visit"]
2626

2727
[dependencies]
28+
bitflags = { workspace = true }
2829
either = { workspace = true }
2930
num-bigint = { workspace = true }
3031
num-traits = { workspace = true }

Diff for: crates/swc_ecma_parser/src/lexer/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -663,7 +663,9 @@ impl Lexer<'_> {
663663
let c = self.cur().unwrap();
664664
self.bump();
665665

666-
if self.syntax.typescript() && self.ctx.in_type && !self.ctx.should_not_lex_lt_or_gt_as_type
666+
if self.syntax.typescript()
667+
&& self.ctx.contains(Context::InType)
668+
&& !self.ctx.contains(Context::ShouldNotLexLtOrGtAsType)
667669
{
668670
if c == '<' {
669671
return Ok(Some(tok!('<')));

Diff for: crates/swc_ecma_parser/src/lexer/number.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -782,7 +782,7 @@ mod tests {
782782
let vec = panic::catch_unwind(|| {
783783
crate::with_test_sess(case, |_, input| {
784784
let mut l = Lexer::new(Syntax::default(), Default::default(), input, None);
785-
l.ctx.strict = strict;
785+
l.ctx.set(Context::Strict, strict);
786786
Ok(l.map(|ts| ts.token).collect::<Vec<_>>())
787787
})
788788
.unwrap()

Diff for: crates/swc_ecma_parser/src/lexer/state.rs

+8-5
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ impl From<TokenKind> for TokenType {
124124
impl Tokens for Lexer<'_> {
125125
#[inline]
126126
fn set_ctx(&mut self, ctx: Context) {
127-
if ctx.module && !self.module_errors.borrow().is_empty() {
127+
if ctx.contains(Context::Module) && !self.module_errors.borrow().is_empty() {
128128
let mut module_errors = self.module_errors.borrow_mut();
129129
self.errors.borrow_mut().append(&mut *module_errors);
130130
}
@@ -181,7 +181,7 @@ impl Tokens for Lexer<'_> {
181181
}
182182

183183
fn add_module_mode_error(&self, error: Error) {
184-
if self.ctx.module {
184+
if self.ctx.contains(Context::Module) {
185185
self.add_error(error);
186186
return;
187187
}
@@ -284,7 +284,10 @@ impl Lexer<'_> {
284284

285285
self.state.start = *start;
286286

287-
if self.syntax.jsx() && !self.ctx.in_property_name && !self.ctx.in_type {
287+
if self.syntax.jsx()
288+
&& !self.ctx.contains(Context::InPropertyName)
289+
&& !self.ctx.contains(Context::InType)
290+
{
288291
//jsx
289292
if self.state.context.current() == Some(TokenContext::JSXExpr) {
290293
return self.read_jsx_token();
@@ -852,8 +855,8 @@ pub(crate) fn lex(syntax: Syntax, s: &'static str) -> Vec<TokenAndSpan> {
852855
#[cfg(test)]
853856
pub(crate) fn lex_module_errors(syntax: Syntax, s: &'static str) -> Vec<Error> {
854857
with_lexer(syntax, Default::default(), s, |l| {
855-
l.ctx.strict = true;
856-
l.ctx.module = true;
858+
l.ctx.insert(Context::Module);
859+
l.ctx.insert(Context::Strict);
857860

858861
let _: Vec<_> = l.collect();
859862

Diff for: crates/swc_ecma_parser/src/lexer/util.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use super::{comments_buffer::BufferedComment, whitespace::SkipWhitespace, Char,
1616
use crate::{
1717
error::{Error, SyntaxError},
1818
lexer::comments_buffer::BufferedCommentKind,
19-
Tokens,
19+
Context, Tokens,
2020
};
2121

2222
impl Lexer<'_> {
@@ -104,7 +104,7 @@ impl Lexer<'_> {
104104
#[cold]
105105
#[inline(never)]
106106
pub(super) fn emit_error_span(&mut self, span: Span, kind: SyntaxError) {
107-
if self.ctx.ignore_error {
107+
if self.ctx.contains(Context::IgnoreError) {
108108
return;
109109
}
110110

@@ -123,7 +123,7 @@ impl Lexer<'_> {
123123
#[cold]
124124
#[inline(never)]
125125
pub(super) fn emit_strict_mode_error_span(&mut self, span: Span, kind: SyntaxError) {
126-
if self.ctx.strict {
126+
if self.ctx.contains(Context::Strict) {
127127
self.emit_error_span(span, kind);
128128
return;
129129
}

Diff for: crates/swc_ecma_parser/src/lib.rs

+50-53
Original file line numberDiff line numberDiff line change
@@ -369,79 +369,76 @@ pub struct EsSyntax {
369369
pub explicit_resource_management: bool,
370370
}
371371

372-
/// Syntactic context.
373-
#[derive(Debug, Clone, Copy, Default)]
374-
pub struct Context {
375-
/// `true` while backtracking
376-
ignore_error: bool,
372+
use bitflags::bitflags;
377373

378-
/// Is in module code?
379-
module: bool,
380-
can_be_module: bool,
381-
strict: bool,
374+
bitflags! {
375+
#[derive(Debug, Clone, Copy, Default)]
376+
pub struct Context: u32 {
382377

383-
expr_ctx: ExpressionContext,
378+
/// `true` while backtracking
379+
const IgnoreError = 1 << 0;
384380

385-
include_in_expr: bool,
386-
/// If true, await expression is parsed, and "await" is treated as a
387-
/// keyword.
388-
in_async: bool,
389-
/// If true, yield expression is parsed, and "yield" is treated as a
390-
/// keyword.
391-
in_generator: bool,
381+
/// Is in module code?
382+
const Module = 1 << 1;
383+
const CanBeModule = 1 << 2;
384+
const Strict = 1 << 3;
392385

393-
/// If true, await is treated as a keyword.
394-
in_static_block: bool,
386+
const ForLoopInit = 1 << 4;
387+
const ForAwaitLoopInit = 1 << 5;
395388

396-
is_continue_allowed: bool,
397-
is_break_allowed: bool,
389+
const IncludeInExpr = 1 << 6;
390+
/// If true, await expression is parsed, and "await" is treated as a
391+
/// keyword.
392+
const InAsync = 1 << 7;
393+
/// If true, yield expression is parsed, and "yield" is treated as a
394+
/// keyword.
395+
const InGenerator = 1 << 8;
398396

399-
in_type: bool,
400-
/// Typescript extension.
401-
should_not_lex_lt_or_gt_as_type: bool,
402-
/// Typescript extension.
403-
in_declare: bool,
397+
/// If true, await is treated as a keyword.
398+
const InStaticBlock = 1 << 9;
404399

405-
/// If true, `:` should not be treated as a type annotation.
406-
in_cond_expr: bool,
407-
will_expect_colon_for_cond: bool,
400+
const IsContinueAllowed = 1 << 10;
401+
const IsBreakAllowed = 1 << 11;
408402

409-
in_class: bool,
403+
const InType = 1 << 12;
404+
/// Typescript extension.
405+
const ShouldNotLexLtOrGtAsType = 1 << 13;
406+
/// Typescript extension.
407+
const InDeclare = 1 << 14;
410408

411-
in_class_field: bool,
409+
/// If true, `:` should not be treated as a type annotation.
410+
const InCondExpr = 1 << 15;
411+
const WillExpectColonForCond = 1 << 16;
412412

413-
in_function: bool,
413+
const InClass = 1 << 17;
414414

415-
/// This indicates current scope or the scope out of arrow function is
416-
/// function declaration or function expression or not.
417-
inside_non_arrow_function_scope: bool,
415+
const InClassField = 1 << 18;
418416

419-
in_parameters: bool,
417+
const InFunction = 1 << 19;
420418

421-
has_super_class: bool,
419+
/// This indicates current scope or the scope out of arrow function is
420+
/// function declaration or function expression or not.
421+
const InsideNonArrowFunctionScope = 1 << 20;
422422

423-
in_property_name: bool,
423+
const InParameters = 1 << 21;
424424

425-
in_forced_jsx_context: bool,
425+
const HasSuperClass = 1 << 22;
426426

427-
// If true, allow super.x and super[x]
428-
allow_direct_super: bool,
427+
const InPropertyName = 1 << 23;
429428

430-
ignore_else_clause: bool,
429+
const InForcedJsxContext = 1 << 24;
431430

432-
disallow_conditional_types: bool,
431+
// If true, allow super.x and super[x]
432+
const AllowDirectSuper = 1 << 25;
433433

434-
allow_using_decl: bool,
434+
const IgnoreElseClause = 1 << 26;
435435

436-
top_level: bool,
437-
}
436+
const DisallowConditionalTypes = 1 << 27;
437+
438+
const AllowUsingDecl = 1 << 28;
438439

439-
#[derive(Debug, Clone, Copy, Default)]
440-
struct ExpressionContext {
441-
// TODO:
442-
// - include_in
443-
for_loop_init: bool,
444-
for_await_loop_init: bool,
440+
const TopLevel = 1 << 29;
441+
}
445442
}
446443

447444
#[cfg(test)]
@@ -499,7 +496,7 @@ macro_rules! expose {
499496

500497
expose!(parse_file_as_expr, Box<Expr>, |p| {
501498
// This allow to parse `import.meta`
502-
p.input().ctx.can_be_module = true;
499+
p.input().ctx.insert(Context::CanBeModule);
503500
p.parse_expr()
504501
});
505502
expose!(parse_file_as_module, Module, |p| { p.parse_module() });

0 commit comments

Comments
 (0)