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 if let Some(name) = name.literal() {
135 if name.sym() == ident.sym() {
136 if strict && name == Sym::EVAL {
137 return None;
138 }
139 if strict
140 && RESERVED_IDENTIFIERS_STRICT.contains(&name.sym())
141 {
142 return None;
143 }
144 }
145 let mut init = assign.rhs().clone();
146 init.set_anonymous_function_definition_name(ident);
147 bindings.push(ObjectPatternElement::SingleName {
148 ident: *ident,
149 name: PropertyName::Literal(name),
150 default_init: Some(init),
151 });
152 } else {
153 return None;
154 }
155 }
156 AssignTarget::Pattern(pattern) => {
157 bindings.push(ObjectPatternElement::Pattern {
158 name: name.clone(),
159 pattern: pattern.clone(),
160 default_init: Some(assign.rhs().clone()),
161 });
162 }
163 AssignTarget::Access(access) => {
164 bindings.push(ObjectPatternElement::AssignmentPropertyAccess {
165 name: name.clone(),
166 access: access.clone(),
167 default_init: Some(assign.rhs().clone()),
168 });
169 }
170 }
171 }
172 (_, Expression::PropertyAccess(access)) => {
173 bindings.push(ObjectPatternElement::AssignmentPropertyAccess {
174 name: name.clone(),
175 access: access.clone(),
176 default_init: None,
177 });
178 }
179 (PropertyName::Computed(name), Expression::Identifier(ident)) => {
180 bindings.push(ObjectPatternElement::SingleName {
181 ident: *ident,
182 name: PropertyName::Computed(name.clone()),
183 default_init: None,
184 });
185 }
186 _ => return None,
187 },
188 PropertyDefinition::SpreadObject(spread) => {
189 match spread {
190 Expression::Identifier(ident) => {
191 bindings.push(ObjectPatternElement::RestProperty { ident: *ident });
192 }
193 Expression::PropertyAccess(access) => {
194 bindings.push(ObjectPatternElement::AssignmentRestPropertyAccess {
195 access: access.clone(),
196 });
197 }
198 _ => return None,
199 }
200 if i + 1 != self.properties.len() {
201 return None;
202 }
203 }
204 PropertyDefinition::MethodDefinition(_) => return None,
205 PropertyDefinition::CoverInitializedName(ident, expr) => {
206 if strict && [Sym::EVAL, Sym::ARGUMENTS].contains(&ident.sym()) {
207 return None;
208 }
209 let mut expr = expr.clone();
210 expr.set_anonymous_function_definition_name(ident);
211 bindings.push(ObjectPatternElement::SingleName {
212 ident: *ident,
213 name: PropertyName::Literal(*ident),
214 default_init: Some(expr),
215 });
216 }
217 }
218 }
219
220 Some(ObjectPattern::new(bindings.into(), self.span))
221 }
222}
223
224impl Spanned for ObjectLiteral {
225 #[inline]
226 fn span(&self) -> Span {
227 self.span
228 }
229}
230
231impl ToIndentedString for ObjectLiteral {
232 fn to_indented_string(&self, interner: &Interner, indent_n: usize) -> String {
233 let mut buf = "{\n".to_owned();
234 let indentation = " ".repeat(indent_n + 1);
235 for property in &*self.properties {
236 match property {
237 PropertyDefinition::IdentifierReference(ident) => {
238 let _ = writeln!(
239 buf,
240 "{indentation}{},",
241 interner.resolve_expect(ident.sym())
242 );
243 }
244 PropertyDefinition::Property(key, value) => {
245 let _ = writeln!(
246 buf,
247 "{indentation}{}: {},",
248 key.to_interned_string(interner),
249 value.to_no_indent_string(interner, indent_n + 1)
250 );
251 }
252 PropertyDefinition::SpreadObject(key) => {
253 let _ = writeln!(buf, "{indentation}...{},", key.to_interned_string(interner));
254 }
255 PropertyDefinition::MethodDefinition(m) => {
256 buf.push_str(&m.to_indented_string(interner, indent_n));
257 }
258 PropertyDefinition::CoverInitializedName(ident, expr) => {
259 let _ = writeln!(
260 buf,
261 "{indentation}{} = {},",
262 interner.resolve_expect(ident.sym()),
263 expr.to_no_indent_string(interner, indent_n + 1)
264 );
265 }
266 }
267 }
268 let _ = write!(buf, "{}}}", " ".repeat(indent_n));
269
270 buf
271 }
272}
273
274impl From<ObjectLiteral> for Expression {
275 #[inline]
276 fn from(obj: ObjectLiteral) -> Self {
277 Self::ObjectLiteral(obj)
278 }
279}
280
281impl VisitWith for ObjectLiteral {
282 fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
283 where
284 V: Visitor<'a>,
285 {
286 for pd in &*self.properties {
287 visitor.visit_property_definition(pd)?;
288 }
289 ControlFlow::Continue(())
290 }
291
292 fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
293 where
294 V: VisitorMut<'a>,
295 {
296 for pd in &mut *self.properties {
297 visitor.visit_property_definition_mut(pd)?;
298 }
299 ControlFlow::Continue(())
300 }
301}
302
303#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
317#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
318#[derive(Clone, Debug, PartialEq)]
319pub enum PropertyDefinition {
320 IdentifierReference(Identifier),
329
330 Property(PropertyName, Expression),
339
340 MethodDefinition(ObjectMethodDefinition),
349
350 SpreadObject(Expression),
362
363 CoverInitializedName(Identifier, Expression),
370}
371
372impl VisitWith for PropertyDefinition {
373 fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
374 where
375 V: Visitor<'a>,
376 {
377 match self {
378 Self::IdentifierReference(id) => visitor.visit_identifier(id),
379 Self::Property(pn, expr) => {
380 visitor.visit_property_name(pn)?;
381 visitor.visit_expression(expr)
382 }
383 Self::MethodDefinition(m) => visitor.visit_object_method_definition(m),
384 Self::SpreadObject(expr) => visitor.visit_expression(expr),
385 Self::CoverInitializedName(id, expr) => {
386 visitor.visit_identifier(id)?;
387 visitor.visit_expression(expr)
388 }
389 }
390 }
391
392 fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
393 where
394 V: VisitorMut<'a>,
395 {
396 match self {
397 Self::IdentifierReference(id) => visitor.visit_identifier_mut(id),
398 Self::Property(pn, expr) => {
399 visitor.visit_property_name_mut(pn)?;
400 visitor.visit_expression_mut(expr)
401 }
402 Self::MethodDefinition(m) => visitor.visit_object_method_definition_mut(m),
403 Self::SpreadObject(expr) => visitor.visit_expression_mut(expr),
404 Self::CoverInitializedName(id, expr) => {
405 visitor.visit_identifier_mut(id)?;
406 visitor.visit_expression_mut(expr)
407 }
408 }
409 }
410}
411
412#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
421#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
422#[derive(Clone, Debug, PartialEq)]
423pub struct ObjectMethodDefinition {
424 pub(crate) name: PropertyName,
425 pub(crate) parameters: FormalParameterList,
426 pub(crate) body: FunctionBody,
427 pub(crate) contains_direct_eval: bool,
428 kind: MethodDefinitionKind,
429
430 #[cfg_attr(feature = "serde", serde(skip))]
431 pub(crate) scopes: FunctionScopes,
432 linear_span: LinearSpanIgnoreEq,
433}
434
435impl ObjectMethodDefinition {
436 #[inline]
438 #[must_use]
439 pub fn new(
440 name: PropertyName,
441 parameters: FormalParameterList,
442 body: FunctionBody,
443 kind: MethodDefinitionKind,
444 start_linear_pos: LinearPosition,
445 ) -> Self {
446 let contains_direct_eval = contains(¶meters, ContainsSymbol::DirectEval)
447 || contains(&body, ContainsSymbol::DirectEval);
448 let linear_span = LinearSpan::new(start_linear_pos, body.linear_pos_end()).into();
449
450 Self {
451 name,
452 parameters,
453 body,
454 contains_direct_eval,
455 kind,
456 scopes: FunctionScopes::default(),
457 linear_span,
458 }
459 }
460
461 #[inline]
463 #[must_use]
464 pub const fn name(&self) -> &PropertyName {
465 &self.name
466 }
467
468 #[inline]
470 #[must_use]
471 pub const fn parameters(&self) -> &FormalParameterList {
472 &self.parameters
473 }
474
475 #[inline]
477 #[must_use]
478 pub const fn body(&self) -> &FunctionBody {
479 &self.body
480 }
481
482 #[inline]
484 #[must_use]
485 pub const fn kind(&self) -> MethodDefinitionKind {
486 self.kind
487 }
488
489 #[inline]
491 #[must_use]
492 pub const fn scopes(&self) -> &FunctionScopes {
493 &self.scopes
494 }
495
496 #[inline]
498 #[must_use]
499 pub const fn linear_span(&self) -> LinearSpan {
500 self.linear_span.0
501 }
502
503 #[inline]
505 #[must_use]
506 pub const fn contains_direct_eval(&self) -> bool {
507 self.contains_direct_eval
508 }
509}
510
511impl ToIndentedString for ObjectMethodDefinition {
512 fn to_indented_string(&self, interner: &Interner, indent_n: usize) -> String {
513 let indentation = " ".repeat(indent_n + 1);
514 let prefix = match &self.kind {
515 MethodDefinitionKind::Get => "get ",
516 MethodDefinitionKind::Set => "set ",
517 MethodDefinitionKind::Ordinary => "",
518 MethodDefinitionKind::Generator => "*",
519 MethodDefinitionKind::AsyncGenerator => "async *",
520 MethodDefinitionKind::Async => "async ",
521 };
522 let name = self.name.to_interned_string(interner);
523 let parameters = join_nodes(interner, self.parameters.as_ref());
524 let body = block_to_string(&self.body.statements, interner, indent_n + 1);
525 format!("{indentation}{prefix}{name}({parameters}) {body},\n")
526 }
527}
528
529impl VisitWith for ObjectMethodDefinition {
530 fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
531 where
532 V: Visitor<'a>,
533 {
534 visitor.visit_property_name(&self.name)?;
535 visitor.visit_formal_parameter_list(&self.parameters)?;
536 visitor.visit_function_body(&self.body)
537 }
538
539 fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
540 where
541 V: VisitorMut<'a>,
542 {
543 visitor.visit_property_name_mut(&mut self.name)?;
544 visitor.visit_formal_parameter_list_mut(&mut self.parameters)?;
545 visitor.visit_function_body_mut(&mut self.body)
546 }
547}