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}