diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs index fb9cc0ed66..81a765c78b 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -297,17 +297,42 @@ impl Executor for Interpreter { val.set_field_slice("length", to_value(args.len())); // Set the name and assign it in the current environment - if name.is_some() { - self.realm.environment.create_mutable_binding( - name.clone().expect("No name was supplied"), - false, - VariableScope::Function, - ); + val.set_field_slice("name", to_value(name.clone())); + self.realm.environment.create_mutable_binding( + name.clone(), + false, + VariableScope::Function, + ); + + self.realm.environment.initialize_binding(name, val.clone()); + + Ok(val) + } + // + Node::FunctionExpr(ref name, ref args, ref expr) => { + // Todo: Function.prototype doesn't exist yet, so the prototype right now is the Object.prototype + // let proto = &self + // .realm + // .environment + // .get_global_object() + // .expect("Could not get the global object") + // .get_field_slice("Object") + // .get_field_slice("Prototype"); + + let func = FunctionObject::create_ordinary( + args.clone(), // TODO: args shouldn't need to be a reference it should be passed by value + self.realm.environment.get_current_environment().clone(), + FunctionBody::Ordinary(*expr.clone()), + ThisMode::NonLexical, + ); + + let mut new_func = Object::function(); + new_func.set_call(func); + let val = to_value(new_func); + val.set_field_slice("length", to_value(args.len())); - self.realm.environment.initialize_binding( - name.as_ref().expect("Could not get name as reference"), - val.clone(), - ) + if let Some(name) = name { + val.set_field_slice("name", to_value(name.clone())); } Ok(val) diff --git a/boa/src/exec/tests.rs b/boa/src/exec/tests.rs index b91bb39de1..4adb74d93d 100644 --- a/boa/src/exec/tests.rs +++ b/boa/src/exec/tests.rs @@ -47,7 +47,7 @@ fn object_field_set() { m['key'] = 22; m['key'] "#; - assert_eq!(exec(scenario), String::from("22")); + assert_eq!(&exec(scenario), "22"); } #[test] @@ -98,28 +98,28 @@ fn array_field_set() { m[1] = 5; m[1] "#; - assert_eq!(exec(element_changes), String::from("5")); + assert_eq!(&exec(element_changes), "5"); let length_changes = r#" let m = [1, 2, 3]; m[10] = 52; m.length "#; - assert_eq!(exec(length_changes), String::from("11")); + assert_eq!(&exec(length_changes), "11"); let negative_index_wont_affect_length = r#" let m = [1, 2, 3]; m[-11] = 5; m.length "#; - assert_eq!(exec(negative_index_wont_affect_length), String::from("3")); + assert_eq!(&exec(negative_index_wont_affect_length), "3"); let non_num_key_wont_affect_length = r#" let m = [1, 2, 3]; m["magic"] = 5; m.length "#; - assert_eq!(exec(non_num_key_wont_affect_length), String::from("3")); + assert_eq!(&exec(non_num_key_wont_affect_length), "3"); } #[test] @@ -128,36 +128,36 @@ fn tilde_operator() { let f = -1.2; ~f "#; - assert_eq!(exec(float), String::from("0")); + assert_eq!(&exec(float), "0"); let numeric = r#" let f = 1789; ~f "#; - assert_eq!(exec(numeric), String::from("-1790")); + assert_eq!(&exec(numeric), "-1790"); // TODO: enable test after we have NaN // let nan = r#" // var m = NaN; // ~m // "#; - // assert_eq!(exec(nan), String::from("-1")); + // assert_eq!(&exec(nan), "-1"); let object = r#" let m = {}; ~m "#; - assert_eq!(exec(object), String::from("-1")); + assert_eq!(&exec(object), "-1"); let boolean_true = r#" ~true "#; - assert_eq!(exec(boolean_true), String::from("-2")); + assert_eq!(&exec(boolean_true), "-2"); let boolean_false = r#" ~false "#; - assert_eq!(exec(boolean_false), String::from("-1")); + assert_eq!(&exec(boolean_false), "-1"); } #[test] @@ -171,7 +171,7 @@ fn early_return() { } early_return() "#; - assert_eq!(exec(early_return), String::from("true")); + assert_eq!(&exec(early_return), "true"); let early_return = r#" function nested_fnct() { @@ -183,7 +183,7 @@ fn early_return() { } outer_fnct() "#; - assert_eq!(exec(early_return), String::from("outer")); + assert_eq!(&exec(early_return), "outer"); } #[test] @@ -204,7 +204,7 @@ fn short_circuit_evaluation() { let _ = add_one(counter) || add_one(counter); counter.value "#; - assert_eq!(exec(short_circuit_eval), String::from("1")); + assert_eq!(&exec(short_circuit_eval), "1"); // the second operand must be evaluated if the first one resolve to `false`. let short_circuit_eval = r#" @@ -216,7 +216,7 @@ fn short_circuit_evaluation() { let _ = add_one(counter) || add_one(counter); counter.value "#; - assert_eq!(exec(short_circuit_eval), String::from("2")); + assert_eq!(&exec(short_circuit_eval), "2"); // AND operation assert_eq!(exec("true && true"), String::from("true")); @@ -234,7 +234,7 @@ fn short_circuit_evaluation() { let _ = add_one(counter) && add_one(counter); counter.value "#; - assert_eq!(exec(short_circuit_eval), String::from("2")); + assert_eq!(&exec(short_circuit_eval), "2"); // the second operand must NOT be evaluated if the first one resolve to `false`. let short_circuit_eval = r#" @@ -246,7 +246,7 @@ fn short_circuit_evaluation() { let _ = add_one(counter) && add_one(counter); counter.value "#; - assert_eq!(exec(short_circuit_eval), String::from("1")); + assert_eq!(&exec(short_circuit_eval), "1"); } #[test] @@ -256,7 +256,7 @@ fn assign_operator_precedence() { a = a + 1; a "#; - assert_eq!(exec(src), String::from("2")); + assert_eq!(&exec(src), "2"); } #[test] @@ -269,7 +269,7 @@ fn do_while_loop() { a "#; - assert_eq!(exec(simple_one), String::from("10")); + assert_eq!(&exec(simple_one), "10"); let multiline_statement = r#" pow = 0; @@ -280,7 +280,7 @@ fn do_while_loop() { } while (pow < 8); b "#; - assert_eq!(exec(multiline_statement), String::from("256")); + assert_eq!(&exec(multiline_statement), "256"); let body_is_executed_at_least_once = r#" a = 0; @@ -291,7 +291,7 @@ fn do_while_loop() { while (false); a "#; - assert_eq!(exec(body_is_executed_at_least_once), String::from("1")); + assert_eq!(&exec(body_is_executed_at_least_once), "1"); } #[test] @@ -300,7 +300,7 @@ fn do_while_post_inc() { var i = 0; do {} while(i++ < 10) i; "#; - assert_eq!(exec(with_post_incrementors), String::from("11")); + assert_eq!(&exec(with_post_incrementors), "11"); } #[test] @@ -313,7 +313,7 @@ fn test_for_loop() { } b "#; - assert_eq!(exec(simple), String::from("hello")); + assert_eq!(&exec(simple), "hello"); let without_init_and_inc_step = r#" let a = 0; @@ -325,7 +325,7 @@ fn test_for_loop() { a "#; - assert_eq!(exec(without_init_and_inc_step), String::from("45")); + assert_eq!(&exec(without_init_and_inc_step), "45"); let body_should_not_execute_on_false_condition = r#" let a = 0 @@ -345,7 +345,7 @@ fn test_for_loop() { i "#; - assert_eq!(exec(inner_scope), String::from("undefined")); + assert_eq!(&exec(inner_scope), "undefined"); } #[test] @@ -355,40 +355,40 @@ fn unary_pre() { ++a; a; "#; - assert_eq!(exec(unary_inc), String::from("6")); + assert_eq!(&exec(unary_inc), "6"); let unary_dec = r#" let a = 5; --a; a; "#; - assert_eq!(exec(unary_dec), String::from("4")); + assert_eq!(&exec(unary_dec), "4"); let inc_obj_prop = r#" const a = { b: 5 }; ++a.b; a['b']; "#; - assert_eq!(exec(inc_obj_prop), String::from("6")); + assert_eq!(&exec(inc_obj_prop), "6"); let inc_obj_field = r#" const a = { b: 5 }; ++a['b']; a.b; "#; - assert_eq!(exec(inc_obj_field), String::from("6")); + assert_eq!(&exec(inc_obj_field), "6"); let execs_before_inc = r#" let a = 5; ++a === 6; "#; - assert_eq!(exec(execs_before_inc), String::from("true")); + assert_eq!(&exec(execs_before_inc), "true"); let execs_before_dec = r#" let a = 5; --a === 4; "#; - assert_eq!(exec(execs_before_dec), String::from("true")); + assert_eq!(&exec(execs_before_dec), "true"); } #[test] @@ -398,40 +398,40 @@ fn unary_post() { a++; a; "#; - assert_eq!(exec(unary_inc), String::from("6")); + assert_eq!(&exec(unary_inc), "6"); let unary_dec = r#" let a = 5; a--; a; "#; - assert_eq!(exec(unary_dec), String::from("4")); + assert_eq!(&exec(unary_dec), "4"); let inc_obj_prop = r#" const a = { b: 5 }; a.b++; a['b']; "#; - assert_eq!(exec(inc_obj_prop), String::from("6")); + assert_eq!(&exec(inc_obj_prop), "6"); let inc_obj_field = r#" const a = { b: 5 }; a['b']++; a.b; "#; - assert_eq!(exec(inc_obj_field), String::from("6")); + assert_eq!(&exec(inc_obj_field), "6"); let execs_after_inc = r#" let a = 5; a++ === 5; "#; - assert_eq!(exec(execs_after_inc), String::from("true")); + assert_eq!(&exec(execs_after_inc), "true"); let execs_after_dec = r#" let a = 5; a-- === 5; "#; - assert_eq!(exec(execs_after_dec), String::from("true")); + assert_eq!(&exec(execs_after_dec), "true"); } #[cfg(test)] @@ -444,7 +444,7 @@ mod in_operator { var p = 'a'; p in o "#; - assert_eq!(exec(p_in_o), String::from("true")); + assert_eq!(&exec(p_in_o), "true"); } #[test] @@ -454,7 +454,7 @@ mod in_operator { var p = 'toString'; p in o "#; - assert_eq!(exec(p_in_o), String::from("true")); + assert_eq!(&exec(p_in_o), "true"); } #[test] @@ -464,7 +464,7 @@ mod in_operator { var p = 'b'; p in o "#; - assert_eq!(exec(p_not_in_o), String::from("false")); + assert_eq!(&exec(p_not_in_o), "false"); } #[test] @@ -476,7 +476,7 @@ mod in_operator { var a = ['a']; n in a "#; - assert_eq!(exec(num_in_array), String::from("true")); + assert_eq!(&exec(num_in_array), "true"); } #[test] @@ -490,7 +490,7 @@ mod in_operator { o[sym] = 'hello'; sym in o "#; - assert_eq!(exec(sym_in_object), String::from("true")); + assert_eq!(&exec(sym_in_object), "true"); } #[test] diff --git a/boa/src/syntax/ast/node.rs b/boa/src/syntax/ast/node.rs index 1f6896aaff..b2288af8ea 100644 --- a/boa/src/syntax/ast/node.rs +++ b/boa/src/syntax/ast/node.rs @@ -14,13 +14,15 @@ use serde::{Deserialize, Serialize}; #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub enum Node { - /// An array is an ordered collection of data (either primitive or object depending upon the language). + /// An array is an ordered collection of data (either primitive or object depending upon the + /// language). /// /// Arrays are used to store multiple values in a single variable. /// This is compared to a variable that can store only one value. /// - /// Each item in an array has a number attached to it, called a numeric index, that allows you to access it. - /// In JavaScript, arrays start at index zero and can be manipulated with various methods. + /// Each item in an array has a number attached to it, called a numeric index, that allows you + /// to access it. In JavaScript, arrays start at index zero and can be manipulated with various + /// methods. /// /// More information: /// - [ECMAScript reference][spec] @@ -30,10 +32,12 @@ pub enum Node { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array ArrayDecl(Box<[Node]>), - /// An arrow function expression is a syntactically compact alternative to a regular function expression. + /// An arrow function expression is a syntactically compact alternative to a regular function + /// expression. /// - /// Arrow function expressions are ill suited as methods, and they cannot be used as constructors. - /// Arrow functions cannot be used as constructors and will throw an error when used with new. + /// Arrow function expressions are ill suited as methods, and they cannot be used as + /// constructors. Arrow functions cannot be used as constructors and will throw an error when + /// used with new. /// /// More information: /// - [ECMAScript reference][spec] @@ -43,7 +47,8 @@ pub enum Node { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions ArrowFunctionDecl(Box<[FormalParameter]>, Box), - /// An assignment operator assigns a value to its left operand based on the value of its right operand. + /// An assignment operator assigns a value to its left operand based on the value of its right + /// operand. /// /// Assignment operator (`=`), assigns the value of its right operand to its left operand. /// @@ -63,12 +68,14 @@ pub enum Node { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Operators BinOp(BinOp, Box, Box), - /// A `block` statement (or compound statement in other languages) is used to group zero or more statements. + /// A `block` statement (or compound statement in other languages) is used to group zero or + /// more statements. /// /// The block statement is often called compound statement in other languages. /// It allows you to use multiple statements where JavaScript expects only one statement. - /// Combining statements into blocks is a common practice in JavaScript. The opposite behavior is possible using an empty statement, - /// where you provide no statement, although one is required. + /// Combining statements into blocks is a common practice in JavaScript. The opposite behavior + /// is possible using an empty statement, where you provide no statement, although one is + /// required. /// /// More information: /// - [ECMAScript reference][spec] @@ -78,11 +85,13 @@ pub enum Node { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/block Block(Box<[Node]>), - /// The `break` statement terminates the current loop, switch, or label statement and transfers program control to the statement following the terminated statement. + /// The `break` statement terminates the current loop, switch, or label statement and transfers + /// program control to the statement following the terminated statement. /// - /// The break statement includes an optional label that allows the program to break out of a labeled statement. - /// The break statement needs to be nested within the referenced label. The labeled statement can be any block statement; - /// it does not have to be preceded by a loop statement. + /// The break statement includes an optional label that allows the program to break out of a + /// labeled statement. The break statement needs to be nested within the referenced label. The + /// labeled statement can be any block statement; it does not have to be preceded by a loop + /// statement. /// /// More information: /// - [ECMAScript reference][spec] @@ -94,9 +103,11 @@ pub enum Node { /// Calling the function actually performs the specified actions with the indicated parameters. /// - /// Defining a function does not execute it. Defining it simply names the function and specifies what to do when the function is called. - /// Functions must be in scope when they are called, but the function declaration can be hoisted - /// The scope of a function is the function in which it is declared (or the entire program, if it is declared at the top level). + /// Defining a function does not execute it. Defining it simply names the function and + /// specifies what to do when the function is called. Functions must be in scope when they are + /// called, but the function declaration can be hoisted. The scope of a function is the + /// function in which it is declared (or the entire program, if it is declared at the top + /// level). /// /// More information: /// - [ECMAScript reference][spec] @@ -106,11 +117,13 @@ pub enum Node { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions#Calling_functions Call(Box, Box<[Node]>), - /// The `conditional` (ternary) operator is the only JavaScript operator that takes three operands. + /// The `conditional` (ternary) operator is the only JavaScript operator that takes three + /// operands. /// - /// This operator is the only JavaScript operator that takes three operands: a condition followed by a question mark (`?`), - /// then an expression to execute `if` the condition is truthy followed by a colon (`:`), and finally the expression to execute if the condition is `falsy`. - /// This operator is frequently used as a shortcut for the `if` statement. + /// This operator is the only JavaScript operator that takes three operands: a condition + /// followed by a question mark (`?`), then an expression to execute `if` the condition is + /// truthy followed by a colon (`:`), and finally the expression to execute if the condition + /// is `false`. This operator is frequently used as a shortcut for the `if` statement. /// /// More information: /// - [ECMAScript reference][spec] @@ -132,13 +145,15 @@ pub enum Node { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals Const(Const), - /// The `const` statements are block-scoped, much like variables defined using the `let` keyword. + /// The `const` statements are block-scoped, much like variables defined using the `let` + /// keyword. /// - /// This declaration creates a constant whose scope can be either global or local to the block in which it is declared. - /// Global constants do not become properties of the window object, unlike var variables. + /// This declaration creates a constant whose scope can be either global or local to the block + /// in which it is declared. Global constants do not become properties of the window object, + /// unlike var variables. /// - /// An initializer for a constant is required. You must specify its value in the same statement in which it's declared. - /// (This makes sense, given that it can't be changed later.) + /// An initializer for a constant is required. You must specify its value in the same statement + /// in which it's declared. (This makes sense, given that it can't be changed later.) /// /// More information: /// - [ECMAScript reference][spec] @@ -150,11 +165,12 @@ pub enum Node { /// [expression]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Expressions ConstDecl(Box<[(String, Node)]>), - /// The `continue` statement terminates execution of the statements in the current iteration of the current or labeled loop, - /// and continues execution of the loop with the next iteration. + /// The `continue` statement terminates execution of the statements in the current iteration of + /// the current or labeled loop, and continues execution of the loop with the next iteration. /// - /// The continue statement can include an optional label that allows the program to jump to the next iteration of a labeled - /// loop statement instead of the current loop. In this case, the continue statement needs to be nested within this labeled statement. + /// The continue statement can include an optional label that allows the program to jump to the + /// next iteration of a labeled loop statement instead of the current loop. In this case, the + /// continue statement needs to be nested within this labeled statement. /// /// More information: /// - [ECMAScript reference][spec] @@ -164,9 +180,11 @@ pub enum Node { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/continue Continue(Option), - /// The `do...while` statement creates a loop that executes a specified statement until the test condition evaluates to false. + /// The `do...while` statement creates a loop that executes a specified statement until the + /// test condition evaluates to false. /// - /// The condition is evaluated after executing the statement, resulting in the specified statement executing at least once. + /// The condition is evaluated after executing the statement, resulting in the specified + /// statement executing at least once. /// /// More information: /// - [ECMAScript reference][spec] @@ -176,13 +194,16 @@ pub enum Node { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/do...while DoWhileLoop(Box, Box), - /// The `function` declaration (function statement) defines a function with the specified parameters. + /// The `function` declaration (function statement) defines a function with the specified + /// parameters. /// - /// A function created with a function declaration is a `Function` object and has all the properties, methods and behavior of `Function`. + /// A function created with a function declaration is a `Function` object and has all the + /// properties, methods and behavior of `Function`. /// /// A function can also be created using an expression (see function expression). /// - /// By default, functions return undefined. To return any other value, the function must have a return statement that specifies the value to return. + /// By default, functions return `undefined`. To return any other value, the function must have + /// a return statement that specifies the value to return. /// /// More information: /// - [ECMAScript reference][spec] @@ -190,20 +211,40 @@ pub enum Node { /// /// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-function /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function - FunctionDecl(Option, Box<[FormalParameter]>, Box), + FunctionDecl(String, Box<[FormalParameter]>, Box), - /// This property accessor provides access to an object's properties by using the [dot notation][mdn]. + /// The `function` expression defines a function with the specified parameters. + /// + /// A function created with a function expression is a `Function` object and has all the + /// properties, methods and behavior of `Function`. + /// + /// A function can also be created using a declaration (see function expression). + /// + /// By default, functions return `undefined`. To return any other value, the function must have + /// a return statement that specifies the value to return. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-function + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function + FunctionExpr(Option, Box<[FormalParameter]>, Box), + + /// This property accessor provides access to an object's properties by using the + /// [dot notation][mdn]. /// /// In the object.property syntax, the property must be a valid JavaScript identifier. - /// (In the ECMAScript standard, the names of properties are technically "IdentifierNames", not "Identifiers", - /// so reserved words can be used but are not recommended). + /// (In the ECMAScript standard, the names of properties are technically "IdentifierNames", not + /// "Identifiers", so reserved words can be used but are not recommended). /// - /// One can think of an object as an associative array (a.k.a. map, dictionary, hash, lookup table). - /// The keys in this array are the names of the object's properties. + /// One can think of an object as an associative array (a.k.a. map, dictionary, hash, lookup + /// table). The keys in this array are the names of the object's properties. /// - /// It's typical when speaking of an object's properties to make a distinction between properties and methods. However, - /// the property/method distinction is little more than a convention. A method is simply a property that can be called (for example, - /// if it has a reference to a Function instance as its value). + /// It's typical when speaking of an object's properties to make a distinction between + /// properties and methods. However, the property/method distinction is little more than a + /// convention. A method is simply a property that can be called (for example, if it has a + /// reference to a Function instance as its value). /// /// More information: /// - [ECMAScript reference][spec] @@ -213,16 +254,20 @@ pub enum Node { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors#Dot_notation GetConstField(Box, String), - /// This property accessor provides access to an object's properties by using the [bracket notation][mdn]. + /// This property accessor provides access to an object's properties by using the + /// [bracket notation][mdn]. /// - /// In the object[property_name] syntax, the property_name is just a string or [Symbol][symbol]. So, it can be any string, including '1foo', '!bar!', or even ' ' (a space). + /// In the object[property_name] syntax, the property_name is just a string or + /// [Symbol][symbol]. So, it can be any string, including '1foo', '!bar!', or even ' ' (a + /// space). /// - /// One can think of an object as an associative array (a.k.a. map, dictionary, hash, lookup table). - /// The keys in this array are the names of the object's properties. + /// One can think of an object as an associative array (a.k.a. map, dictionary, hash, lookup + /// table). The keys in this array are the names of the object's properties. /// - /// It's typical when speaking of an object's properties to make a distinction between properties and methods. However, - /// the property/method distinction is little more than a convention. A method is simply a property that can be called (for example, - /// if it has a reference to a Function instance as its value). + /// It's typical when speaking of an object's properties to make a distinction between + /// properties and methods. However, the property/method distinction is little more than a + /// convention. A method is simply a property that can be called (for example, if it has a + /// reference to a Function instance as its value). /// /// More information: /// - [ECMAScript reference][spec] @@ -251,7 +296,8 @@ pub enum Node { Box, ), - /// The `if` statement executes a statement if a specified condition is [`truthy`][truthy]. If the condition is [`falsy`][falsy], another statement can be executed. + /// The `if` statement executes a statement if a specified condition is [`truthy`][truthy]. If + /// the condition is [`falsy`][falsy], another statement can be executed. /// /// Multiple `if...else` statements can be nested to create an else if clause. /// @@ -268,13 +314,16 @@ pub enum Node { /// [expression]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Expressions If(Box, Box, Option>), - /// The `let` statement declares a block scope local variable, optionally initializing it to a value. + /// The `let` statement declares a block scope local variable, optionally initializing it to a + /// value. /// /// - /// `let` allows you to declare variables that are limited to a scope of a block statement, or expression on which - /// it is used, unlike the `var` keyword, which defines a variable globally, or locally to an entire function regardless of block scope. + /// `let` allows you to declare variables that are limited to a scope of a block statement, or + /// expression on which it is used, unlike the `var` keyword, which defines a variable + /// globally, or locally to an entire function regardless of block scope. /// - /// Just like const the `let` does not create properties of the window object when declared globally (in the top-most scope). + /// Just like const the `let` does not create properties of the window object when declared + /// globally (in the top-most scope). /// /// More information: /// - [ECMAScript reference][spec] @@ -284,12 +333,15 @@ pub enum Node { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let LetDecl(Box<[(String, Option)]>), - /// An `identifier` is a sequence of characters in the code that identifies a variable, function, or property. + /// An `identifier` is a sequence of characters in the code that identifies a variable, + /// function, or property. /// - /// In JavaScript, identifiers are case-sensitive and can contain Unicode letters, $, _, and digits (0-9), but may not start with a digit. + /// In JavaScript, identifiers are case-sensitive and can contain Unicode letters, $, _, and + /// digits (0-9), but may not start with a digit. /// - /// An identifier differs from a string in that a string is data, while an identifier is part of the code. In JavaScript, there is no way - /// to convert identifiers to strings, but sometimes it is possible to parse strings into identifiers. + /// An identifier differs from a string in that a string is data, while an identifier is part + /// of the code. In JavaScript, there is no way to convert identifiers to strings, but + /// sometimes it is possible to parse strings into identifiers. /// /// More information: /// - [ECMAScript reference][spec] @@ -299,7 +351,8 @@ pub enum Node { /// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/Identifier Local(String), - /// The `new` operator lets developers create an instance of a user-defined object type or of one of the built-in object types that has a constructor function. + /// The `new` operator lets developers create an instance of a user-defined object type or of + /// one of the built-in object types that has a constructor function. /// /// The new keyword does the following things: /// - Creates a blank, plain JavaScript object; @@ -315,13 +368,16 @@ pub enum Node { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new New(Box), - /// Objects in JavaScript may be defined as an unordered collection of related data, of primitive or reference types, in the form of “key: value” pairs. + /// Objects in JavaScript may be defined as an unordered collection of related data, of + /// primitive or reference types, in the form of “key: value” pairs. /// - /// Objects can be initialized using `new Object()`, `Object.create()`, or using the literal notation. + /// Objects can be initialized using `new Object()`, `Object.create()`, or using the literal + /// notation. /// - /// An object initializer is an expression that describes the initialization of an [`Object`][object]. - /// Objects consist of properties, which are used to describe an object. Values of object properties can either - /// contain [`primitive`][primitive] data types or other objects. + /// An object initializer is an expression that describes the initialization of an + /// [`Object`][object]. Objects consist of properties, which are used to describe an object. + /// Values of object properties can either contain [`primitive`][primitive] data types or other + /// objects. /// /// More information: /// - [ECMAScript reference][spec] @@ -333,15 +389,17 @@ pub enum Node { /// [primitive]: https://developer.mozilla.org/en-US/docs/Glossary/primitive Object(Box<[PropertyDefinition]>), - /// The `return` statement ends function execution and specifies a value to be returned to the function caller. + /// The `return` statement ends function execution and specifies a value to be returned to the + /// function caller. /// /// Syntax: `return [expression];` /// /// `expression`: - /// > The expression whose value is to be returned. If omitted, `undefined` is returned instead. + /// > The expression whose value is to be returned. If omitted, `undefined` is returned + /// > nstead. /// - /// When a `return` statement is used in a function body, the execution of the function is stopped. - /// If specified, a given value is returned to the function caller. + /// When a `return` statement is used in a function body, the execution of the function is + /// stopped. If specified, a given value is returned to the function caller. /// /// More information: /// - [ECMAScript reference][spec] @@ -351,13 +409,15 @@ pub enum Node { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/return Return(Option>), - /// The `switch` statement evaluates an expression, matching the expression's value to a case clause, - /// and executes statements associated with that case, as well as statements in cases that follow the matching case. + /// The `switch` statement evaluates an expression, matching the expression's value to a case + /// clause, and executes statements associated with that case, as well as statements in cases + /// that follow the matching case. /// - /// A `switch` statement first evaluates its expression. It then looks for the first case clause whose expression evaluates - /// to the same value as the result of the input expression (using the strict comparison, `===`) and transfers control to that clause, - /// executing the associated statements. (If multiple cases match the provided value, the first case that matches is selected, even if - /// the cases are not equal to each other.) + /// A `switch` statement first evaluates its expression. It then looks for the first case + /// clause whose expression evaluates to the same value as the result of the input expression + /// (using the strict comparison, `===`) and transfers control to that clause, executing the + /// associated statements. (If multiple cases match the provided value, the first case that + /// matches is selected, even if the cases are not equal to each other.) /// /// More information: /// - [ECMAScript reference][spec] @@ -367,12 +427,15 @@ pub enum Node { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch Switch(Box, Box<[(Node, Box<[Node]>)]>, Option>), - /// The `spread` operator allows an iterable such as an array expression or string to be expanded. + /// The `spread` operator allows an iterable such as an array expression or string to be + /// expanded. /// /// Syntax: `...x` /// - /// It expands array expressions or strings in places where zero or more arguments (for function calls) or elements (for array literals) - /// are expected, or an object expression to be expanded in places where zero or more key-value pairs (for object literals) are expected. + /// It expands array expressions or strings in places where zero or more arguments (for + /// function calls) or elements (for array literals) + /// are expected, or an object expression to be expanded in places where zero or more key-value + /// pairs (for object literals) are expected. /// /// More information: /// - [ECMAScript reference][spec] @@ -395,8 +458,8 @@ pub enum Node { /// Syntax: `throw expression;` /// /// Execution of the current function will stop (the statements after throw won't be executed), - /// and control will be passed to the first catch block in the call stack. If no catch block exists among - /// caller functions, the program will terminate. + /// and control will be passed to the first catch block in the call stack. If no catch block + /// exists among caller functions, the program will terminate. /// /// More information: /// - [ECMAScript reference][spec] @@ -420,10 +483,12 @@ pub enum Node { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof TypeOf(Box), - /// The `try...catch` statement marks a block of statements to try and specifies a response should an exception be thrown. + /// The `try...catch` statement marks a block of statements to try and specifies a response + /// should an exception be thrown. /// - /// The `try` statement consists of a `try`-block, which contains one or more statements. `{}` must always be used, - /// even for single statements. At least one `catch`-block, or a `finally`-block, must be present. + /// The `try` statement consists of a `try`-block, which contains one or more statements. `{}` + /// must always be used, even for single statements. At least one `catch`-block, or a + /// `finally`-block, must be present. /// /// More information: /// - [ECMAScript reference][spec] @@ -464,13 +529,15 @@ pub enum Node { /// The `var` statement declares a variable, optionally initializing it to a value. /// - /// var declarations, wherever they occur, are processed before any code is executed. This is called hoisting, and is discussed further below. + /// var declarations, wherever they occur, are processed before any code is executed. This is + /// called hoisting, and is discussed further below. /// - /// The scope of a variable declared with var is its current execution context, which is either the enclosing function or, - /// for variables declared outside any function, global. If you re-declare a JavaScript variable, it will not lose its value. + /// The scope of a variable declared with var is its current execution context, which is either + /// the enclosing function or, for variables declared outside any function, global. If you + /// re-declare a JavaScript variable, it will not lose its value. /// - /// Assigning a value to an undeclared variable implicitly creates it as a global variable - /// (it becomes a property of the global object) when the assignment is executed. + /// Assigning a value to an undeclared variable implicitly creates it as a global variable (it + /// becomes a property of the global object) when the assignment is executed. /// /// More information: /// - [ECMAScript reference][spec] @@ -480,7 +547,8 @@ pub enum Node { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var VarDecl(Box<[(String, Option)]>), - /// The `while` statement creates a loop that executes a specified statement as long as the test condition evaluates to `true`. + /// The `while` statement creates a loop that executes a specified statement as long as the + /// test condition evaluates to `true`. /// /// The condition is evaluated before executing the statement. /// @@ -636,14 +704,24 @@ impl Node { } /// Creates a `FunctionDecl` AST node. - pub fn function_decl(name: ON, params: P, body: B) -> Self + pub fn function_decl(name: N, params: P, body: B) -> Self + where + N: Into, + P: Into>, + B: Into>, + { + Self::FunctionDecl(name.into(), params.into(), body.into()) + } + + /// Creates a `FunctionDecl` AST node. + pub fn function_expr(name: ON, params: P, body: B) -> Self where N: Into, ON: Into>, P: Into>, B: Into>, { - Self::FunctionDecl(name.into().map(N::into), params.into(), body.into()) + Self::FunctionExpr(name.into().map(N::into), params.into(), body.into()) } /// Creates a `GetConstField` AST node. @@ -990,18 +1068,24 @@ impl Node { f.write_str("]") } Self::FunctionDecl(ref name, ref _args, ref node) => { + write!(f, "function {} {{", name)?; + //join_nodes(f, args)?; TODO: port + f.write_str("} ")?; + node.display(f, indentation + 1) + } + Self::FunctionExpr(ref name, ref args, ref node) => { write!(f, "function ")?; if let Some(func_name) = name { write!(f, "{}", func_name)?; } write!(f, "{{")?; - //join_nodes(f, args)?; TODO: port + join_nodes(f, args)?; f.write_str("} ")?; node.display(f, indentation + 1) } - Self::ArrowFunctionDecl(ref _args, ref node) => { + Self::ArrowFunctionDecl(ref args, ref node) => { write!(f, "(")?; - //join_nodes(f, args)?; TODO: port + join_nodes(f, args)?; f.write_str(") => ")?; node.display(f, indentation) } @@ -1038,7 +1122,10 @@ impl Node { } /// Utility to join multiple Nodes into a single string. -fn join_nodes(f: &mut fmt::Formatter<'_>, nodes: &[Node]) -> fmt::Result { +fn join_nodes(f: &mut fmt::Formatter<'_>, nodes: &[N]) -> fmt::Result +where + N: fmt::Display, +{ let mut first = true; for e in nodes { if !first { @@ -1086,6 +1173,19 @@ impl FormalParameter { } } +impl fmt::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). diff --git a/boa/src/syntax/parser/expression/primary/function_expression.rs b/boa/src/syntax/parser/expression/primary/function_expression.rs index 4cc4bd27c0..67ba84797c 100644 --- a/boa/src/syntax/parser/expression/primary/function_expression.rs +++ b/boa/src/syntax/parser/expression/primary/function_expression.rs @@ -55,6 +55,6 @@ impl TokenParser for FunctionExpression { cursor.expect(Punctuator::CloseBlock, "function expression")?; - Ok(Node::function_decl::<_, &String, _, _>(name, params, body)) + Ok(Node::function_expr::<_, &String, _, _>(name, params, body)) } } diff --git a/boa/src/syntax/parser/expression/primary/object_initializer/mod.rs b/boa/src/syntax/parser/expression/primary/object_initializer/mod.rs index 1fb4e9ba6b..d3638e328a 100644 --- a/boa/src/syntax/parser/expression/primary/object_initializer/mod.rs +++ b/boa/src/syntax/parser/expression/primary/object_initializer/mod.rs @@ -242,7 +242,7 @@ impl TokenParser for MethodDefinition { Ok(node::PropertyDefinition::MethodDefinition( methodkind, prop_name, - Node::function_decl::<_, String, _, _>(None, params, body), + Node::function_expr::<_, String, _, _>(None, params, body), )) } } diff --git a/boa/src/syntax/parser/expression/primary/object_initializer/tests.rs b/boa/src/syntax/parser/expression/primary/object_initializer/tests.rs index af5e660a27..54e82a5592 100644 --- a/boa/src/syntax/parser/expression/primary/object_initializer/tests.rs +++ b/boa/src/syntax/parser/expression/primary/object_initializer/tests.rs @@ -32,7 +32,7 @@ fn check_object_short_function() { PropertyDefinition::method_definition( MethodDefinitionKind::Ordinary, "b", - Node::function_decl::<_, String, _, _>(None, Vec::new(), Node::statement_list(vec![])), + Node::function_expr::<_, String, _, _>(None, Vec::new(), Node::statement_list(vec![])), ), ]; @@ -57,7 +57,7 @@ fn check_object_short_function_arguments() { PropertyDefinition::method_definition( MethodDefinitionKind::Ordinary, "b", - Node::FunctionDecl( + Node::FunctionExpr( None, Box::new([FormalParameter::new("test", None, false)]), Box::new(Node::StatementList(Box::new([]))), @@ -85,7 +85,7 @@ fn check_object_getter() { PropertyDefinition::method_definition( MethodDefinitionKind::Get, "b", - Node::FunctionDecl( + Node::FunctionExpr( None, Box::new([]), Box::new(Node::statement_list(Vec::new())), @@ -113,7 +113,7 @@ fn check_object_setter() { PropertyDefinition::method_definition( MethodDefinitionKind::Set, "b", - Node::function_decl::<_, String, _, _>( + Node::function_expr::<_, String, _, _>( None, vec![FormalParameter::new("test", None, false)], Node::statement_list(Vec::new()),