use crate::prelude::*;
use biome_js_syntax::JsFileSource;
use biome_rowan::{TextRange, TextSize};
use bitflags::bitflags;
use indexmap::IndexMap;
use rustc_hash::FxHashSet;
use std::ops::{Deref, DerefMut, Range};
type LabelSet = IndexMap<String, LabelledItem>;
#[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) enum LabelledItem {
Iteration(TextRange),
Other(TextRange),
}
impl LabelledItem {
pub(crate) fn range(&self) -> &TextRange {
match self {
LabelledItem::Iteration(range) | LabelledItem::Other(range) => range,
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub(crate) enum ExportDefaultItemKind {
Expression,
FunctionOverload,
FunctionDeclaration,
Interface,
ClassDeclaration,
Declaration,
}
impl ExportDefaultItemKind {
pub(crate) fn is_interface(&self) -> bool {
matches!(self, ExportDefaultItemKind::Interface)
}
pub const fn is_mergeable(&self, other: &ExportDefaultItemKind) -> bool {
Self::can_merge(self, other) || Self::can_merge(other, self)
}
const fn can_merge(a: &ExportDefaultItemKind, b: &ExportDefaultItemKind) -> bool {
match (a, b) {
(
ExportDefaultItemKind::FunctionOverload,
ExportDefaultItemKind::FunctionDeclaration
| ExportDefaultItemKind::FunctionOverload,
) => true,
(
ExportDefaultItemKind::Interface,
ExportDefaultItemKind::ClassDeclaration
| ExportDefaultItemKind::FunctionDeclaration
| ExportDefaultItemKind::Interface,
) => true,
(_, _) => false,
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) struct ExportDefaultItem {
pub kind: ExportDefaultItemKind,
pub range: Range<usize>,
}
#[derive(Debug)]
pub(crate) struct JsParserState {
parsing_context: ParsingContextFlags,
label_set: LabelSet,
strict: Option<StrictMode>,
pub default_item: Option<ExportDefaultItem>,
pub duplicate_binding_parent: Option<&'static str>,
pub name_map: IndexMap<String, TextRange>,
pub(crate) speculative_parsing: bool,
pub(crate) not_parenthesized_arrow: FxHashSet<TextSize>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum StrictMode {
Module,
Explicit(TextRange),
Class(TextRange),
}
impl JsParserState {
pub fn new(source_type: &JsFileSource) -> Self {
let mut state = JsParserState {
parsing_context: ParsingContextFlags::TOP_LEVEL,
label_set: IndexMap::new(),
strict: source_type
.module_kind()
.is_module()
.then_some(StrictMode::Module),
default_item: None,
name_map: IndexMap::new(),
duplicate_binding_parent: None,
not_parenthesized_arrow: Default::default(),
speculative_parsing: false,
};
if source_type.module_kind().is_module() {
state.parsing_context |= ParsingContextFlags::IN_ASYNC
}
if source_type.language().is_definition_file() {
EnterAmbientContext.apply(&mut state);
}
state
}
pub fn in_function(&self) -> bool {
self.parsing_context
.contains(ParsingContextFlags::IN_FUNCTION)
}
pub fn in_generator(&self) -> bool {
self.parsing_context
.contains(ParsingContextFlags::IN_GENERATOR)
}
pub fn in_async(&self) -> bool {
self.parsing_context.contains(ParsingContextFlags::IN_ASYNC)
}
pub fn in_ambient_context(&self) -> bool {
self.parsing_context
.contains(ParsingContextFlags::AMBIENT_CONTEXT)
}
pub fn in_constructor(&self) -> bool {
self.parsing_context
.contains(ParsingContextFlags::IN_CONSTRUCTOR)
}
pub fn is_top_level(&self) -> bool {
self.parsing_context
.contains(ParsingContextFlags::TOP_LEVEL)
}
pub fn continue_allowed(&self) -> bool {
self.parsing_context
.contains(ParsingContextFlags::CONTINUE_ALLOWED)
}
pub fn break_allowed(&self) -> bool {
self.parsing_context
.contains(ParsingContextFlags::BREAK_ALLOWED)
}
pub fn strict(&self) -> Option<&StrictMode> {
self.strict.as_ref()
}
pub fn get_labelled_item(&self, label: &str) -> Option<&LabelledItem> {
self.label_set.get(label)
}
pub(super) fn checkpoint(&self) -> JsParserStateCheckpoint {
JsParserStateCheckpoint::snapshot(self)
}
pub(super) fn restore(&mut self, checkpoint: JsParserStateCheckpoint) {
checkpoint.rewind(self);
}
}
#[derive(Debug)]
pub(super) struct JsParserStateCheckpoint {
#[cfg(debug_assertions)]
debug_checkpoint: JsDebugParserStateCheckpoint,
}
impl JsParserStateCheckpoint {
#[cfg(debug_assertions)]
fn snapshot(state: &JsParserState) -> Self {
Self {
debug_checkpoint: JsDebugParserStateCheckpoint::snapshot(state),
}
}
#[cfg(not(debug_assertions))]
fn snapshot(_: &JsParserState) -> Self {
Self {}
}
#[cfg(debug_assertions)]
fn rewind(self, state: &mut JsParserState) {
self.debug_checkpoint.rewind(state);
}
#[cfg(not(debug_assertions))]
fn rewind(self, _: &JsParserState) {}
}
#[derive(Debug, Clone)]
#[cfg(debug_assertions)]
pub(super) struct JsDebugParserStateCheckpoint {
parsing_context: ParsingContextFlags,
label_set_len: usize,
strict: Option<StrictMode>,
default_item: Option<ExportDefaultItem>,
duplicate_binding_parent: Option<&'static str>,
name_map_len: usize,
}
#[cfg(debug_assertions)]
impl JsDebugParserStateCheckpoint {
fn snapshot(state: &JsParserState) -> Self {
Self {
parsing_context: state.parsing_context,
label_set_len: state.label_set.len(),
strict: state.strict.clone(),
default_item: state.default_item.clone(),
duplicate_binding_parent: state.duplicate_binding_parent,
name_map_len: state.name_map.len(),
}
}
fn rewind(self, state: &mut JsParserState) {
assert_eq!(state.parsing_context, self.parsing_context);
assert_eq!(state.label_set.len(), self.label_set_len);
assert_eq!(state.strict, self.strict);
assert_eq!(state.default_item, self.default_item);
assert_eq!(
state.duplicate_binding_parent,
self.duplicate_binding_parent
);
assert_eq!(state.name_map.len(), self.name_map_len);
}
}
pub(crate) struct ParserStateGuard<'parser, 't, C>
where
C: ChangeParserState,
{
snapshot: C::Snapshot,
inner: &'parser mut JsParser<'t>,
}
impl<'parser, 't, C: ChangeParserState> ParserStateGuard<'parser, 't, C> {
pub(super) fn new(parser: &'parser mut JsParser<'t>, snapshot: C::Snapshot) -> Self {
Self {
snapshot,
inner: parser,
}
}
}
impl<'parser, 't, C: ChangeParserState> Drop for ParserStateGuard<'parser, 't, C> {
fn drop(&mut self) {
let snapshot = std::mem::take(&mut self.snapshot);
C::restore(self.inner.state_mut(), snapshot);
}
}
impl<'parser, 't, C: ChangeParserState> Deref for ParserStateGuard<'parser, 't, C> {
type Target = JsParser<'t>;
fn deref(&self) -> &Self::Target {
self.inner
}
}
impl<'parser, 't, C: ChangeParserState> DerefMut for ParserStateGuard<'parser, 't, C> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.inner
}
}
pub(crate) trait ChangeParserState {
type Snapshot: Default;
fn apply(self, state: &mut JsParserState) -> Self::Snapshot;
fn restore(state: &mut JsParserState, value: Self::Snapshot);
}
#[derive(Default, Debug)]
pub struct EnableStrictModeSnapshot(Option<StrictMode>);
pub(crate) struct EnableStrictMode(pub StrictMode);
impl ChangeParserState for EnableStrictMode {
type Snapshot = EnableStrictModeSnapshot;
#[inline]
fn apply(self, state: &mut JsParserState) -> Self::Snapshot {
EnableStrictModeSnapshot(std::mem::replace(&mut state.strict, Some(self.0)))
}
#[inline]
fn restore(state: &mut JsParserState, value: Self::Snapshot) {
state.strict = value.0
}
}
bitflags! {
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
pub(crate) struct SignatureFlags: u8 {
const ASYNC = 1 << 0;
const GENERATOR = 1 << 1;
const CONSTRUCTOR = 1 << 2;
}
}
impl From<SignatureFlags> for ParsingContextFlags {
fn from(flags: SignatureFlags) -> Self {
let mut parsing_context = ParsingContextFlags::empty();
if flags.contains(SignatureFlags::ASYNC) {
parsing_context |= ParsingContextFlags::IN_ASYNC;
}
if flags.contains(SignatureFlags::GENERATOR) {
parsing_context |= ParsingContextFlags::IN_GENERATOR;
}
if flags.contains(SignatureFlags::CONSTRUCTOR) {
parsing_context |= ParsingContextFlags::IN_CONSTRUCTOR;
}
parsing_context
}
}
bitflags! {
#[derive(Debug, Copy, Default, Clone, Eq, PartialEq)]
pub(crate) struct ParsingContextFlags: u8 {
const IN_GENERATOR = 1 << 0;
const IN_FUNCTION = 1 << 1;
const IN_CONSTRUCTOR = 1 << 2;
const IN_ASYNC = 1 << 3;
const TOP_LEVEL = 1 << 4;
const BREAK_ALLOWED = 1 << 5;
const CONTINUE_ALLOWED = 1 << 6;
const AMBIENT_CONTEXT = 1 << 7;
const LOOP = Self::BREAK_ALLOWED.bits() | Self::CONTINUE_ALLOWED.bits();
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();
const PARAMETER_RESET_MASK = Self::IN_CONSTRUCTOR.bits() | Self::IN_FUNCTION.bits() | Self::TOP_LEVEL.bits() | Self::IN_GENERATOR.bits() | Self::IN_ASYNC.bits();
}
}
#[derive(Debug, Default, Copy, Clone)]
pub(crate) struct ParsingContextFlagsSnapshot(ParsingContextFlags);
pub(crate) trait ChangeParserStateFlags {
fn compute_new_flags(&self, existing: ParsingContextFlags) -> ParsingContextFlags;
}
impl<T: ChangeParserStateFlags> ChangeParserState for T {
type Snapshot = ParsingContextFlagsSnapshot;
fn apply(self, state: &mut JsParserState) -> Self::Snapshot {
let new_flags = self.compute_new_flags(state.parsing_context);
ParsingContextFlagsSnapshot(std::mem::replace(&mut state.parsing_context, new_flags))
}
fn restore(state: &mut JsParserState, value: Self::Snapshot) {
state.parsing_context = value.0
}
}
pub(crate) struct EnterParameters(
pub(crate) SignatureFlags,
);
impl ChangeParserStateFlags for EnterParameters {
fn compute_new_flags(&self, existing: ParsingContextFlags) -> ParsingContextFlags {
(existing - ParsingContextFlags::PARAMETER_RESET_MASK) | ParsingContextFlags::from(self.0)
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub(crate) enum BreakableKind {
Iteration,
Switch,
}
pub(crate) struct EnterBreakable(pub(crate) BreakableKind);
impl ChangeParserStateFlags for EnterBreakable {
fn compute_new_flags(&self, existing: ParsingContextFlags) -> ParsingContextFlags {
let mut flags = existing | ParsingContextFlags::BREAK_ALLOWED;
if self.0 == BreakableKind::Iteration {
flags |= ParsingContextFlags::CONTINUE_ALLOWED;
}
flags
}
}
#[derive(Debug, Clone, Default)]
pub struct EnterFunctionSnapshot {
parsing_context: ParsingContextFlags,
label_set: LabelSet,
}
pub(crate) struct EnterFunction(pub(crate) SignatureFlags);
impl ChangeParserState for EnterFunction {
type Snapshot = EnterFunctionSnapshot;
#[inline]
fn apply(self, state: &mut JsParserState) -> Self::Snapshot {
let new_flags = (state.parsing_context - ParsingContextFlags::FUNCTION_RESET_MASK)
| ParsingContextFlags::IN_FUNCTION
| ParsingContextFlags::from(self.0);
EnterFunctionSnapshot {
parsing_context: std::mem::replace(&mut state.parsing_context, new_flags),
label_set: std::mem::take(&mut state.label_set),
}
}
#[inline]
fn restore(state: &mut JsParserState, value: Self::Snapshot) {
state.parsing_context = value.parsing_context;
state.label_set = value.label_set;
}
}
pub(crate) struct EnterClassPropertyInitializer;
impl ChangeParserStateFlags for EnterClassPropertyInitializer {
fn compute_new_flags(&self, existing: ParsingContextFlags) -> ParsingContextFlags {
existing
- ParsingContextFlags::TOP_LEVEL
- ParsingContextFlags::IN_ASYNC
- ParsingContextFlags::IN_GENERATOR
}
}
#[derive(Default, Debug, Clone)]
pub(crate) struct EnterClassStaticInitializationBlockSnapshot {
label_set: LabelSet,
flags: ParsingContextFlags,
}
pub(crate) struct EnterClassStaticInitializationBlock;
impl ChangeParserState for EnterClassStaticInitializationBlock {
type Snapshot = EnterClassStaticInitializationBlockSnapshot;
fn apply(self, state: &mut JsParserState) -> Self::Snapshot {
let flags = (state.parsing_context
- ParsingContextFlags::FUNCTION_RESET_MASK
- ParsingContextFlags::IN_FUNCTION)
| ParsingContextFlags::IN_ASYNC; EnterClassStaticInitializationBlockSnapshot {
flags: std::mem::replace(&mut state.parsing_context, flags),
label_set: std::mem::take(&mut state.label_set),
}
}
fn restore(state: &mut JsParserState, value: Self::Snapshot) {
state.parsing_context = value.flags;
state.label_set = value.label_set;
}
}
#[derive(Debug, Default)]
pub(crate) struct WithLabelSnapshot {
#[cfg(debug_assertions)]
label_set_len: usize,
}
pub(crate) struct WithLabel(pub String, pub LabelledItem);
impl ChangeParserState for WithLabel {
type Snapshot = WithLabelSnapshot;
fn apply(self, state: &mut JsParserState) -> Self::Snapshot {
#[cfg(debug_assertions)]
let previous_len = state.label_set.len();
state.label_set.insert(self.0, self.1);
WithLabelSnapshot {
#[cfg(debug_assertions)]
label_set_len: previous_len,
}
}
#[cfg(not(debug_assertions))]
fn restore(state: &mut JsParserState, _: Self::Snapshot) {
state.label_set.pop();
}
#[cfg(debug_assertions)]
fn restore(state: &mut JsParserState, value: Self::Snapshot) {
assert_eq!(state.label_set.len(), value.label_set_len + 1);
state.label_set.pop();
}
}
pub(crate) struct EnterType;
impl ChangeParserStateFlags for EnterType {
fn compute_new_flags(&self, existing: ParsingContextFlags) -> ParsingContextFlags {
existing - ParsingContextFlags::IN_ASYNC - ParsingContextFlags::IN_GENERATOR
}
}
#[derive(Default)]
pub(crate) struct EnterAmbientContextSnapshot {
flags: ParsingContextFlags,
default_item: Option<ExportDefaultItem>,
strict_mode: Option<StrictMode>,
}
pub(crate) struct EnterAmbientContext;
impl ChangeParserState for EnterAmbientContext {
type Snapshot = EnterAmbientContextSnapshot;
fn apply(self, state: &mut JsParserState) -> Self::Snapshot {
let new_flags = state.parsing_context | ParsingContextFlags::AMBIENT_CONTEXT;
EnterAmbientContextSnapshot {
flags: std::mem::replace(&mut state.parsing_context, new_flags),
default_item: state.default_item.take(),
strict_mode: state.strict.take(),
}
}
fn restore(state: &mut JsParserState, value: Self::Snapshot) {
state.parsing_context = value.flags;
state.default_item = value.default_item;
state.strict = value.strict_mode;
}
}