//! This module implements the `Node` structure, which composes the AST. pub mod array; pub mod await_expr; pub mod block; pub mod break_node; pub mod call; pub mod conditional; pub mod declaration; pub mod field; pub mod identifier; pub mod iteration; pub mod new; pub mod object; pub mod operator; pub mod return_smt; pub mod spread; pub mod statement_list; pub mod switch; pub mod throw; pub mod try_node; pub use self::{ array::ArrayDecl, await_expr::AwaitExpr, block::Block, break_node::Break, call::Call, conditional::{ConditionalOp, If}, declaration::{ ArrowFunctionDecl, AsyncFunctionDecl, AsyncFunctionExpr, ConstDecl, ConstDeclList, FunctionDecl, FunctionExpr, LetDecl, LetDeclList, VarDecl, VarDeclList, }, field::{GetConstField, GetField}, identifier::Identifier, iteration::{Continue, DoWhileLoop, ForInLoop, ForLoop, ForOfLoop, WhileLoop}, new::New, object::Object, operator::{Assign, BinOp, UnaryOp}, return_smt::Return, spread::Spread, statement_list::{RcStatementList, StatementList}, switch::{Case, Switch}, throw::Throw, try_node::{Catch, Finally, Try}, }; use super::Const; use crate::{ exec::Executable, gc::{empty_trace, Finalize, Trace}, BoaProfiler, Context, Result, Value, }; use std::{ cmp::Ordering, fmt::{self, Display}, }; #[cfg(feature = "deser")] use serde::{Deserialize, Serialize}; #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub enum Node { /// Array declaration node. [More information](./array/struct.ArrayDecl.html). ArrayDecl(ArrayDecl), /// An arrow function expression node. [More information](./arrow_function/struct.ArrowFunctionDecl.html). ArrowFunctionDecl(ArrowFunctionDecl), /// An assignment operator node. [More information](./operator/struct.Assign.html). Assign(Assign), /// An async function declaration node. [More information](./declaration/struct.AsyncFunctionDecl.html). AsyncFunctionDecl(AsyncFunctionDecl), /// An async function expression node. [More information](./declaration/struct.AsyncFunctionExpr.html). AsyncFunctionExpr(AsyncFunctionExpr), /// An await expression node. [More information](./await_expr/struct.AwaitExpression.html). AwaitExpr(AwaitExpr), /// A binary operator node. [More information](./operator/struct.BinOp.html). BinOp(BinOp), /// A Block node. [More information](./block/struct.Block.html). Block(Block), /// A break node. [More information](./break/struct.Break.html). Break(Break), /// A function call. [More information](./expression/struct.Call.html). Call(Call), /// A javascript conditional operand ( x ? y : z ). [More information](./conditional/struct.ConditionalOp.html). ConditionalOp(ConditionalOp), /// Literals represent values in JavaScript. /// /// These are fixed values not variables that you literally provide in your script. /// /// More information: /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// /// [spec]: https://tc39.es/ecma262/#sec-primary-expression-literals /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals Const(Const), /// A constant declaration list. [More information](./declaration/struct.ConstDeclList.html). ConstDeclList(ConstDeclList), /// A continue statement. [More information](./iteration/struct.Continue.html). Continue(Continue), /// A do ... while statement. [More information](./iteration/struct.DoWhileLoop.html). DoWhileLoop(DoWhileLoop), /// A function declaration node. [More information](./declaration/struct.FunctionDecl.html). FunctionDecl(FunctionDecl), /// A function expression node. [More information](./declaration/struct.FunctionExpr.html). FunctionExpr(FunctionExpr), /// Provides access to an object types' constant properties. [More information](./declaration/struct.GetConstField.html). GetConstField(GetConstField), /// Provides access to object fields. [More information](./declaration/struct.GetField.html). GetField(GetField), /// A `for` statement. [More information](./iteration/struct.ForLoop.html). ForLoop(ForLoop), /// A `for...of` or `for..in` statement. [More information](./iteration/struct.ForIn.html). ForInLoop(ForInLoop), /// A `for...of` statement. [More information](./iteration/struct.ForOf.html). ForOfLoop(ForOfLoop), /// An 'if' statement. [More information](./conditional/struct.If.html). If(If), /// A `let` declaration list. [More information](./declaration/struct.LetDeclList.html). LetDeclList(LetDeclList), /// A local identifier node. [More information](./identifier/struct.Identifier.html). Identifier(Identifier), /// A `new` expression. [More information](./expression/struct.New.html). New(New), /// An object. [More information](./object/struct.Object.html). Object(Object), /// A return statement. [More information](./object/struct.Return.html). Return(Return), /// A switch {case} statement. [More information](./switch/struct.Switch.html). Switch(Switch), /// A spread (...x) statement. [More information](./spread/struct.Spread.html). Spread(Spread), /// A throw statement. [More information](./throw/struct.Throw.html). Throw(Throw), /// A `try...catch` node. [More information](./try_node/struct.Try.htl). Try(Try), /// The JavaScript `this` keyword refers to the object it belongs to. /// /// A property of an execution context (global, function or eval) that, /// in non–strict mode, is always a reference to an object and in strict /// mode can be any value. /// /// More information: /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// /// [spec]: https://tc39.es/ecma262/#sec-this-keyword /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this This, /// Unary operation node. [More information](./operator/struct.UnaryOp.html) UnaryOp(UnaryOp), /// Array declaration node. [More information](./declaration/struct.VarDeclList.html). VarDeclList(VarDeclList), /// A 'while {...}' node. [More information](./iteration/struct.WhileLoop.html). WhileLoop(WhileLoop), } impl Display for Node { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.display(f, 0) } } impl From for Node { fn from(c: Const) -> Self { Self::Const(c) } } impl Node { /// Returns a node ordering based on the hoistability of each node. pub(crate) fn hoistable_order(a: &Node, b: &Node) -> Ordering { match (a, b) { (Node::FunctionDecl(_), Node::FunctionDecl(_)) => Ordering::Equal, (_, Node::FunctionDecl(_)) => Ordering::Greater, (Node::FunctionDecl(_), _) => Ordering::Less, (_, _) => Ordering::Equal, } } /// Creates a `This` AST node. pub fn this() -> Self { Self::This } /// Implements the display formatting with indentation. fn display(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result { let indent = " ".repeat(indentation); match *self { Self::Block(_) => {} _ => write!(f, "{}", indent)?, } match *self { Self::Call(ref expr) => Display::fmt(expr, f), Self::Const(ref c) => write!(f, "{}", c), Self::ConditionalOp(ref cond_op) => Display::fmt(cond_op, f), Self::ForLoop(ref for_loop) => for_loop.display(f, indentation), Self::ForOfLoop(ref for_of) => for_of.display(f, indentation), Self::ForInLoop(ref for_in) => for_in.display(f, indentation), Self::This => write!(f, "this"), Self::Try(ref try_catch) => try_catch.display(f, indentation), Self::Break(ref break_smt) => Display::fmt(break_smt, f), Self::Continue(ref cont) => Display::fmt(cont, f), Self::Spread(ref spread) => Display::fmt(spread, f), Self::Block(ref block) => block.display(f, indentation), Self::Identifier(ref s) => Display::fmt(s, f), Self::New(ref expr) => Display::fmt(expr, f), Self::GetConstField(ref get_const_field) => Display::fmt(get_const_field, f), Self::GetField(ref get_field) => Display::fmt(get_field, f), Self::WhileLoop(ref while_loop) => while_loop.display(f, indentation), Self::DoWhileLoop(ref do_while) => do_while.display(f, indentation), Self::If(ref if_smt) => if_smt.display(f, indentation), Self::Switch(ref switch) => switch.display(f, indentation), Self::Object(ref obj) => obj.display(f, indentation), Self::ArrayDecl(ref arr) => Display::fmt(arr, f), Self::VarDeclList(ref list) => Display::fmt(list, f), Self::FunctionDecl(ref decl) => decl.display(f, indentation), Self::FunctionExpr(ref expr) => expr.display(f, indentation), Self::ArrowFunctionDecl(ref decl) => decl.display(f, indentation), Self::BinOp(ref op) => Display::fmt(op, f), Self::UnaryOp(ref op) => Display::fmt(op, f), Self::Return(ref ret) => Display::fmt(ret, f), Self::Throw(ref throw) => Display::fmt(throw, f), Self::Assign(ref op) => Display::fmt(op, f), Self::LetDeclList(ref decl) => Display::fmt(decl, f), Self::ConstDeclList(ref decl) => Display::fmt(decl, f), Self::AsyncFunctionDecl(ref decl) => decl.display(f, indentation), Self::AsyncFunctionExpr(ref expr) => expr.display(f, indentation), Self::AwaitExpr(ref expr) => expr.display(f, indentation), } } } impl Executable for Node { fn run(&self, context: &mut Context) -> Result { let _timer = BoaProfiler::global().start_event("Executable", "exec"); match *self { Node::AsyncFunctionDecl(ref decl) => decl.run(context), Node::AsyncFunctionExpr(ref function_expr) => function_expr.run(context), Node::AwaitExpr(ref expr) => expr.run(context), Node::Call(ref call) => call.run(context), Node::Const(Const::Null) => Ok(Value::null()), Node::Const(Const::Num(num)) => Ok(Value::rational(num)), Node::Const(Const::Int(num)) => Ok(Value::integer(num)), Node::Const(Const::BigInt(ref num)) => Ok(Value::from(num.clone())), Node::Const(Const::Undefined) => Ok(Value::Undefined), // we can't move String from Const into value, because const is a garbage collected value // Which means Drop() get's called on Const, but str will be gone at that point. // Do Const values need to be garbage collected? We no longer need them once we've generated Values Node::Const(Const::String(ref value)) => Ok(Value::string(value.to_string())), Node::Const(Const::Bool(value)) => Ok(Value::boolean(value)), Node::Block(ref block) => block.run(context), Node::Identifier(ref identifier) => identifier.run(context), Node::GetConstField(ref get_const_field_node) => get_const_field_node.run(context), Node::GetField(ref get_field) => get_field.run(context), Node::WhileLoop(ref while_loop) => while_loop.run(context), Node::DoWhileLoop(ref do_while) => do_while.run(context), Node::ForLoop(ref for_loop) => for_loop.run(context), Node::ForOfLoop(ref for_of_loop) => for_of_loop.run(context), Node::ForInLoop(ref for_in_loop) => for_in_loop.run(context), Node::If(ref if_smt) => if_smt.run(context), Node::ConditionalOp(ref op) => op.run(context), Node::Switch(ref switch) => switch.run(context), Node::Object(ref obj) => obj.run(context), Node::ArrayDecl(ref arr) => arr.run(context), // Node::FunctionDecl(ref decl) => decl.run(context), // Node::FunctionExpr(ref function_expr) => function_expr.run(context), Node::ArrowFunctionDecl(ref decl) => decl.run(context), Node::BinOp(ref op) => op.run(context), Node::UnaryOp(ref op) => op.run(context), Node::New(ref call) => call.run(context), Node::Return(ref ret) => ret.run(context), Node::Throw(ref throw) => throw.run(context), Node::Assign(ref op) => op.run(context), Node::VarDeclList(ref decl) => decl.run(context), Node::LetDeclList(ref decl) => decl.run(context), Node::ConstDeclList(ref decl) => decl.run(context), Node::Spread(ref spread) => spread.run(context), Node::This => { // Will either return `this` binding or undefined context .realm() .environment .get_this_binding() .map_err(|e| e.to_error(context)) } Node::Try(ref try_node) => try_node.run(context), Node::Break(ref break_node) => break_node.run(context), Node::Continue(ref continue_node) => continue_node.run(context), } } } /// Utility to join multiple Nodes into a single string. fn join_nodes(f: &mut fmt::Formatter<'_>, nodes: &[N]) -> fmt::Result where N: Display, { let mut first = true; for e in nodes { if !first { f.write_str(", ")?; } first = false; Display::fmt(e, f)?; } Ok(()) } /// "Formal parameter" is a fancy way of saying "function parameter". /// /// In the declaration of a function, the parameters must be identifiers, /// not any value like numbers, strings, or objects. ///```text ///function foo(formalParameter1, formalParameter2) { ///} ///``` /// /// More information: /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// /// [spec]: https://tc39.es/ecma262/#prod-FormalParameter /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Missing_formal_parameter #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] #[derive(Clone, Debug, PartialEq, Trace, Finalize)] pub struct FormalParameter { name: Box, init: Option, is_rest_param: bool, } impl FormalParameter { /// Creates a new formal parameter. pub(in crate::syntax) fn new(name: N, init: Option, is_rest_param: bool) -> Self where N: Into>, { Self { name: name.into(), init, is_rest_param, } } /// Gets the name of the formal parameter. pub fn name(&self) -> &str { &self.name } /// Gets the initialization node of the formal parameter, if any. pub fn init(&self) -> Option<&Node> { self.init.as_ref() } /// Gets wether the parameter is a rest parameter. pub fn is_rest_param(&self) -> bool { self.is_rest_param } } impl Display for FormalParameter { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.is_rest_param { write!(f, "...")?; } write!(f, "{}", self.name)?; if let Some(n) = self.init.as_ref() { write!(f, " = {}", n)?; } Ok(()) } } /// A JavaScript property is a characteristic of an object, often describing attributes associated with a data structure. /// /// A property has a name (a string) and a value (primitive, method, or object reference). /// Note that when we say that "a property holds an object", that is shorthand for "a property holds an object reference". /// This distinction matters because the original referenced object remains unchanged when you change the property's value. /// /// More information: /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// /// [spec]: https://tc39.es/ecma262/#prod-PropertyDefinition /// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/property/JavaScript // TODO: Support all features: https://tc39.es/ecma262/#prod-PropertyDefinition #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] #[derive(Clone, Debug, PartialEq, Trace, Finalize)] pub enum PropertyDefinition { /// Puts a variable into an object. /// /// More information: /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// /// [spec]: https://tc39.es/ecma262/#prod-IdentifierReference /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Property_definitions IdentifierReference(Box), /// Binds a property name to a JavaScript value. /// /// More information: /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// /// [spec]: https://tc39.es/ecma262/#prod-PropertyDefinition /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Property_definitions Property(Box, Node), /// A property of an object can also refer to a function or a getter or setter method. /// /// More information: /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// /// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Method_definitions MethodDefinition(MethodDefinitionKind, Box, FunctionExpr), /// The Rest/Spread Properties for ECMAScript proposal (stage 4) adds spread properties to object literals. /// It copies own enumerable properties from a provided object onto a new object. /// /// Shallow-cloning (excluding `prototype`) or merging objects is now possible using a shorter syntax than `Object.assign()`. /// /// More information: /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// /// [spec]: https://tc39.es/ecma262/#prod-PropertyDefinition /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Spread_properties SpreadObject(Node), } impl PropertyDefinition { /// Creates an `IdentifierReference` property definition. pub fn identifier_reference(ident: I) -> Self where I: Into>, { Self::IdentifierReference(ident.into()) } /// Creates a `Property` definition. pub fn property(name: N, value: V) -> Self where N: Into>, V: Into, { Self::Property(name.into(), value.into()) } /// Creates a `MethodDefinition`. pub fn method_definition(kind: MethodDefinitionKind, name: N, body: FunctionExpr) -> Self where N: Into>, { Self::MethodDefinition(kind, name.into(), body) } /// Creates a `SpreadObject`. pub fn spread_object(obj: O) -> Self where O: Into, { Self::SpreadObject(obj.into()) } } /// Method definition kinds. /// /// Starting with ECMAScript 2015, a shorter syntax for method definitions on objects initializers is introduced. /// It is a shorthand for a function assigned to the method's name. /// /// More information: /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// /// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] #[derive(Clone, Debug, PartialEq, Copy, Finalize)] pub enum MethodDefinitionKind { /// The `get` syntax binds an object property to a function that will be called when that property is looked up. /// /// Sometimes it is desirable to allow access to a property that returns a dynamically computed value, /// or you may want to reflect the status of an internal variable without requiring the use of explicit method calls. /// In JavaScript, this can be accomplished with the use of a getter. /// /// It is not possible to simultaneously have a getter bound to a property and have that property actually hold a value, /// although it is possible to use a getter and a setter in conjunction to create a type of pseudo-property. /// /// More information: /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// /// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get Get, /// The `set` syntax binds an object property to a function to be called when there is an attempt to set that property. /// /// In JavaScript, a setter can be used to execute a function whenever a specified property is attempted to be changed. /// Setters are most often used in conjunction with getters to create a type of pseudo-property. /// It is not possible to simultaneously have a setter on a property that holds an actual value. /// /// More information: /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// /// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set Set, /// Starting with ECMAScript 2015, you are able to define own methods in a shorter syntax, similar to the getters and setters. /// /// More information: /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// /// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Method_definition_syntax Ordinary, // TODO: support other method definition kinds, like `Generator`. } unsafe impl Trace for MethodDefinitionKind { empty_trace!(); }