use crate::syntax::{
ast::node::{
declaration::{
BindingPatternTypeArray, BindingPatternTypeObject, DeclarationPatternArray,
DeclarationPatternObject,
},
field::get_private_field::GetPrivateField,
object::{PropertyDefinition, PropertyName},
ArrayDecl, DeclarationPattern, GetConstField, GetField, Identifier, Node, Object,
},
parser::RESERVED_IDENTIFIERS_STRICT,
};
use boa_interner::{Interner, Sym, ToInternedString};
#[cfg(feature = "deser")]
use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct Assign {
lhs: Box<AssignTarget>,
rhs: Box<Node>,
}
impl Assign {
pub(in crate::syntax) fn new<L, R>(lhs: L, rhs: R) -> Self
where
L: Into<AssignTarget>,
R: Into<Node>,
{
Self {
lhs: Box::new(lhs.into()),
rhs: Box::new(rhs.into()),
}
}
pub fn lhs(&self) -> &AssignTarget {
&self.lhs
}
pub fn rhs(&self) -> &Node {
&self.rhs
}
}
impl ToInternedString for Assign {
fn to_interned_string(&self, interner: &Interner) -> String {
format!(
"{} = {}",
self.lhs.to_interned_string(interner),
self.rhs.to_interned_string(interner)
)
}
}
impl From<Assign> for Node {
fn from(op: Assign) -> Self {
Self::Assign(op)
}
}
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum AssignTarget {
Identifier(Identifier),
GetPrivateField(GetPrivateField),
GetConstField(GetConstField),
GetField(GetField),
DeclarationPattern(DeclarationPattern),
}
impl AssignTarget {
pub(crate) fn from_node(node: &Node, strict: bool) -> Option<Self> {
match node {
Node::Identifier(target) => Some(Self::Identifier(*target)),
Node::GetPrivateField(target) => Some(Self::GetPrivateField(target.clone())),
Node::GetConstField(target) => Some(Self::GetConstField(target.clone())),
Node::GetField(target) => Some(Self::GetField(target.clone())),
Node::Object(object) => {
let pattern = object_decl_to_declaration_pattern(object, strict)?;
Some(Self::DeclarationPattern(pattern))
}
Node::ArrayDecl(array) => {
let pattern = array_decl_to_declaration_pattern(array, strict)?;
Some(Self::DeclarationPattern(pattern))
}
_ => None,
}
}
}
impl ToInternedString for AssignTarget {
fn to_interned_string(&self, interner: &Interner) -> String {
match self {
AssignTarget::Identifier(target) => target.to_interned_string(interner),
AssignTarget::GetPrivateField(target) => target.to_interned_string(interner),
AssignTarget::GetConstField(target) => target.to_interned_string(interner),
AssignTarget::GetField(target) => target.to_interned_string(interner),
AssignTarget::DeclarationPattern(target) => target.to_interned_string(interner),
}
}
}
impl From<Identifier> for AssignTarget {
fn from(target: Identifier) -> Self {
Self::Identifier(target)
}
}
impl From<GetConstField> for AssignTarget {
fn from(target: GetConstField) -> Self {
Self::GetConstField(target)
}
}
impl From<GetField> for AssignTarget {
fn from(target: GetField) -> Self {
Self::GetField(target)
}
}
pub(crate) fn object_decl_to_declaration_pattern(
object: &Object,
strict: bool,
) -> Option<DeclarationPattern> {
let mut bindings = Vec::new();
let mut excluded_keys = Vec::new();
for (i, property) in object.properties().iter().enumerate() {
match property {
PropertyDefinition::IdentifierReference(ident) if strict && *ident == Sym::EVAL => {
return None
}
PropertyDefinition::IdentifierReference(ident) => {
if strict && RESERVED_IDENTIFIERS_STRICT.contains(ident) {
return None;
}
excluded_keys.push(*ident);
bindings.push(BindingPatternTypeObject::SingleName {
ident: *ident,
property_name: PropertyName::Literal(*ident),
default_init: None,
});
}
PropertyDefinition::Property(name, node) => match (name, node) {
(PropertyName::Literal(name), Node::Identifier(ident)) if *name == ident.sym() => {
if strict && *name == Sym::EVAL {
return None;
}
if strict && RESERVED_IDENTIFIERS_STRICT.contains(name) {
return None;
}
excluded_keys.push(*name);
bindings.push(BindingPatternTypeObject::SingleName {
ident: *name,
property_name: PropertyName::Literal(*name),
default_init: None,
});
}
(PropertyName::Literal(name), Node::Identifier(ident)) => {
bindings.push(BindingPatternTypeObject::SingleName {
ident: ident.sym(),
property_name: PropertyName::Literal(*name),
default_init: None,
});
}
(PropertyName::Literal(name), Node::Object(object)) => {
let pattern = object_decl_to_declaration_pattern(object, strict)?;
bindings.push(BindingPatternTypeObject::BindingPattern {
ident: PropertyName::Literal(*name),
pattern,
default_init: None,
});
}
(PropertyName::Literal(name), Node::ArrayDecl(array)) => {
let pattern = array_decl_to_declaration_pattern(array, strict)?;
bindings.push(BindingPatternTypeObject::BindingPattern {
ident: PropertyName::Literal(*name),
pattern,
default_init: None,
});
}
(_, Node::Assign(assign)) => match assign.lhs() {
AssignTarget::Identifier(ident) => {
if let Some(name) = name.literal() {
if name == ident.sym() {
if strict && name == Sym::EVAL {
return None;
}
if strict && RESERVED_IDENTIFIERS_STRICT.contains(&name) {
return None;
}
excluded_keys.push(name);
bindings.push(BindingPatternTypeObject::SingleName {
ident: name,
property_name: PropertyName::Literal(name),
default_init: Some(assign.rhs().clone()),
});
} else {
bindings.push(BindingPatternTypeObject::SingleName {
ident: ident.sym(),
property_name: PropertyName::Literal(name),
default_init: Some(assign.rhs().clone()),
});
}
} else {
return None;
}
}
AssignTarget::DeclarationPattern(pattern) => {
bindings.push(BindingPatternTypeObject::BindingPattern {
ident: name.clone(),
pattern: pattern.clone(),
default_init: Some(assign.rhs().clone()),
});
}
AssignTarget::GetConstField(field) => {
bindings.push(BindingPatternTypeObject::AssignmentGetConstField {
property_name: name.clone(),
get_const_field: field.clone(),
default_init: Some(assign.rhs().clone()),
});
}
AssignTarget::GetField(field) => {
bindings.push(BindingPatternTypeObject::AssignmentGetField {
property_name: name.clone(),
get_field: field.clone(),
default_init: Some(assign.rhs().clone()),
});
}
AssignTarget::GetPrivateField(_) => return None,
},
(_, Node::GetConstField(field)) => {
bindings.push(BindingPatternTypeObject::AssignmentGetConstField {
property_name: name.clone(),
get_const_field: field.clone(),
default_init: None,
});
}
(_, Node::GetField(field)) => {
bindings.push(BindingPatternTypeObject::AssignmentGetField {
property_name: name.clone(),
get_field: field.clone(),
default_init: None,
});
}
(PropertyName::Computed(name), Node::Identifier(ident)) => {
bindings.push(BindingPatternTypeObject::SingleName {
ident: ident.sym(),
property_name: PropertyName::Computed(name.clone()),
default_init: None,
});
}
_ => return None,
},
PropertyDefinition::SpreadObject(spread) => {
match spread {
Node::Identifier(ident) => {
bindings.push(BindingPatternTypeObject::RestProperty {
ident: ident.sym(),
excluded_keys: excluded_keys.clone(),
});
}
Node::GetConstField(get_const_field) => {
bindings.push(BindingPatternTypeObject::AssignmentRestProperty {
get_const_field: get_const_field.clone(),
excluded_keys: excluded_keys.clone(),
});
}
_ => return None,
}
if i + 1 != object.properties().len() {
return None;
}
}
PropertyDefinition::MethodDefinition(_, _) => return None,
PropertyDefinition::CoverInitializedName(ident, expr) => {
if strict && (*ident == Sym::EVAL || *ident == Sym::ARGUMENTS) {
return None;
}
bindings.push(BindingPatternTypeObject::SingleName {
ident: *ident,
property_name: PropertyName::Literal(*ident),
default_init: Some(expr.clone()),
});
}
}
}
if object.properties().is_empty() {
bindings.push(BindingPatternTypeObject::Empty);
}
Some(DeclarationPattern::Object(DeclarationPatternObject::new(
bindings, None,
)))
}
pub(crate) fn array_decl_to_declaration_pattern(
array: &ArrayDecl,
strict: bool,
) -> Option<DeclarationPattern> {
if array.has_trailing_comma_spread() {
return None;
}
let mut bindings = Vec::new();
for (i, node) in array.as_ref().iter().enumerate() {
match node {
Node::Identifier(ident) => {
if strict && ident.sym() == Sym::ARGUMENTS {
return None;
}
bindings.push(BindingPatternTypeArray::SingleName {
ident: ident.sym(),
default_init: None,
});
}
Node::Spread(spread) => {
match spread.val() {
Node::Identifier(ident) => {
bindings
.push(BindingPatternTypeArray::SingleNameRest { ident: ident.sym() });
}
Node::GetField(get_field) => {
bindings.push(BindingPatternTypeArray::GetFieldRest {
get_field: get_field.clone(),
});
}
Node::GetConstField(get_const_field) => {
bindings.push(BindingPatternTypeArray::GetConstFieldRest {
get_const_field: get_const_field.clone(),
});
}
Node::ArrayDecl(array) => {
let pattern = array_decl_to_declaration_pattern(array, strict)?;
bindings.push(BindingPatternTypeArray::BindingPatternRest { pattern });
}
Node::Object(object) => {
let pattern = object_decl_to_declaration_pattern(object, strict)?;
bindings.push(BindingPatternTypeArray::BindingPatternRest { pattern });
}
_ => return None,
}
if i + 1 != array.as_ref().len() {
return None;
}
}
Node::Empty => {
bindings.push(BindingPatternTypeArray::Elision);
}
Node::Assign(assign) => match assign.lhs() {
AssignTarget::Identifier(ident) => {
bindings.push(BindingPatternTypeArray::SingleName {
ident: ident.sym(),
default_init: Some(assign.rhs().clone()),
});
}
AssignTarget::GetConstField(get_const_field) => {
bindings.push(BindingPatternTypeArray::GetConstField {
get_const_field: get_const_field.clone(),
});
}
AssignTarget::GetField(get_field) => {
bindings.push(BindingPatternTypeArray::GetField {
get_field: get_field.clone(),
});
}
AssignTarget::DeclarationPattern(pattern) => match pattern {
DeclarationPattern::Object(pattern) => {
let mut pattern = pattern.clone();
if pattern.init.is_none() {
pattern.init = Some(assign.rhs().clone());
}
bindings.push(BindingPatternTypeArray::BindingPattern {
pattern: DeclarationPattern::Object(pattern),
});
}
DeclarationPattern::Array(pattern) => {
let mut pattern = pattern.clone();
if pattern.init.is_none() {
pattern.init = Some(assign.rhs().clone());
}
bindings.push(BindingPatternTypeArray::BindingPattern {
pattern: DeclarationPattern::Array(pattern),
});
}
},
AssignTarget::GetPrivateField(_) => return None,
},
Node::ArrayDecl(array) => {
let pattern = array_decl_to_declaration_pattern(array, strict)?;
bindings.push(BindingPatternTypeArray::BindingPattern { pattern });
}
Node::Object(object) => {
let pattern = object_decl_to_declaration_pattern(object, strict)?;
bindings.push(BindingPatternTypeArray::BindingPattern { pattern });
}
Node::GetField(get_field) => {
bindings.push(BindingPatternTypeArray::GetField {
get_field: get_field.clone(),
});
}
Node::GetConstField(get_const_field) => {
bindings.push(BindingPatternTypeArray::GetConstField {
get_const_field: get_const_field.clone(),
});
}
_ => return None,
}
}
Some(DeclarationPattern::Array(DeclarationPatternArray::new(
bindings, None,
)))
}