Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(biome_js_parser/state): use enumflags2 for SignatureFlag and ParsingContextFlag #3800

Merged
merged 1 commit into from
Sep 5, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 121 additions & 46 deletions crates/biome_js_parser/src/state.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use crate::prelude::*;
use biome_js_syntax::JsFileSource;
use biome_rowan::{TextRange, TextSize};
use bitflags::bitflags;
use enumflags2::{bitflags, make_bitflags, BitFlags};
use indexmap::IndexMap;
use rustc_hash::FxHashSet;
use std::ops::{Deref, DerefMut, Range};
use std::ops::{BitOr, BitOrAssign, Deref, DerefMut, Range, Sub};

type LabelSet = IndexMap<String, LabelledItem>;

Expand Down Expand Up @@ -359,17 +359,32 @@ impl ChangeParserState for EnableStrictMode {
}
}

bitflags! {
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[bitflags]
#[repr(u8)]
enum SignatureFlag {
Async = 1 << 0,
Generator = 1 << 1,
Constructor = 1 << 2,
}

#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
pub(crate) struct SignatureFlags(BitFlags<SignatureFlag>);

impl SignatureFlags {
/// Is the function in an async context
pub const ASYNC: Self = Self(make_bitflags!(SignatureFlag::{Async}));
/// Is the function in a generator context
pub const GENERATOR: Self = Self(make_bitflags!(SignatureFlag::{Generator}));
/// Is the function a constructor (or constructor context)
pub const CONSTRUCTOR: Self = Self(make_bitflags!(SignatureFlag::{Constructor}));

/// Flags describing the context of a function.
pub(crate) struct SignatureFlags: u8 {
/// Is the function in an async context
const ASYNC = 1 << 0;
/// Is the function in a generator context
const GENERATOR = 1 << 1;
/// Is the function a constructor (or constructor context)
const CONSTRUCTOR = 1 << 2;
pub const fn empty() -> Self {
Self(BitFlags::EMPTY)
}

pub fn contains(&self, other: impl Into<SignatureFlags>) -> bool {
self.0.contains(other.into().0)
}
}

Expand All @@ -393,48 +408,108 @@ impl From<SignatureFlags> for ParsingContextFlags {
}
}

bitflags! {
/// Flags representing the parsing state.
/// The reasons to use flags instead of individual boolean fields on `ParserState` are:
/// * It's possible to use bit masks to define what state should be inherited. For example,
/// functions inherit whether they're defined inside a parameter but override the `in_async` flag
/// * It's easier to snapshot the previous state. Individual boolean fields would require that a change
/// snapshots each individual boolean field to allow restoring the previous state. With bitflags, all that
/// is needed is to copy away the flags field and restore it after.
#[derive(Debug, Copy, Default, Clone, Eq, PartialEq)]
pub(crate) struct ParsingContextFlags: u8 {
/// Whether the parser is in a generator function like `function* a() {}`
/// Matches the `Yield` parameter in the ECMA spec
const IN_GENERATOR = 1 << 0;
/// Whether the parser is inside a function
const IN_FUNCTION = 1 << 1;
/// Whatever the parser is inside a constructor
const IN_CONSTRUCTOR = 1 << 2;
impl BitOr for SignatureFlags {
type Output = Self;

fn bitor(self, rhs: Self) -> Self::Output {
SignatureFlags(self.0 | rhs.0)
}
}

impl BitOrAssign for SignatureFlags {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[bitflags]
#[repr(u8)]
enum ParsingContextFlag {
InGenerator = 1 << 0,
InFunction = 1 << 1,
InConstructor = 1 << 2,
InAsync = 1 << 3,
TopLevel = 1 << 4,
BreakAllowed = 1 << 5,
ContinueAllowed = 1 << 6,
AmbientContext = 1 << 7,
}

/// Flags representing the parsing state.
/// The reasons to use flags instead of individual boolean fields on `ParserState` are:
/// * It's possible to use bit masks to define what state should be inherited. For example,
/// functions inherit whether they're defined inside a parameter but override the `in_async` flag
/// * It's easier to snapshot the previous state. Individual boolean fields would require that a change
/// snapshots each individual boolean field to allow restoring the previous state. With bitflags, all that
/// is needed is to copy away the flags field and restore it after.
#[derive(Debug, Copy, Default, Clone, Eq, PartialEq)]
pub(crate) struct ParsingContextFlags(BitFlags<ParsingContextFlag>);

impl ParsingContextFlags {
/// Whether the parser is in a generator function like `function* a() {}`
/// Matches the `Yield` parameter in the ECMA spec
const IN_GENERATOR: Self = Self(make_bitflags!(ParsingContextFlag::{InGenerator}));
/// Whether the parser is inside a function
const IN_FUNCTION: Self = Self(make_bitflags!(ParsingContextFlag::{InFunction}));
/// Whatever the parser is inside a constructor
const IN_CONSTRUCTOR: Self = Self(make_bitflags!(ParsingContextFlag::{InConstructor}));

/// Is async allowed in this context. Either because it's an async function or top level await is supported.
/// Equivalent to the `Async` generator in the ECMA spec
const IN_ASYNC: Self = Self(make_bitflags!(ParsingContextFlag::{InAsync}));

/// Whether the parser is parsing a top-level statement (not inside a class, function, parameter) or not
const TOP_LEVEL: Self = Self(make_bitflags!(ParsingContextFlag::{TopLevel}));

/// Is async allowed in this context. Either because it's an async function or top level await is supported.
/// Equivalent to the `Async` generator in the ECMA spec
const IN_ASYNC = 1 << 3;
/// Whether the parser is in an iteration or switch statement and
/// `break` is allowed.
const BREAK_ALLOWED: Self = Self(make_bitflags!(ParsingContextFlag::{BreakAllowed}));

/// Whether the parser is parsing a top-level statement (not inside a class, function, parameter) or not
const TOP_LEVEL = 1 << 4;
/// Whether the parser is in an iteration statement and `continue` is allowed.
const CONTINUE_ALLOWED: Self = Self(make_bitflags!(ParsingContextFlag::{ContinueAllowed}));

/// Whether the parser is in an iteration or switch statement and
/// `break` is allowed.
const BREAK_ALLOWED = 1 << 5;
/// Whether the parser is in a TypeScript ambient context
const AMBIENT_CONTEXT: Self = Self(make_bitflags!(ParsingContextFlag::{AmbientContext}));

/// Whether the parser is in an iteration statement and `continue` is allowed.
const CONTINUE_ALLOWED = 1 << 6;
/// Bitmask of all the flags that must be reset (shouldn't be inherited) when the parser enters a function
const FUNCTION_RESET_MASK: Self = Self(
make_bitflags!(ParsingContextFlag::{BreakAllowed | ContinueAllowed | InConstructor | InAsync | InGenerator | TopLevel }),
);

/// Whatever the parser is in a TypeScript ambient context
const AMBIENT_CONTEXT = 1 << 7;
/// Bitmask of all the flags that must be reset (shouldn't be inherited) when entering parameters.
const PARAMETER_RESET_MASK: Self = Self(
make_bitflags!(ParsingContextFlag::{InConstructor | InFunction | TopLevel | InGenerator | InAsync }),
);

const LOOP = Self::BREAK_ALLOWED.bits() | Self::CONTINUE_ALLOWED.bits();
pub const fn empty() -> Self {
Self(BitFlags::EMPTY)
}

pub fn contains(&self, other: impl Into<ParsingContextFlags>) -> bool {
self.0.contains(other.into().0)
}
}

impl BitOr for ParsingContextFlags {
type Output = Self;

fn bitor(self, rhs: Self) -> Self::Output {
ParsingContextFlags(self.0 | rhs.0)
}
}

impl BitOrAssign for ParsingContextFlags {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}

/// Bitmask of all the flags that must be reset (shouldn't be inherited) when the parser enters a function
const FUNCTION_RESET_MASK = Self::BREAK_ALLOWED.bits() | Self::CONTINUE_ALLOWED.bits() | Self::IN_CONSTRUCTOR.bits() | Self::IN_ASYNC.bits() | Self::IN_GENERATOR.bits() | Self::TOP_LEVEL.bits();
impl Sub for ParsingContextFlags {
type Output = Self;

/// Bitmask of all the flags that must be reset (shouldn't be inherited) when entering parameters.
const PARAMETER_RESET_MASK = Self::IN_CONSTRUCTOR.bits() | Self::IN_FUNCTION.bits() | Self::TOP_LEVEL.bits() | Self::IN_GENERATOR.bits() | Self::IN_ASYNC.bits();
fn sub(self, rhs: Self) -> Self::Output {
Self(self.0 & !rhs.0)
}
}

Expand Down
Loading