use crate::syntax::ast::{
node::{
declaration::block_to_string, join_nodes, AsyncFunctionExpr, AsyncGeneratorExpr,
FormalParameterList, FunctionExpr, GeneratorExpr, Node, StatementList,
},
Const,
};
use boa_interner::{Interner, Sym, ToInternedString};
#[cfg(feature = "deser")]
use serde::{Deserialize, Serialize};
#[cfg(test)]
mod tests;
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "deser", serde(transparent))]
#[derive(Clone, Debug, PartialEq)]
pub struct Object {
properties: Box<[PropertyDefinition]>,
}
impl Object {
pub fn properties(&self) -> &[PropertyDefinition] {
&self.properties
}
pub(in crate::syntax::ast::node) fn to_indented_string(
&self,
interner: &Interner,
indent_n: usize,
) -> String {
let mut buf = "{\n".to_owned();
let indentation = " ".repeat(indent_n + 1);
for property in self.properties().iter() {
buf.push_str(&match property {
PropertyDefinition::IdentifierReference(ident) => {
format!("{indentation}{},\n", interner.resolve_expect(*ident))
}
PropertyDefinition::Property(key, value) => {
format!(
"{indentation}{}: {},\n",
key.to_interned_string(interner),
value.to_no_indent_string(interner, indent_n + 1)
)
}
PropertyDefinition::SpreadObject(key) => {
format!("{indentation}...{},\n", key.to_interned_string(interner))
}
PropertyDefinition::MethodDefinition(method, key) => {
format!(
"{indentation}{}{}({}) {},\n",
match &method {
MethodDefinition::Get(_) => "get ",
MethodDefinition::Set(_) => "set ",
_ => "",
},
key.to_interned_string(interner),
match &method {
MethodDefinition::Get(node)
| MethodDefinition::Set(node)
| MethodDefinition::Ordinary(node) => {
join_nodes(interner, &node.parameters().parameters)
}
MethodDefinition::Generator(node) => {
join_nodes(interner, &node.parameters().parameters)
}
MethodDefinition::AsyncGenerator(node) => {
join_nodes(interner, &node.parameters().parameters)
}
MethodDefinition::Async(node) => {
join_nodes(interner, &node.parameters().parameters)
}
},
match &method {
MethodDefinition::Get(node)
| MethodDefinition::Set(node)
| MethodDefinition::Ordinary(node) => {
block_to_string(node.body(), interner, indent_n + 1)
}
MethodDefinition::Generator(node) => {
block_to_string(node.body(), interner, indent_n + 1)
}
MethodDefinition::AsyncGenerator(node) => {
block_to_string(node.body(), interner, indent_n + 1)
}
MethodDefinition::Async(node) => {
block_to_string(node.body(), interner, indent_n + 1)
}
},
)
}
PropertyDefinition::CoverInitializedName(ident, expr) => {
format!(
"{indentation}{} = {},\n",
interner.resolve_expect(*ident),
expr.to_no_indent_string(interner, indent_n + 1)
)
}
});
}
buf.push_str(&format!("{}}}", " ".repeat(indent_n)));
buf
}
}
impl ToInternedString for Object {
fn to_interned_string(&self, interner: &Interner) -> String {
self.to_indented_string(interner, 0)
}
}
impl<T> From<T> for Object
where
T: Into<Box<[PropertyDefinition]>>,
{
fn from(props: T) -> Self {
Self {
properties: props.into(),
}
}
}
impl From<Object> for Node {
fn from(obj: Object) -> Self {
Self::Object(obj)
}
}
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum PropertyDefinition {
IdentifierReference(Sym),
Property(PropertyName, Node),
MethodDefinition(MethodDefinition, PropertyName),
SpreadObject(Node),
CoverInitializedName(Sym, Node),
}
impl PropertyDefinition {
pub fn identifier_reference(ident: Sym) -> Self {
Self::IdentifierReference(ident)
}
pub fn property<N, V>(name: N, value: V) -> Self
where
N: Into<PropertyName>,
V: Into<Node>,
{
Self::Property(name.into(), value.into())
}
pub fn method_definition<N>(kind: MethodDefinition, name: N) -> Self
where
N: Into<PropertyName>,
{
Self::MethodDefinition(kind, name.into())
}
pub fn spread_object<O>(obj: O) -> Self
where
O: Into<Node>,
{
Self::SpreadObject(obj.into())
}
}
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum MethodDefinition {
Get(FunctionExpr),
Set(FunctionExpr),
Ordinary(FunctionExpr),
Generator(GeneratorExpr),
AsyncGenerator(AsyncGeneratorExpr),
Async(AsyncFunctionExpr),
}
impl MethodDefinition {
pub(crate) fn body(&self) -> &StatementList {
match self {
MethodDefinition::Get(expr)
| MethodDefinition::Set(expr)
| MethodDefinition::Ordinary(expr) => expr.body(),
MethodDefinition::Generator(expr) => expr.body(),
MethodDefinition::AsyncGenerator(expr) => expr.body(),
MethodDefinition::Async(expr) => expr.body(),
}
}
pub(crate) fn parameters(&self) -> &FormalParameterList {
match self {
MethodDefinition::Get(expr)
| MethodDefinition::Set(expr)
| MethodDefinition::Ordinary(expr) => expr.parameters(),
MethodDefinition::Generator(expr) => expr.parameters(),
MethodDefinition::AsyncGenerator(expr) => expr.parameters(),
MethodDefinition::Async(expr) => expr.parameters(),
}
}
}
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum PropertyName {
Literal(Sym),
Computed(Node),
}
impl PropertyName {
pub(crate) fn literal(&self) -> Option<Sym> {
if let Self::Literal(sym) = self {
Some(*sym)
} else {
None
}
}
pub(crate) fn computed(&self) -> Option<&Node> {
if let Self::Computed(node) = self {
Some(node)
} else {
None
}
}
pub(in crate::syntax) fn prop_name(&self) -> Option<Sym> {
match self {
PropertyName::Literal(sym)
| PropertyName::Computed(Node::Const(Const::String(sym))) => Some(*sym),
PropertyName::Computed(_) => None,
}
}
}
impl ToInternedString for PropertyName {
fn to_interned_string(&self, interner: &Interner) -> String {
match self {
PropertyName::Literal(key) => interner.resolve_expect(*key).to_owned(),
PropertyName::Computed(key) => key.to_interned_string(interner),
}
}
}
impl From<Sym> for PropertyName {
fn from(name: Sym) -> Self {
Self::Literal(name)
}
}
impl From<Node> for PropertyName {
fn from(name: Node) -> Self {
Self::Computed(name)
}
}
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub(crate) enum ClassElementName {
PropertyName(PropertyName),
PrivateIdentifier(Sym),
}