1use crate::{
4 LinearPosition, LinearSpan, LinearSpanIgnoreEq, Span, Spanned, block_to_string,
5 expression::{
6 Expression, Identifier, RESERVED_IDENTIFIERS_STRICT,
7 operator::assign::{AssignOp, AssignTarget},
8 },
9 function::{FormalParameterList, FunctionBody},
10 join_nodes,
11 operations::{ContainsSymbol, contains},
12 pattern::{ObjectPattern, ObjectPatternElement},
13 property::{MethodDefinitionKind, PropertyName},
14 scope::FunctionScopes,
15 visitor::{VisitWith, Visitor, VisitorMut},
16};
17use boa_interner::{Interner, Sym, ToIndentedString, ToInternedString};
18use core::{fmt::Write as _, ops::ControlFlow};
19
20#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
40#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
41#[derive(Clone, Debug, PartialEq)]
42pub struct ObjectLiteral {
43 properties: Box<[PropertyDefinition]>,
44 span: Span,
45}
46
47impl ObjectLiteral {
48 #[inline]
50 #[must_use]
51 pub fn new<T>(properties: T, span: Span) -> Self
52 where
53 T: Into<Box<[PropertyDefinition]>>,
54 {
55 Self {
56 properties: properties.into(),
57 span,
58 }
59 }
60
61 #[inline]
63 #[must_use]
64 pub const fn properties(&self) -> &[PropertyDefinition] {
65 &self.properties
66 }
67
68 #[must_use]
70 pub fn to_pattern(&self, strict: bool) -> Option<ObjectPattern> {
71 let mut bindings = Vec::new();
72 for (i, property) in self.properties.iter().enumerate() {
73 match property {
74 PropertyDefinition::IdentifierReference(ident) if strict && *ident == Sym::EVAL => {
75 return None;
76 }
77 PropertyDefinition::IdentifierReference(ident) => {
78 if strict && RESERVED_IDENTIFIERS_STRICT.contains(&ident.sym()) {
79 return None;
80 }
81
82 bindings.push(ObjectPatternElement::SingleName {
83 ident: *ident,
84 name: PropertyName::Literal(*ident),
85 default_init: None,
86 });
87 }
88 PropertyDefinition::Property(name, expr) => match (name, expr) {
89 (PropertyName::Literal(name), Expression::Identifier(ident))
90 if name.sym() == ident.sym() =>
91 {
92 if strict && *name == Sym::EVAL {
93 return None;
94 }
95 if strict && RESERVED_IDENTIFIERS_STRICT.contains(&name.sym()) {
96 return None;
97 }
98
99 bindings.push(ObjectPatternElement::SingleName {
100 ident: *ident,
101 name: PropertyName::Literal(*name),
102 default_init: None,
103 });
104 }
105 (PropertyName::Literal(name), Expression::Identifier(ident)) => {
106 bindings.push(ObjectPatternElement::SingleName {
107 ident: *ident,
108 name: PropertyName::Literal(*name),
109 default_init: None,
110 });
111 }
112 (PropertyName::Literal(name), Expression::ObjectLiteral(object)) => {
113 let pattern = object.to_pattern(strict)?.into();
114 bindings.push(ObjectPatternElement::Pattern {
115 name: PropertyName::Literal(*name),
116 pattern,
117 default_init: None,
118 });
119 }
120 (PropertyName::Literal(name), Expression::ArrayLiteral(array)) => {
121 let pattern = array.to_pattern(strict)?.into();
122 bindings.push(ObjectPatternElement::Pattern {
123 name: PropertyName::Literal(*name),
124 pattern,
125 default_init: None,
126 });
127 }
128 (_, Expression::Assign(assign)) => {
129 if assign.op() != AssignOp::Assign {
130 return None;
131 }
132 match assign.lhs() {
133 AssignTarget::Identifier(ident) => {
134 let name = name.literal()?;
135 if name.sym() == ident.sym() {
136 if strict && name == Sym::EVAL {
137 return None;
138 }
139 if strict && RESERVED_IDENTIFIERS_STRICT.contains(&name.sym()) {
140 return None;
141 }
142 }
143 let mut init = assign.rhs().clone();
144 init.set_anonymous_function_definition_name(ident);
145 bindings.push(ObjectPatternElement::SingleName {
146 ident: *ident,
147 name: PropertyName::Literal(name),
148 default_init: Some(init),
149 });
150 }
151 AssignTarget::Pattern(pattern) => {
152 bindings.push(ObjectPatternElement::Pattern {
153 name: name.clone(),
154 pattern: pattern.clone(),
155 default_init: Some(assign.rhs().clone()),
156 });
157 }
158 AssignTarget::Access(access) => {
159 bindings.push(ObjectPatternElement::AssignmentPropertyAccess {
160 name: name.clone(),
161 access: access.clone(),
162 default_init: Some(assign.rhs().clone()),
163 });
164 }
165 }
166 }
167 (_, Expression::PropertyAccess(access)) => {
168 bindings.push(ObjectPatternElement::AssignmentPropertyAccess {
169 name: name.clone(),
170 access: access.clone(),
171 default_init: None,
172 });
173 }
174 (PropertyName::Computed(name), Expression::Identifier(ident)) => {
175 bindings.push(ObjectPatternElement::SingleName {
176 ident: *ident,
177 name: PropertyName::Computed(name.clone()),
178 default_init: None,
179 });
180 }
181 _ => return None,
182 },
183 PropertyDefinition::SpreadObject(spread) => {
184 match spread {
185 Expression::Identifier(ident) => {
186 bindings.push(ObjectPatternElement::RestProperty { ident: *ident });
187 }
188 Expression::PropertyAccess(access) => {
189 bindings.push(ObjectPatternElement::AssignmentRestPropertyAccess {
190 access: access.clone(),
191 });
192 }
193 _ => return None,
194 }
195 if i + 1 != self.properties.len() {
196 return None;
197 }
198 }
199 PropertyDefinition::MethodDefinition(_) => return None,
200 PropertyDefinition::CoverInitializedName(ident, expr) => {
201 if strict && [Sym::EVAL, Sym::ARGUMENTS].contains(&ident.sym()) {
202 return None;
203 }
204 let mut expr = expr.clone();
205 expr.set_anonymous_function_definition_name(ident);
206 bindings.push(ObjectPatternElement::SingleName {
207 ident: *ident,
208 name: PropertyName::Literal(*ident),
209 default_init: Some(expr),
210 });
211 }
212 }
213 }
214
215 Some(ObjectPattern::new(bindings.into(), self.span))
216 }
217}
218
219impl Spanned for ObjectLiteral {
220 #[inline]
221 fn span(&self) -> Span {
222 self.span
223 }
224}
225
226impl ToIndentedString for ObjectLiteral {
227 fn to_indented_string(&self, interner: &Interner, indent_n: usize) -> String {
228 let mut buf = "{\n".to_owned();
229 let indentation = " ".repeat(indent_n + 1);
230 for property in &*self.properties {
231 match property {
232 PropertyDefinition::IdentifierReference(ident) => {
233 let _ = writeln!(
234 buf,
235 "{indentation}{},",
236 interner.resolve_expect(ident.sym())
237 );
238 }
239 PropertyDefinition::Property(key, value) => {
240 let _ = writeln!(
241 buf,
242 "{indentation}{}: {},",
243 key.to_interned_string(interner),
244 value.to_no_indent_string(interner, indent_n + 1)
245 );
246 }
247 PropertyDefinition::SpreadObject(key) => {
248 let _ = writeln!(buf, "{indentation}...{},", key.to_interned_string(interner));
249 }
250 PropertyDefinition::MethodDefinition(m) => {
251 buf.push_str(&m.to_indented_string(interner, indent_n));
252 }
253 PropertyDefinition::CoverInitializedName(ident, expr) => {
254 let _ = writeln!(
255 buf,
256 "{indentation}{} = {},",
257 interner.resolve_expect(ident.sym()),
258 expr.to_no_indent_string(interner, indent_n + 1)
259 );
260 }
261 }
262 }
263 let _ = write!(buf, "{}}}", " ".repeat(indent_n));
264
265 buf
266 }
267}
268
269impl From<ObjectLiteral> for Expression {
270 #[inline]
271 fn from(obj: ObjectLiteral) -> Self {
272 Self::ObjectLiteral(obj)
273 }
274}
275
276impl VisitWith for ObjectLiteral {
277 fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
278 where
279 V: Visitor<'a>,
280 {
281 for pd in &*self.properties {
282 visitor.visit_property_definition(pd)?;
283 }
284 ControlFlow::Continue(())
285 }
286
287 fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
288 where
289 V: VisitorMut<'a>,
290 {
291 for pd in &mut *self.properties {
292 visitor.visit_property_definition_mut(pd)?;
293 }
294 ControlFlow::Continue(())
295 }
296}
297
298#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
312#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
313#[derive(Clone, Debug, PartialEq)]
314pub enum PropertyDefinition {
315 IdentifierReference(Identifier),
324
325 Property(PropertyName, Expression),
334
335 MethodDefinition(ObjectMethodDefinition),
344
345 SpreadObject(Expression),
357
358 CoverInitializedName(Identifier, Expression),
365}
366
367impl VisitWith for PropertyDefinition {
368 fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
369 where
370 V: Visitor<'a>,
371 {
372 match self {
373 Self::IdentifierReference(id) => visitor.visit_identifier(id),
374 Self::Property(pn, expr) => {
375 visitor.visit_property_name(pn)?;
376 visitor.visit_expression(expr)
377 }
378 Self::MethodDefinition(m) => visitor.visit_object_method_definition(m),
379 Self::SpreadObject(expr) => visitor.visit_expression(expr),
380 Self::CoverInitializedName(id, expr) => {
381 visitor.visit_identifier(id)?;
382 visitor.visit_expression(expr)
383 }
384 }
385 }
386
387 fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
388 where
389 V: VisitorMut<'a>,
390 {
391 match self {
392 Self::IdentifierReference(id) => visitor.visit_identifier_mut(id),
393 Self::Property(pn, expr) => {
394 visitor.visit_property_name_mut(pn)?;
395 visitor.visit_expression_mut(expr)
396 }
397 Self::MethodDefinition(m) => visitor.visit_object_method_definition_mut(m),
398 Self::SpreadObject(expr) => visitor.visit_expression_mut(expr),
399 Self::CoverInitializedName(id, expr) => {
400 visitor.visit_identifier_mut(id)?;
401 visitor.visit_expression_mut(expr)
402 }
403 }
404 }
405}
406
407#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
416#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
417#[derive(Clone, Debug, PartialEq)]
418pub struct ObjectMethodDefinition {
419 pub(crate) name: PropertyName,
420 pub(crate) parameters: FormalParameterList,
421 pub(crate) body: FunctionBody,
422 pub(crate) contains_direct_eval: bool,
423 kind: MethodDefinitionKind,
424
425 #[cfg_attr(feature = "serde", serde(skip))]
426 pub(crate) scopes: FunctionScopes,
427 linear_span: LinearSpanIgnoreEq,
428}
429
430impl ObjectMethodDefinition {
431 #[inline]
433 #[must_use]
434 pub fn new(
435 name: PropertyName,
436 parameters: FormalParameterList,
437 body: FunctionBody,
438 kind: MethodDefinitionKind,
439 start_linear_pos: LinearPosition,
440 ) -> Self {
441 let contains_direct_eval = contains(¶meters, ContainsSymbol::DirectEval)
442 || contains(&body, ContainsSymbol::DirectEval);
443 let linear_span = LinearSpan::new(start_linear_pos, body.linear_pos_end()).into();
444
445 Self {
446 name,
447 parameters,
448 body,
449 contains_direct_eval,
450 kind,
451 scopes: FunctionScopes::default(),
452 linear_span,
453 }
454 }
455
456 #[inline]
458 #[must_use]
459 pub const fn name(&self) -> &PropertyName {
460 &self.name
461 }
462
463 #[inline]
465 #[must_use]
466 pub const fn parameters(&self) -> &FormalParameterList {
467 &self.parameters
468 }
469
470 #[inline]
472 #[must_use]
473 pub const fn body(&self) -> &FunctionBody {
474 &self.body
475 }
476
477 #[inline]
479 #[must_use]
480 pub const fn kind(&self) -> MethodDefinitionKind {
481 self.kind
482 }
483
484 #[inline]
486 #[must_use]
487 pub const fn scopes(&self) -> &FunctionScopes {
488 &self.scopes
489 }
490
491 #[inline]
493 #[must_use]
494 pub const fn linear_span(&self) -> LinearSpan {
495 self.linear_span.0
496 }
497
498 #[inline]
500 #[must_use]
501 pub const fn contains_direct_eval(&self) -> bool {
502 self.contains_direct_eval
503 }
504}
505
506impl ToIndentedString for ObjectMethodDefinition {
507 fn to_indented_string(&self, interner: &Interner, indent_n: usize) -> String {
508 let indentation = " ".repeat(indent_n + 1);
509 let prefix = match &self.kind {
510 MethodDefinitionKind::Get => "get ",
511 MethodDefinitionKind::Set => "set ",
512 MethodDefinitionKind::Ordinary => "",
513 MethodDefinitionKind::Generator => "*",
514 MethodDefinitionKind::AsyncGenerator => "async *",
515 MethodDefinitionKind::Async => "async ",
516 };
517 let name = self.name.to_interned_string(interner);
518 let parameters = join_nodes(interner, self.parameters.as_ref());
519 let body = block_to_string(&self.body.statements, interner, indent_n + 1);
520 format!("{indentation}{prefix}{name}({parameters}) {body},\n")
521 }
522}
523
524impl VisitWith for ObjectMethodDefinition {
525 fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
526 where
527 V: Visitor<'a>,
528 {
529 visitor.visit_property_name(&self.name)?;
530 visitor.visit_formal_parameter_list(&self.parameters)?;
531 visitor.visit_function_body(&self.body)
532 }
533
534 fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
535 where
536 V: VisitorMut<'a>,
537 {
538 visitor.visit_property_name_mut(&mut self.name)?;
539 visitor.visit_formal_parameter_list_mut(&mut self.parameters)?;
540 visitor.visit_function_body_mut(&mut self.body)
541 }
542}