boa/syntax/ast/node/
mod.rs

1//! This module implements the `Node` structure, which composes the AST.
2
3pub mod array;
4pub mod await_expr;
5pub mod block;
6pub mod break_node;
7pub mod call;
8pub mod conditional;
9pub mod declaration;
10pub mod field;
11pub mod identifier;
12pub mod iteration;
13pub mod new;
14pub mod object;
15pub mod operator;
16pub mod return_smt;
17pub mod spread;
18pub mod statement_list;
19pub mod switch;
20pub mod template;
21pub mod throw;
22pub mod try_node;
23
24pub use self::{
25    array::ArrayDecl,
26    await_expr::AwaitExpr,
27    block::Block,
28    break_node::Break,
29    call::Call,
30    conditional::{ConditionalOp, If},
31    declaration::{
32        ArrowFunctionDecl, AsyncFunctionDecl, AsyncFunctionExpr, Declaration, DeclarationList,
33        FunctionDecl, FunctionExpr,
34    },
35    field::{GetConstField, GetField},
36    identifier::Identifier,
37    iteration::{Continue, DoWhileLoop, ForInLoop, ForLoop, ForOfLoop, WhileLoop},
38    new::New,
39    object::Object,
40    operator::{Assign, BinOp, UnaryOp},
41    return_smt::Return,
42    spread::Spread,
43    statement_list::{RcStatementList, StatementList},
44    switch::{Case, Switch},
45    template::{TaggedTemplate, TemplateLit},
46    throw::Throw,
47    try_node::{Catch, Finally, Try},
48};
49use super::Const;
50use crate::{
51    exec::Executable,
52    gc::{empty_trace, Finalize, Trace},
53    BoaProfiler, Context, JsResult, JsValue,
54};
55use std::{
56    cmp::Ordering,
57    fmt::{self, Display},
58};
59
60#[cfg(feature = "deser")]
61use serde::{Deserialize, Serialize};
62
63// TODO: This should be split into Expression and Statement.
64#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
65#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
66pub enum Node {
67    /// Array declaration node. [More information](./array/struct.ArrayDecl.html).
68    ArrayDecl(ArrayDecl),
69
70    /// An arrow function expression node. [More information](./arrow_function/struct.ArrowFunctionDecl.html).
71    ArrowFunctionDecl(ArrowFunctionDecl),
72
73    /// An assignment operator node. [More information](./operator/struct.Assign.html).
74    Assign(Assign),
75
76    /// An async function declaration node. [More information](./declaration/struct.AsyncFunctionDecl.html).
77    AsyncFunctionDecl(AsyncFunctionDecl),
78
79    /// An async function expression node. [More information](./declaration/struct.AsyncFunctionExpr.html).
80    AsyncFunctionExpr(AsyncFunctionExpr),
81
82    /// An await expression node. [More information](./await_expr/struct.AwaitExpression.html).
83    AwaitExpr(AwaitExpr),
84
85    /// A binary operator node. [More information](./operator/struct.BinOp.html).
86    BinOp(BinOp),
87
88    /// A Block node. [More information](./block/struct.Block.html).
89    Block(Block),
90
91    /// A break node. [More information](./break/struct.Break.html).
92    Break(Break),
93
94    /// A function call. [More information](./expression/struct.Call.html).
95    Call(Call),
96
97    /// A javascript conditional operand ( x ? y : z ). [More information](./conditional/struct.ConditionalOp.html).
98    ConditionalOp(ConditionalOp),
99
100    /// Literals represent values in JavaScript.
101    ///
102    /// These are fixed values not variables that you literally provide in your script.
103    ///
104    /// More information:
105    ///  - [ECMAScript reference][spec]
106    ///  - [MDN documentation][mdn]
107    ///
108    /// [spec]: https://tc39.es/ecma262/#sec-primary-expression-literals
109    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals
110    Const(Const),
111
112    /// A constant declaration list. [More information](./declaration/enum.DeclarationList.html#variant.Const).
113    ConstDeclList(DeclarationList),
114
115    /// A continue statement. [More information](./iteration/struct.Continue.html).
116    Continue(Continue),
117
118    /// A do ... while statement. [More information](./iteration/struct.DoWhileLoop.html).
119    DoWhileLoop(DoWhileLoop),
120
121    /// A function declaration node. [More information](./declaration/struct.FunctionDecl.html).
122    FunctionDecl(FunctionDecl),
123
124    /// A function expression node. [More information](./declaration/struct.FunctionExpr.html).
125    FunctionExpr(FunctionExpr),
126
127    /// Provides access to an object types' constant properties. [More information](./declaration/struct.GetConstField.html).
128    GetConstField(GetConstField),
129
130    /// Provides access to object fields. [More information](./declaration/struct.GetField.html).
131    GetField(GetField),
132
133    /// A `for` statement. [More information](./iteration/struct.ForLoop.html).
134    ForLoop(ForLoop),
135
136    /// A `for...of` or `for..in` statement. [More information](./iteration/struct.ForIn.html).
137    ForInLoop(ForInLoop),
138
139    /// A `for...of` statement. [More information](./iteration/struct.ForOf.html).
140    ForOfLoop(ForOfLoop),
141
142    /// An 'if' statement. [More information](./conditional/struct.If.html).
143    If(If),
144
145    /// A `let` declaration list. [More information](./declaration/enum.DeclarationList.html#variant.Let).
146    LetDeclList(DeclarationList),
147
148    /// A local identifier node. [More information](./identifier/struct.Identifier.html).
149    Identifier(Identifier),
150
151    /// A `new` expression. [More information](./expression/struct.New.html).
152    New(New),
153
154    /// An object. [More information](./object/struct.Object.html).
155    Object(Object),
156
157    /// A return statement. [More information](./object/struct.Return.html).
158    Return(Return),
159
160    /// A switch {case} statement. [More information](./switch/struct.Switch.html).
161    Switch(Switch),
162
163    /// A spread (...x) statement. [More information](./spread/struct.Spread.html).
164    Spread(Spread),
165
166    /// A tagged template. [More information](./template/struct.TaggedTemplate.html).
167    TaggedTemplate(TaggedTemplate),
168
169    /// A template literal. [More information](./template/struct.TemplateLit.html).
170    TemplateLit(TemplateLit),
171
172    /// A throw statement. [More information](./throw/struct.Throw.html).
173    Throw(Throw),
174
175    /// A `try...catch` node. [More information](./try_node/struct.Try.htl).
176    Try(Try),
177
178    /// The JavaScript `this` keyword refers to the object it belongs to.
179    ///
180    /// A property of an execution context (global, function or eval) that,
181    /// in non–strict mode, is always a reference to an object and in strict
182    /// mode can be any value.
183    ///
184    /// More information:
185    ///  - [ECMAScript reference][spec]
186    ///  - [MDN documentation][mdn]
187    ///
188    /// [spec]: https://tc39.es/ecma262/#sec-this-keyword
189    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
190    This,
191
192    /// Unary operation node. [More information](./operator/struct.UnaryOp.html)
193    UnaryOp(UnaryOp),
194
195    /// Array declaration node. [More information](./declaration/enum.DeclarationList.html#variant.Var).
196    VarDeclList(DeclarationList),
197
198    /// A 'while {...}' node. [More information](./iteration/struct.WhileLoop.html).
199    WhileLoop(WhileLoop),
200
201    /// A empty node.
202    ///
203    /// Empty statement do nothing, just return undefined.
204    ///
205    /// More information:
206    ///  - [ECMAScript reference][spec]
207    ///  - [MDN documentation][mdn]
208    ///
209    /// [spec]: https://tc39.es/ecma262/#prod-EmptyStatement
210    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/Empty
211    Empty,
212}
213
214impl Display for Node {
215    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216        self.display(f, 0)
217    }
218}
219
220impl From<Const> for Node {
221    fn from(c: Const) -> Self {
222        Self::Const(c)
223    }
224}
225
226impl Node {
227    /// Returns a node ordering based on the hoistability of each node.
228    pub(crate) fn hoistable_order(a: &Node, b: &Node) -> Ordering {
229        match (a, b) {
230            (Node::FunctionDecl(_), Node::FunctionDecl(_)) => Ordering::Equal,
231            (_, Node::FunctionDecl(_)) => Ordering::Greater,
232            (Node::FunctionDecl(_), _) => Ordering::Less,
233
234            (_, _) => Ordering::Equal,
235        }
236    }
237
238    /// Creates a `This` AST node.
239    pub fn this() -> Self {
240        Self::This
241    }
242
243    /// Displays the value of the node with the given indentation. For example, an indent
244    /// level of 2 would produce this:
245    ///
246    /// ```js
247    ///         function hello() {
248    ///             console.log("hello");
249    ///         }
250    ///         hello();
251    ///         a = 2;
252    /// ```
253    fn display(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result {
254        let indent = "    ".repeat(indentation);
255        match *self {
256            Self::Block(_) => {}
257            _ => write!(f, "{}", indent)?,
258        }
259        self.display_no_indent(f, indentation)
260    }
261
262    /// Implements the display formatting with indentation. This will not prefix the value with
263    /// any indentation. If you want to prefix this with proper indents, use [`display`](Self::display).
264    fn display_no_indent(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result {
265        match *self {
266            Self::Call(ref expr) => Display::fmt(expr, f),
267            Self::Const(ref c) => write!(f, "{}", c),
268            Self::ConditionalOp(ref cond_op) => Display::fmt(cond_op, f),
269            Self::ForLoop(ref for_loop) => for_loop.display(f, indentation),
270            Self::ForOfLoop(ref for_of) => for_of.display(f, indentation),
271            Self::ForInLoop(ref for_in) => for_in.display(f, indentation),
272            Self::This => write!(f, "this"),
273            Self::Try(ref try_catch) => try_catch.display(f, indentation),
274            Self::Break(ref break_smt) => Display::fmt(break_smt, f),
275            Self::Continue(ref cont) => Display::fmt(cont, f),
276            Self::Spread(ref spread) => Display::fmt(spread, f),
277            Self::Block(ref block) => block.display(f, indentation),
278            Self::Identifier(ref s) => Display::fmt(s, f),
279            Self::New(ref expr) => Display::fmt(expr, f),
280            Self::GetConstField(ref get_const_field) => Display::fmt(get_const_field, f),
281            Self::GetField(ref get_field) => Display::fmt(get_field, f),
282            Self::WhileLoop(ref while_loop) => while_loop.display(f, indentation),
283            Self::DoWhileLoop(ref do_while) => do_while.display(f, indentation),
284            Self::If(ref if_smt) => if_smt.display(f, indentation),
285            Self::Switch(ref switch) => switch.display(f, indentation),
286            Self::Object(ref obj) => obj.display(f, indentation),
287            Self::ArrayDecl(ref arr) => Display::fmt(arr, f),
288            Self::VarDeclList(ref list) => Display::fmt(list, f),
289            Self::FunctionDecl(ref decl) => decl.display(f, indentation),
290            Self::FunctionExpr(ref expr) => expr.display(f, indentation),
291            Self::ArrowFunctionDecl(ref decl) => decl.display(f, indentation),
292            Self::BinOp(ref op) => Display::fmt(op, f),
293            Self::UnaryOp(ref op) => Display::fmt(op, f),
294            Self::Return(ref ret) => Display::fmt(ret, f),
295            Self::TaggedTemplate(ref template) => Display::fmt(template, f),
296            Self::TemplateLit(ref template) => Display::fmt(template, f),
297            Self::Throw(ref throw) => Display::fmt(throw, f),
298            Self::Assign(ref op) => Display::fmt(op, f),
299            Self::LetDeclList(ref decl) => Display::fmt(decl, f),
300            Self::ConstDeclList(ref decl) => Display::fmt(decl, f),
301            Self::AsyncFunctionDecl(ref decl) => decl.display(f, indentation),
302            Self::AsyncFunctionExpr(ref expr) => expr.display(f, indentation),
303            Self::AwaitExpr(ref expr) => Display::fmt(expr, f),
304            Self::Empty => write!(f, ";"),
305        }
306    }
307}
308
309impl Executable for Node {
310    fn run(&self, context: &mut Context) -> JsResult<JsValue> {
311        let _timer = BoaProfiler::global().start_event("Executable", "exec");
312        match *self {
313            Node::AsyncFunctionDecl(ref decl) => decl.run(context),
314            Node::AsyncFunctionExpr(ref function_expr) => function_expr.run(context),
315            Node::AwaitExpr(ref expr) => expr.run(context),
316            Node::Call(ref call) => call.run(context),
317            Node::Const(Const::Null) => Ok(JsValue::null()),
318            Node::Const(Const::Num(num)) => Ok(JsValue::new(num)),
319            Node::Const(Const::Int(num)) => Ok(JsValue::new(num)),
320            Node::Const(Const::BigInt(ref num)) => Ok(JsValue::new(num.clone())),
321            Node::Const(Const::Undefined) => Ok(JsValue::undefined()),
322            // we can't move String from Const into value, because const is a garbage collected value
323            // Which means Drop() get's called on Const, but str will be gone at that point.
324            // Do Const values need to be garbage collected? We no longer need them once we've generated Values
325            Node::Const(Const::String(ref value)) => Ok(JsValue::new(value.to_string())),
326            Node::Const(Const::Bool(value)) => Ok(JsValue::new(value)),
327            Node::Block(ref block) => block.run(context),
328            Node::Identifier(ref identifier) => identifier.run(context),
329            Node::GetConstField(ref get_const_field_node) => get_const_field_node.run(context),
330            Node::GetField(ref get_field) => get_field.run(context),
331            Node::WhileLoop(ref while_loop) => while_loop.run(context),
332            Node::DoWhileLoop(ref do_while) => do_while.run(context),
333            Node::ForLoop(ref for_loop) => for_loop.run(context),
334            Node::ForOfLoop(ref for_of_loop) => for_of_loop.run(context),
335            Node::ForInLoop(ref for_in_loop) => for_in_loop.run(context),
336            Node::If(ref if_smt) => if_smt.run(context),
337            Node::ConditionalOp(ref op) => op.run(context),
338            Node::Switch(ref switch) => switch.run(context),
339            Node::Object(ref obj) => obj.run(context),
340            Node::ArrayDecl(ref arr) => arr.run(context),
341            // <https://tc39.es/ecma262/#sec-createdynamicfunction>
342            Node::FunctionDecl(ref decl) => decl.run(context),
343            // <https://tc39.es/ecma262/#sec-createdynamicfunction>
344            Node::FunctionExpr(ref function_expr) => function_expr.run(context),
345            Node::ArrowFunctionDecl(ref decl) => decl.run(context),
346            Node::BinOp(ref op) => op.run(context),
347            Node::UnaryOp(ref op) => op.run(context),
348            Node::New(ref call) => call.run(context),
349            Node::Return(ref ret) => ret.run(context),
350            Node::TaggedTemplate(ref template) => template.run(context),
351            Node::TemplateLit(ref template) => template.run(context),
352            Node::Throw(ref throw) => throw.run(context),
353            Node::Assign(ref op) => op.run(context),
354            Node::VarDeclList(ref decl) => decl.run(context),
355            Node::LetDeclList(ref decl) => decl.run(context),
356            Node::ConstDeclList(ref decl) => decl.run(context),
357            Node::Spread(ref spread) => spread.run(context),
358            Node::This => {
359                // Will either return `this` binding or undefined
360                context.get_this_binding()
361            }
362            Node::Try(ref try_node) => try_node.run(context),
363            Node::Break(ref break_node) => break_node.run(context),
364            Node::Continue(ref continue_node) => continue_node.run(context),
365            Node::Empty => Ok(JsValue::undefined()),
366        }
367    }
368}
369
370/// Utility to join multiple Nodes into a single string.
371fn join_nodes<N>(f: &mut fmt::Formatter<'_>, nodes: &[N]) -> fmt::Result
372where
373    N: Display,
374{
375    let mut first = true;
376    for e in nodes {
377        if !first {
378            f.write_str(", ")?;
379        }
380        first = false;
381        Display::fmt(e, f)?;
382    }
383    Ok(())
384}
385
386/// "Formal parameter" is a fancy way of saying "function parameter".
387///
388/// In the declaration of a function, the parameters must be identifiers,
389/// not any value like numbers, strings, or objects.
390///```text
391///function foo(formalParameter1, formalParameter2) {
392///}
393///```
394///
395/// More information:
396///  - [ECMAScript reference][spec]
397///  - [MDN documentation][mdn]
398///
399/// [spec]: https://tc39.es/ecma262/#prod-FormalParameter
400/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Missing_formal_parameter
401#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
402#[derive(Clone, Debug, PartialEq, Trace, Finalize)]
403pub struct FormalParameter {
404    name: Box<str>,
405    init: Option<Node>,
406    is_rest_param: bool,
407}
408
409impl FormalParameter {
410    /// Creates a new formal parameter.
411    pub(in crate::syntax) fn new<N>(name: N, init: Option<Node>, is_rest_param: bool) -> Self
412    where
413        N: Into<Box<str>>,
414    {
415        Self {
416            name: name.into(),
417            init,
418            is_rest_param,
419        }
420    }
421
422    /// Gets the name of the formal parameter.
423    pub fn name(&self) -> &str {
424        &self.name
425    }
426
427    /// Gets the initialization node of the formal parameter, if any.
428    pub fn init(&self) -> Option<&Node> {
429        self.init.as_ref()
430    }
431
432    /// Gets wether the parameter is a rest parameter.
433    pub fn is_rest_param(&self) -> bool {
434        self.is_rest_param
435    }
436}
437
438impl Display for FormalParameter {
439    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
440        if self.is_rest_param {
441            write!(f, "...")?;
442        }
443        write!(f, "{}", self.name)?;
444        if let Some(n) = self.init.as_ref() {
445            write!(f, " = {}", n)?;
446        }
447        Ok(())
448    }
449}
450
451/// A JavaScript property is a characteristic of an object, often describing attributes associated with a data structure.
452///
453/// A property has a name (a string) and a value (primitive, method, or object reference).
454/// Note that when we say that "a property holds an object", that is shorthand for "a property holds an object reference".
455/// This distinction matters because the original referenced object remains unchanged when you change the property's value.
456///
457/// More information:
458///  - [ECMAScript reference][spec]
459///  - [MDN documentation][mdn]
460///
461/// [spec]: https://tc39.es/ecma262/#prod-PropertyDefinition
462/// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/property/JavaScript
463// TODO: Support all features: https://tc39.es/ecma262/#prod-PropertyDefinition
464#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
465#[derive(Clone, Debug, PartialEq, Trace, Finalize)]
466pub enum PropertyDefinition {
467    /// Puts a variable into an object.
468    ///
469    /// More information:
470    ///  - [ECMAScript reference][spec]
471    ///  - [MDN documentation][mdn]
472    ///
473    /// [spec]: https://tc39.es/ecma262/#prod-IdentifierReference
474    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Property_definitions
475    IdentifierReference(Box<str>),
476
477    /// Binds a property name to a JavaScript value.
478    ///
479    /// More information:
480    ///  - [ECMAScript reference][spec]
481    ///  - [MDN documentation][mdn]
482    ///
483    /// [spec]: https://tc39.es/ecma262/#prod-PropertyDefinition
484    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Property_definitions
485    Property(PropertyName, Node),
486
487    /// A property of an object can also refer to a function or a getter or setter method.
488    ///
489    /// More information:
490    ///  - [ECMAScript reference][spec]
491    ///  - [MDN documentation][mdn]
492    ///
493    /// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition
494    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Method_definitions
495    MethodDefinition(MethodDefinitionKind, PropertyName, FunctionExpr),
496
497    /// The Rest/Spread Properties for ECMAScript proposal (stage 4) adds spread properties to object literals.
498    /// It copies own enumerable properties from a provided object onto a new object.
499    ///
500    /// Shallow-cloning (excluding `prototype`) or merging objects is now possible using a shorter syntax than `Object.assign()`.
501    ///
502    /// More information:
503    ///  - [ECMAScript reference][spec]
504    ///  - [MDN documentation][mdn]
505    ///
506    /// [spec]: https://tc39.es/ecma262/#prod-PropertyDefinition
507    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Spread_properties
508    SpreadObject(Node),
509}
510
511impl PropertyDefinition {
512    /// Creates an `IdentifierReference` property definition.
513    pub fn identifier_reference<I>(ident: I) -> Self
514    where
515        I: Into<Box<str>>,
516    {
517        Self::IdentifierReference(ident.into())
518    }
519
520    /// Creates a `Property` definition.
521    pub fn property<N, V>(name: N, value: V) -> Self
522    where
523        N: Into<PropertyName>,
524        V: Into<Node>,
525    {
526        Self::Property(name.into(), value.into())
527    }
528
529    /// Creates a `MethodDefinition`.
530    pub fn method_definition<N>(kind: MethodDefinitionKind, name: N, body: FunctionExpr) -> Self
531    where
532        N: Into<PropertyName>,
533    {
534        Self::MethodDefinition(kind, name.into(), body)
535    }
536
537    /// Creates a `SpreadObject`.
538    pub fn spread_object<O>(obj: O) -> Self
539    where
540        O: Into<Node>,
541    {
542        Self::SpreadObject(obj.into())
543    }
544}
545
546/// Method definition kinds.
547///
548/// Starting with ECMAScript 2015, a shorter syntax for method definitions on objects initializers is introduced.
549/// It is a shorthand for a function assigned to the method's name.
550///
551/// More information:
552///  - [ECMAScript reference][spec]
553///  - [MDN documentation][mdn]
554///
555/// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition
556/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions
557#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
558#[derive(Clone, Debug, PartialEq, Copy, Finalize)]
559pub enum MethodDefinitionKind {
560    /// The `get` syntax binds an object property to a function that will be called when that property is looked up.
561    ///
562    /// Sometimes it is desirable to allow access to a property that returns a dynamically computed value,
563    /// or you may want to reflect the status of an internal variable without requiring the use of explicit method calls.
564    /// In JavaScript, this can be accomplished with the use of a getter.
565    ///
566    /// It is not possible to simultaneously have a getter bound to a property and have that property actually hold a value,
567    /// although it is possible to use a getter and a setter in conjunction to create a type of pseudo-property.
568    ///
569    /// More information:
570    ///  - [ECMAScript reference][spec]
571    ///  - [MDN documentation][mdn]
572    ///
573    /// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition
574    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get
575    Get,
576
577    /// The `set` syntax binds an object property to a function to be called when there is an attempt to set that property.
578    ///
579    /// In JavaScript, a setter can be used to execute a function whenever a specified property is attempted to be changed.
580    /// Setters are most often used in conjunction with getters to create a type of pseudo-property.
581    /// It is not possible to simultaneously have a setter on a property that holds an actual value.
582    ///
583    /// More information:
584    ///  - [ECMAScript reference][spec]
585    ///  - [MDN documentation][mdn]
586    ///
587    /// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition
588    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set
589    Set,
590
591    /// Starting with ECMAScript 2015, you are able to define own methods in a shorter syntax, similar to the getters and setters.
592    ///
593    /// More information:
594    ///  - [ECMAScript reference][spec]
595    ///  - [MDN documentation][mdn]
596    ///
597    /// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition
598    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Method_definition_syntax
599    Ordinary,
600    // TODO: support other method definition kinds, like `Generator`.
601}
602
603unsafe impl Trace for MethodDefinitionKind {
604    empty_trace!();
605}
606
607/// PropertyName can be either a literal or computed.
608///
609/// More information:
610///  - [ECMAScript reference][spec]
611///
612/// [spec]: https://tc39.es/ecma262/#prod-PropertyName
613#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
614#[derive(Clone, Debug, PartialEq, Finalize)]
615pub enum PropertyName {
616    /// A `Literal` property name can be either an identifier, a string or a numeric literal.
617    ///
618    /// More information:
619    ///  - [ECMAScript reference][spec]
620    ///
621    /// [spec]: https://tc39.es/ecma262/#prod-LiteralPropertyName
622    Literal(Box<str>),
623    /// A `Computed` property name is an expression that gets evaluated and converted into a property name.
624    ///
625    /// More information:
626    ///  - [ECMAScript reference][spec]
627    ///
628    /// [spec]: https://tc39.es/ecma262/#prod-ComputedPropertyName
629    Computed(Node),
630}
631
632impl Display for PropertyName {
633    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
634        match self {
635            PropertyName::Literal(key) => write!(f, "{}", key),
636            PropertyName::Computed(key) => write!(f, "{}", key),
637        }
638    }
639}
640
641impl<T> From<T> for PropertyName
642where
643    T: Into<Box<str>>,
644{
645    fn from(name: T) -> Self {
646        Self::Literal(name.into())
647    }
648}
649
650impl From<Node> for PropertyName {
651    fn from(name: Node) -> Self {
652        Self::Computed(name)
653    }
654}
655
656unsafe impl Trace for PropertyName {
657    empty_trace!();
658}
659
660/// This parses the given source code, and then makes sure that
661/// the resulting StatementList is formatted in the same manner
662/// as the source code. This is expected to have a preceding
663/// newline.
664///
665/// This is a utility function for tests. It was made in case people
666/// are using different indents in their source files. This fixes
667/// any strings which may have been changed in a different indent
668/// level.
669#[cfg(test)]
670fn test_formatting(source: &'static str) {
671    // Remove preceding newline.
672    let source = &source[1..];
673
674    // Find out how much the code is indented
675    let first_line = &source[..source.find('\n').unwrap()];
676    let trimmed_first_line = first_line.trim();
677    let characters_to_remove = first_line.len() - trimmed_first_line.len();
678
679    let scenario = source
680        .lines()
681        .map(|l| &l[characters_to_remove..]) // Remove preceding whitespace from each line
682        .collect::<Vec<&'static str>>()
683        .join("\n");
684    let result = format!("{}", crate::parse(&scenario, false).unwrap());
685    if scenario != result {
686        eprint!("========= Expected:\n{}", scenario);
687        eprint!("========= Got:\n{}", result);
688        // Might be helpful to find differing whitespace
689        eprintln!("========= Expected: {:?}", scenario);
690        eprintln!("========= Got:      {:?}", result);
691        panic!("parsing test did not give the correct result (see above)");
692    }
693}