use crate::syntax::{
ast::{
node::{ContainsSymbol, Declaration, DeclarationPattern, Node},
Position,
},
parser::ParseError,
};
use bitflags::bitflags;
use boa_interner::{Interner, Sym, ToInternedString};
use rustc_hash::FxHashSet;
#[cfg(feature = "deser")]
use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Default, PartialEq)]
pub struct FormalParameterList {
pub(crate) parameters: Box<[FormalParameter]>,
pub(crate) flags: FormalParameterListFlags,
pub(crate) length: u32,
}
impl FormalParameterList {
pub(crate) fn new(
parameters: Box<[FormalParameter]>,
flags: FormalParameterListFlags,
length: u32,
) -> Self {
Self {
parameters,
flags,
length,
}
}
pub(crate) fn empty() -> Self {
Self {
parameters: Box::new([]),
flags: FormalParameterListFlags::default(),
length: 0,
}
}
pub(crate) fn length(&self) -> u32 {
self.length
}
pub(crate) fn is_simple(&self) -> bool {
self.flags.contains(FormalParameterListFlags::IS_SIMPLE)
}
pub(crate) fn has_duplicates(&self) -> bool {
self.flags
.contains(FormalParameterListFlags::HAS_DUPLICATES)
}
pub(crate) fn has_rest_parameter(&self) -> bool {
self.flags
.contains(FormalParameterListFlags::HAS_REST_PARAMETER)
}
pub(crate) fn has_expressions(&self) -> bool {
self.flags
.contains(FormalParameterListFlags::HAS_EXPRESSIONS)
}
pub(crate) fn has_arguments(&self) -> bool {
self.flags.contains(FormalParameterListFlags::HAS_ARGUMENTS)
}
pub(crate) fn name_in_lexically_declared_names(
&self,
names: &[Sym],
position: Position,
) -> Result<(), ParseError> {
for parameter in self.parameters.iter() {
for name in ¶meter.names() {
if names.contains(name) {
return Err(ParseError::General {
message: "formal parameter declared in lexically declared names",
position,
});
}
}
}
Ok(())
}
pub(crate) fn contains_yield_expression(&self) -> bool {
for parameter in self.parameters.iter() {
if parameter
.declaration()
.contains(ContainsSymbol::YieldExpression)
{
return true;
}
}
false
}
pub(crate) fn contains_await_expression(&self) -> bool {
for parameter in self.parameters.iter() {
if parameter
.declaration()
.contains(ContainsSymbol::AwaitExpression)
{
return true;
}
}
false
}
}
impl From<Vec<FormalParameter>> for FormalParameterList {
fn from(parameters: Vec<FormalParameter>) -> Self {
let mut flags = FormalParameterListFlags::default();
let mut length = 0;
let mut names = FxHashSet::default();
for parameter in ¶meters {
let parameter_names = parameter.names();
for name in parameter_names {
if name == Sym::ARGUMENTS {
flags |= FormalParameterListFlags::HAS_ARGUMENTS;
}
if names.contains(&name) {
flags |= FormalParameterListFlags::HAS_DUPLICATES;
} else {
names.insert(name);
}
}
if parameter.is_rest_param() {
flags |= FormalParameterListFlags::HAS_REST_PARAMETER;
}
if parameter.init().is_some() {
flags |= FormalParameterListFlags::HAS_EXPRESSIONS;
}
if parameter.is_rest_param() || parameter.init().is_some() || !parameter.is_identifier()
{
flags.remove(FormalParameterListFlags::IS_SIMPLE);
}
if !(flags.contains(FormalParameterListFlags::HAS_EXPRESSIONS)
|| parameter.is_rest_param()
|| parameter.init().is_some())
{
length += 1;
}
}
Self {
parameters: parameters.into_boxed_slice(),
flags,
length,
}
}
}
impl From<FormalParameter> for FormalParameterList {
fn from(parameter: FormalParameter) -> Self {
let mut flags = FormalParameterListFlags::default();
if parameter.is_rest_param() {
flags |= FormalParameterListFlags::HAS_REST_PARAMETER;
}
if parameter.init().is_some() {
flags |= FormalParameterListFlags::HAS_EXPRESSIONS;
}
if parameter.names().contains(&Sym::ARGUMENTS) {
flags |= FormalParameterListFlags::HAS_ARGUMENTS;
}
if parameter.is_rest_param() || parameter.init().is_some() || !parameter.is_identifier() {
flags.remove(FormalParameterListFlags::IS_SIMPLE);
}
let length = if parameter.is_rest_param() || parameter.init().is_some() {
0
} else {
1
};
Self {
parameters: Box::new([parameter]),
flags,
length,
}
}
}
bitflags! {
#[allow(clippy::unsafe_derive_deserialize)]
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
pub(crate) struct FormalParameterListFlags: u8 {
const IS_SIMPLE = 0b0000_0001;
const HAS_DUPLICATES = 0b0000_0010;
const HAS_REST_PARAMETER = 0b0000_0100;
const HAS_EXPRESSIONS = 0b0000_1000;
const HAS_ARGUMENTS = 0b0001_0000;
}
}
impl Default for FormalParameterListFlags {
fn default() -> Self {
Self::empty().union(Self::IS_SIMPLE)
}
}
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct FormalParameter {
declaration: Declaration,
is_rest_param: bool,
}
impl FormalParameter {
pub(in crate::syntax) fn new<D>(declaration: D, is_rest_param: bool) -> Self
where
D: Into<Declaration>,
{
Self {
declaration: declaration.into(),
is_rest_param,
}
}
pub fn names(&self) -> Vec<Sym> {
match &self.declaration {
Declaration::Identifier { ident, .. } => vec![ident.sym()],
Declaration::Pattern(pattern) => match pattern {
DeclarationPattern::Object(object_pattern) => object_pattern.idents(),
DeclarationPattern::Array(array_pattern) => array_pattern.idents(),
},
}
}
pub fn declaration(&self) -> &Declaration {
&self.declaration
}
pub fn init(&self) -> Option<&Node> {
self.declaration.init()
}
pub fn is_rest_param(&self) -> bool {
self.is_rest_param
}
pub fn is_identifier(&self) -> bool {
matches!(&self.declaration, Declaration::Identifier { .. })
}
}
impl ToInternedString for FormalParameter {
fn to_interned_string(&self, interner: &Interner) -> String {
let mut buf = if self.is_rest_param {
"...".to_owned()
} else {
String::new()
};
buf.push_str(&self.declaration.to_interned_string(interner));
buf
}
}