diff --git a/boa_ast/src/expression/mod.rs b/boa_ast/src/expression/mod.rs index 16f7746fd67..ae1c8eb47ae 100644 --- a/boa_ast/src/expression/mod.rs +++ b/boa_ast/src/expression/mod.rs @@ -19,8 +19,8 @@ use self::{ }; use super::{ - function::FormalParameterList, function::{ArrowFunction, AsyncFunction, AsyncGenerator, Class, Function, Generator}, + function::{AsyncArrowFunction, FormalParameterList}, Statement, }; @@ -88,6 +88,9 @@ pub enum Expression { /// See [`ArrowFunction`]. ArrowFunction(ArrowFunction), + /// See [`AsyncArrowFunction`]. + AsyncArrowFunction(AsyncArrowFunction), + /// See [`Generator`]. Generator(Generator), @@ -168,6 +171,7 @@ impl Expression { Self::ObjectLiteral(o) => o.to_indented_string(interner, indentation), Self::Spread(sp) => sp.to_interned_string(interner), Self::Function(f) => f.to_indented_string(interner, indentation), + Self::AsyncArrowFunction(f) => f.to_indented_string(interner, indentation), Self::ArrowFunction(arrf) => arrf.to_indented_string(interner, indentation), Self::Class(cl) => cl.to_indented_string(interner, indentation), Self::Generator(gen) => gen.to_indented_string(interner, indentation), @@ -219,6 +223,7 @@ impl VisitWith for Expression { Expression::Spread(sp) => visitor.visit_spread(sp), Expression::Function(f) => visitor.visit_function(f), Expression::ArrowFunction(af) => visitor.visit_arrow_function(af), + Expression::AsyncArrowFunction(af) => visitor.visit_async_arrow_function(af), Expression::Generator(g) => visitor.visit_generator(g), Expression::AsyncFunction(af) => visitor.visit_async_function(af), Expression::AsyncGenerator(ag) => visitor.visit_async_generator(ag), @@ -256,6 +261,7 @@ impl VisitWith for Expression { Expression::Spread(sp) => visitor.visit_spread_mut(sp), Expression::Function(f) => visitor.visit_function_mut(f), Expression::ArrowFunction(af) => visitor.visit_arrow_function_mut(af), + Expression::AsyncArrowFunction(af) => visitor.visit_async_arrow_function_mut(af), Expression::Generator(g) => visitor.visit_generator_mut(g), Expression::AsyncFunction(af) => visitor.visit_async_function_mut(af), Expression::AsyncGenerator(ag) => visitor.visit_async_generator_mut(ag), diff --git a/boa_ast/src/function/async_arrow_function.rs b/boa_ast/src/function/async_arrow_function.rs new file mode 100644 index 00000000000..351a3bdbd13 --- /dev/null +++ b/boa_ast/src/function/async_arrow_function.rs @@ -0,0 +1,118 @@ +use std::ops::ControlFlow; + +use super::FormalParameterList; +use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{ + expression::{Expression, Identifier}, + join_nodes, StatementList, +}; +use boa_interner::{Interner, ToIndentedString}; + +/// An async arrow function expression, as defined by the [spec]. +/// +/// An [async arrow function][mdn] 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. +/// +/// [spec]: https://tc39.es/ecma262/#prod-AsyncArrowFunction +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +pub struct AsyncArrowFunction { + name: Option, + parameters: FormalParameterList, + body: StatementList, +} + +impl AsyncArrowFunction { + /// Creates a new `AsyncArrowFunction` AST Expression. + #[inline] + #[must_use] + pub fn new( + name: Option, + parameters: FormalParameterList, + body: StatementList, + ) -> Self { + Self { + name, + parameters, + body, + } + } + + /// Gets the name of the function declaration. + #[inline] + #[must_use] + pub fn name(&self) -> Option { + self.name + } + + /// Sets the name of the function declaration. + //#[inline] + //#[must_use] + //pub fn set_name(&mut self, name: Option) { + // self.name = name; + //} + + /// Gets the list of parameters of the arrow function. + #[inline] + #[must_use] + pub fn parameters(&self) -> &FormalParameterList { + &self.parameters + } + + /// Gets the body of the arrow function. + #[inline] + #[must_use] + pub fn body(&self) -> &StatementList { + &self.body + } +} + +impl ToIndentedString for AsyncArrowFunction { + fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { + let mut buf = format!("async ({}", join_nodes(interner, self.parameters.as_ref())); + if self.body().statements().is_empty() { + buf.push_str(") => {}"); + } else { + buf.push_str(&format!( + ") => {{\n{}{}}}", + self.body.to_indented_string(interner, indentation + 1), + " ".repeat(indentation) + )); + } + buf + } +} + +impl From for Expression { + fn from(decl: AsyncArrowFunction) -> Self { + Self::AsyncArrowFunction(decl) + } +} + +impl VisitWith for AsyncArrowFunction { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + if let Some(ident) = &self.name { + try_break!(visitor.visit_identifier(ident)); + } + try_break!(visitor.visit_formal_parameter_list(&self.parameters)); + visitor.visit_statement_list(&self.body) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + if let Some(ident) = &mut self.name { + try_break!(visitor.visit_identifier_mut(ident)); + } + try_break!(visitor.visit_formal_parameter_list_mut(&mut self.parameters)); + visitor.visit_statement_list_mut(&mut self.body) + } +} diff --git a/boa_ast/src/function/async_function.rs b/boa_ast/src/function/async_function.rs index 556a02c016e..cda3bcbbf3c 100644 --- a/boa_ast/src/function/async_function.rs +++ b/boa_ast/src/function/async_function.rs @@ -25,6 +25,7 @@ pub struct AsyncFunction { name: Option, parameters: FormalParameterList, body: StatementList, + has_binding_identifier: bool, } impl AsyncFunction { @@ -35,11 +36,13 @@ impl AsyncFunction { name: Option, parameters: FormalParameterList, body: StatementList, + has_binding_identifier: bool, ) -> Self { Self { name, parameters, body, + has_binding_identifier, } } @@ -63,6 +66,13 @@ impl AsyncFunction { pub fn body(&self) -> &StatementList { &self.body } + + /// Returns whether the function expression has a binding identifier. + #[inline] + #[must_use] + pub fn has_binding_identifier(&self) -> bool { + self.has_binding_identifier + } } impl ToIndentedString for AsyncFunction { diff --git a/boa_ast/src/function/async_generator.rs b/boa_ast/src/function/async_generator.rs index fe85194aad2..bb04b089dc4 100644 --- a/boa_ast/src/function/async_generator.rs +++ b/boa_ast/src/function/async_generator.rs @@ -24,6 +24,7 @@ pub struct AsyncGenerator { name: Option, parameters: FormalParameterList, body: StatementList, + has_binding_identifier: bool, } impl AsyncGenerator { @@ -34,11 +35,13 @@ impl AsyncGenerator { name: Option, parameters: FormalParameterList, body: StatementList, + has_binding_identifier: bool, ) -> Self { Self { name, parameters, body, + has_binding_identifier, } } @@ -62,6 +65,13 @@ impl AsyncGenerator { pub fn body(&self) -> &StatementList { &self.body } + + /// Returns whether the function expression has a binding identifier. + #[inline] + #[must_use] + pub fn has_binding_identifier(&self) -> bool { + self.has_binding_identifier + } } impl ToIndentedString for AsyncGenerator { diff --git a/boa_ast/src/function/generator.rs b/boa_ast/src/function/generator.rs index bae097453d5..fe5c0ebff7f 100644 --- a/boa_ast/src/function/generator.rs +++ b/boa_ast/src/function/generator.rs @@ -26,6 +26,7 @@ pub struct Generator { name: Option, parameters: FormalParameterList, body: StatementList, + has_binding_identifier: bool, } impl Generator { @@ -36,11 +37,13 @@ impl Generator { name: Option, parameters: FormalParameterList, body: StatementList, + has_binding_identifier: bool, ) -> Self { Self { name, parameters, body, + has_binding_identifier, } } @@ -64,6 +67,13 @@ impl Generator { pub fn body(&self) -> &StatementList { &self.body } + + /// Returns whether the function expression has a binding identifier. + #[inline] + #[must_use] + pub fn has_binding_identifier(&self) -> bool { + self.has_binding_identifier + } } impl ToIndentedString for Generator { diff --git a/boa_ast/src/function/mod.rs b/boa_ast/src/function/mod.rs index 34d0eede730..a39c6941b2a 100644 --- a/boa_ast/src/function/mod.rs +++ b/boa_ast/src/function/mod.rs @@ -5,12 +5,13 @@ //! //! - [`Function`]s. //! - [`ArrowFunction`]s. +//! - [`AsyncArrowFunction`]s. //! - [`Generator`]s. //! - [`AsyncFunction`]s. //! - [`AsyncGenerator`]s. //! //! All of them can be declared in either [declaration][decl] form or [expression][expr] form, -//! except from `ArrowFunction`s, which can only be declared in expression form. +//! except from `ArrowFunction`s and `AsyncArrowFunction`s, which can only be declared in expression form. //! //! This module also contains [`Class`]es, which are templates for creating objects. Classes //! can also be declared in either declaration or expression form. @@ -21,6 +22,7 @@ //! [expr]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function mod arrow_function; +mod async_arrow_function; mod async_function; mod async_generator; mod class; @@ -28,6 +30,7 @@ mod generator; mod parameters; pub use arrow_function::ArrowFunction; +pub use async_arrow_function::AsyncArrowFunction; pub use async_function::AsyncFunction; pub use async_generator::AsyncGenerator; pub use class::{Class, ClassElement}; @@ -59,10 +62,11 @@ pub struct Function { name: Option, parameters: FormalParameterList, body: StatementList, + has_binding_identifier: bool, } impl Function { - /// Creates a new function expression + /// Creates a new function expression. #[inline] #[must_use] pub fn new( @@ -74,6 +78,24 @@ impl Function { name, parameters, body, + has_binding_identifier: false, + } + } + + /// Creates a new function expression with an expression binding identifier. + #[inline] + #[must_use] + pub fn new_with_binding_identifier( + name: Option, + parameters: FormalParameterList, + body: StatementList, + has_binding_identifier: bool, + ) -> Self { + Self { + name, + parameters, + body, + has_binding_identifier, } } @@ -97,6 +119,13 @@ impl Function { pub fn body(&self) -> &StatementList { &self.body } + + /// Returns whether the function expression has a binding identifier. + #[inline] + #[must_use] + pub fn has_binding_identifier(&self) -> bool { + self.has_binding_identifier + } } impl ToIndentedString for Function { diff --git a/boa_ast/src/operations.rs b/boa_ast/src/operations.rs index ea1d630b204..256dd4a7674 100644 --- a/boa_ast/src/operations.rs +++ b/boa_ast/src/operations.rs @@ -9,7 +9,8 @@ use boa_interner::Sym; use crate::{ expression::{access::SuperPropertyAccess, Await, Identifier, SuperCall, Yield}, function::{ - ArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement, Function, Generator, + ArrowFunction, AsyncArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement, + Function, Generator, }, property::{MethodDefinition, PropertyDefinition}, visitor::{VisitWith, Visitor}, @@ -133,6 +134,25 @@ where node.visit_with(self) } + fn visit_async_arrow_function( + &mut self, + node: &'ast AsyncArrowFunction, + ) -> ControlFlow { + if ![ + ContainsSymbol::NewTarget, + ContainsSymbol::SuperProperty, + ContainsSymbol::SuperCall, + ContainsSymbol::Super, + ContainsSymbol::This, + ] + .contains(&self.0) + { + return ControlFlow::Continue(()); + } + + node.visit_with(self) + } + fn visit_super_property_access( &mut self, node: &'ast SuperPropertyAccess, diff --git a/boa_ast/src/visitor.rs b/boa_ast/src/visitor.rs index dd6e8be2be5..f997b3a1311 100644 --- a/boa_ast/src/visitor.rs +++ b/boa_ast/src/visitor.rs @@ -31,8 +31,8 @@ use crate::expression::{ Spread, SuperCall, TaggedTemplate, Yield, }; use crate::function::{ - ArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement, FormalParameter, - FormalParameterList, Function, Generator, + ArrowFunction, AsyncArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement, + FormalParameter, FormalParameterList, Function, Generator, }; use crate::pattern::{ ArrayPattern, ArrayPatternElement, ObjectPattern, ObjectPatternElement, Pattern, @@ -119,6 +119,7 @@ pub trait Visitor<'ast>: Sized { define_visit!(visit_object_literal, ObjectLiteral); define_visit!(visit_spread, Spread); define_visit!(visit_arrow_function, ArrowFunction); + define_visit!(visit_async_arrow_function, AsyncArrowFunction); define_visit!(visit_template_literal, TemplateLiteral); define_visit!(visit_property_access, PropertyAccess); define_visit!(visit_new, New); @@ -203,6 +204,7 @@ pub trait VisitorMut<'ast>: Sized { define_visit_mut!(visit_object_literal_mut, ObjectLiteral); define_visit_mut!(visit_spread_mut, Spread); define_visit_mut!(visit_arrow_function_mut, ArrowFunction); + define_visit_mut!(visit_async_arrow_function_mut, AsyncArrowFunction); define_visit_mut!(visit_template_literal_mut, TemplateLiteral); define_visit_mut!(visit_property_access_mut, PropertyAccess); define_visit_mut!(visit_new_mut, New); diff --git a/boa_engine/src/builtins/function/mod.rs b/boa_engine/src/builtins/function/mod.rs index 9823d7eda54..0967f20a415 100644 --- a/boa_engine/src/builtins/function/mod.rs +++ b/boa_engine/src/builtins/function/mod.rs @@ -841,31 +841,29 @@ impl BuiltInFunctionObject { } }; - match (function, name) { - ( - Function::Native { - function: _, - constructor: _, - }, - Some(name), - ) => Ok(js_string!( - utf16!("function "), - &name, - utf16!("() {{\n [native Code]\n}}") - ) - .into()), - (Function::Ordinary { .. }, Some(name)) if name.is_empty() => { - Ok(js_string!("[Function (anonymous)]").into()) + let name = if let Some(name) = name { + if name.is_empty() { + "anonymous".into() + } else { + name } - (Function::Ordinary { .. }, Some(name)) => { + } else { + "anonymous".into() + }; + + match function { + Function::Native { .. } | Function::Closure { .. } | Function::Ordinary { .. } => { Ok(js_string!(utf16!("[Function: "), &name, utf16!("]")).into()) } - (Function::Ordinary { .. }, None) => Ok(js_string!("[Function (anonymous)]").into()), - (Function::Generator { .. }, Some(name)) => { - Ok(js_string!(utf16!("[Function*: "), &name, utf16!("]")).into()) + Function::Async { .. } => { + Ok(js_string!(utf16!("[AsyncFunction: "), &name, utf16!("]")).into()) + } + Function::Generator { .. } => { + Ok(js_string!(utf16!("[GeneratorFunction: "), &name, utf16!("]")).into()) + } + Function::AsyncGenerator { .. } => { + Ok(js_string!(utf16!("[AsyncGeneratorFunction: "), &name, utf16!("]")).into()) } - (Function::Generator { .. }, None) => Ok(js_string!("[Function* (anonymous)]").into()), - _ => Ok("TODO".into()), } } diff --git a/boa_engine/src/bytecompiler/function.rs b/boa_engine/src/bytecompiler/function.rs index 08440efdb25..3690c13e615 100644 --- a/boa_engine/src/bytecompiler/function.rs +++ b/boa_engine/src/bytecompiler/function.rs @@ -18,6 +18,7 @@ pub(crate) struct FunctionCompiler { r#async: bool, strict: bool, arrow: bool, + has_binding_identifier: bool, } impl FunctionCompiler { @@ -30,6 +31,7 @@ impl FunctionCompiler { r#async: false, strict: false, arrow: false, + has_binding_identifier: false, } } @@ -73,6 +75,13 @@ impl FunctionCompiler { self } + /// Indicate if the function has a binding identifier. + #[inline] + pub(crate) fn has_binding_identifier(mut self, has_binding_identifier: bool) -> Self { + self.has_binding_identifier = has_binding_identifier; + self + } + /// Compile a function statement list and it's parameters into bytecode. pub(crate) fn compile( mut self, @@ -99,6 +108,14 @@ impl FunctionCompiler { context, }; + if self.has_binding_identifier { + compiler.code_block.has_binding_identifier = true; + compiler.context.push_compile_time_environment(false); + compiler + .context + .create_immutable_binding(self.name.into(), self.strict); + } + compiler.context.push_compile_time_environment(true); // An arguments object is added when all of the following conditions are met @@ -185,13 +202,15 @@ impl FunctionCompiler { } else { let (num_bindings, compile_environment) = compiler.context.pop_compile_time_environment(); - compiler - .code_block - .compile_environments - .push(compile_environment); + compiler.push_compile_environment(compile_environment); compiler.code_block.num_bindings = num_bindings; } + if self.has_binding_identifier { + let (_, compile_environment) = compiler.context.pop_compile_time_environment(); + compiler.push_compile_environment(compile_environment); + } + compiler.code_block.params = parameters.clone(); // TODO These are redundant if a function returns so may need to check if a function returns and adding these if it doesn't diff --git a/boa_engine/src/bytecompiler/mod.rs b/boa_engine/src/bytecompiler/mod.rs index 38a8351f448..245b3320631 100644 --- a/boa_engine/src/bytecompiler/mod.rs +++ b/boa_engine/src/bytecompiler/mod.rs @@ -18,8 +18,8 @@ use boa_ast::{ Call, Identifier, New, Optional, OptionalOperationKind, }, function::{ - ArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement, FormalParameterList, - Function, Generator, + ArrowFunction, AsyncArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement, + FormalParameterList, Function, Generator, }, pattern::{ArrayPatternElement, ObjectPatternElement, Pattern}, property::{MethodDefinition, PropertyDefinition, PropertyName}, @@ -48,6 +48,7 @@ enum NodeKind { enum FunctionKind { Ordinary, Arrow, + AsyncArrow, Async, Generator, AsyncGenerator, @@ -60,20 +61,23 @@ struct FunctionSpec<'a> { name: Option, parameters: &'a FormalParameterList, body: &'a StatementList, + has_binding_identifier: bool, } impl<'a> FunctionSpec<'a> { #[inline] fn is_arrow(&self) -> bool { - self.kind == FunctionKind::Arrow + matches!(self.kind, FunctionKind::Arrow | FunctionKind::AsyncArrow) } + #[inline] fn is_async(&self) -> bool { matches!( self.kind, - FunctionKind::Async | FunctionKind::AsyncGenerator + FunctionKind::Async | FunctionKind::AsyncGenerator | FunctionKind::AsyncArrow ) } + #[inline] fn is_generator(&self) -> bool { matches!( @@ -90,9 +94,11 @@ impl<'a> From<&'a Function> for FunctionSpec<'a> { name: function.name(), parameters: function.parameters(), body: function.body(), + has_binding_identifier: function.has_binding_identifier(), } } } + impl<'a> From<&'a ArrowFunction> for FunctionSpec<'a> { fn from(function: &'a ArrowFunction) -> Self { FunctionSpec { @@ -100,9 +106,23 @@ impl<'a> From<&'a ArrowFunction> for FunctionSpec<'a> { name: function.name(), parameters: function.parameters(), body: function.body(), + has_binding_identifier: false, + } + } +} + +impl<'a> From<&'a AsyncArrowFunction> for FunctionSpec<'a> { + fn from(function: &'a AsyncArrowFunction) -> Self { + FunctionSpec { + kind: FunctionKind::AsyncArrow, + name: function.name(), + parameters: function.parameters(), + body: function.body(), + has_binding_identifier: false, } } } + impl<'a> From<&'a AsyncFunction> for FunctionSpec<'a> { fn from(function: &'a AsyncFunction) -> Self { FunctionSpec { @@ -110,9 +130,11 @@ impl<'a> From<&'a AsyncFunction> for FunctionSpec<'a> { name: function.name(), parameters: function.parameters(), body: function.body(), + has_binding_identifier: function.has_binding_identifier(), } } } + impl<'a> From<&'a Generator> for FunctionSpec<'a> { fn from(function: &'a Generator) -> Self { FunctionSpec { @@ -120,9 +142,11 @@ impl<'a> From<&'a Generator> for FunctionSpec<'a> { name: function.name(), parameters: function.parameters(), body: function.body(), + has_binding_identifier: function.has_binding_identifier(), } } } + impl<'a> From<&'a AsyncGenerator> for FunctionSpec<'a> { fn from(function: &'a AsyncGenerator) -> Self { FunctionSpec { @@ -130,6 +154,7 @@ impl<'a> From<&'a AsyncGenerator> for FunctionSpec<'a> { name: function.name(), parameters: function.parameters(), body: function.body(), + has_binding_identifier: function.has_binding_identifier(), } } } @@ -1342,6 +1367,9 @@ impl<'b> ByteCompiler<'b> { Expression::ArrowFunction(function) => { self.function(function.into(), NodeKind::Expression, use_expr)?; } + Expression::AsyncArrowFunction(function) => { + self.function(function.into(), NodeKind::Expression, use_expr)?; + } Expression::Generator(function) => { self.function(function.into(), NodeKind::Expression, use_expr)?; } @@ -1963,12 +1991,12 @@ impl<'b> ByteCompiler<'b> { }, IterableLoopInitializer::Const(declaration) => match declaration { Binding::Identifier(ident) => { - self.context.create_immutable_binding(*ident); + self.context.create_immutable_binding(*ident, true); self.emit_binding(BindingOpcode::InitConst, *ident); } Binding::Pattern(pattern) => { for ident in pattern.idents() { - self.context.create_immutable_binding(ident); + self.context.create_immutable_binding(ident, true); } self.compile_declaration_pattern(pattern, BindingOpcode::InitConst)?; } @@ -2089,12 +2117,12 @@ impl<'b> ByteCompiler<'b> { }, IterableLoopInitializer::Const(declaration) => match declaration { Binding::Identifier(ident) => { - self.context.create_immutable_binding(*ident); + self.context.create_immutable_binding(*ident, true); self.emit_binding(BindingOpcode::InitConst, *ident); } Binding::Pattern(pattern) => { for ident in pattern.idents() { - self.context.create_immutable_binding(ident); + self.context.create_immutable_binding(ident, true); } self.compile_declaration_pattern(pattern, BindingOpcode::InitConst)?; } @@ -2632,6 +2660,7 @@ impl<'b> ByteCompiler<'b> { name, parameters, body, + has_binding_identifier, .. } = function; @@ -2641,15 +2670,18 @@ impl<'b> ByteCompiler<'b> { .r#async(r#async) .strict(self.code_block.strict) .arrow(arrow) + .has_binding_identifier(has_binding_identifier) .compile(parameters, body, self.context)?; let index = self.code_block.functions.len() as u32; self.code_block.functions.push(code); - if generator && r#async { + if r#async && generator { self.emit(Opcode::GetGeneratorAsync, &[index]); } else if generator { self.emit(Opcode::GetGenerator, &[index]); + } else if r#async && arrow { + self.emit(Opcode::GetAsyncArrowFunction, &[index]); } else if r#async { self.emit(Opcode::GetFunctionAsync, &[index]); } else if arrow { @@ -3065,14 +3097,14 @@ impl<'b> ByteCompiler<'b> { if *ident == Sym::ARGUMENTS { has_identifier_argument = true; } - self.context.create_immutable_binding(*ident); + self.context.create_immutable_binding(*ident, true); } Binding::Pattern(pattern) => { for ident in pattern.idents() { if ident == Sym::ARGUMENTS { has_identifier_argument = true; } - self.context.create_immutable_binding(ident); + self.context.create_immutable_binding(ident, true); } } } @@ -3259,10 +3291,7 @@ impl<'b> ByteCompiler<'b> { } else { let (num_bindings, compile_environment) = compiler.context.pop_compile_time_environment(); - compiler - .code_block - .compile_environments - .push(compile_environment); + compiler.push_compile_environment(compile_environment); compiler.code_block.num_bindings = num_bindings; compiler.code_block.is_class_constructor = true; } @@ -3272,10 +3301,7 @@ impl<'b> ByteCompiler<'b> { } let (num_bindings, compile_environment) = compiler.context.pop_compile_time_environment(); - compiler - .code_block - .compile_environments - .push(compile_environment); + compiler.push_compile_environment(compile_environment); compiler.code_block.num_bindings = num_bindings; compiler.code_block.is_class_constructor = true; } @@ -3450,10 +3476,7 @@ impl<'b> ByteCompiler<'b> { } let (num_bindings, compile_environment) = field_compiler.context.pop_compile_time_environment(); - field_compiler - .code_block - .compile_environments - .push(compile_environment); + field_compiler.push_compile_environment(compile_environment); field_compiler.code_block.num_bindings = num_bindings; field_compiler.emit_opcode(Opcode::Return); @@ -3484,10 +3507,7 @@ impl<'b> ByteCompiler<'b> { } let (num_bindings, compile_environment) = field_compiler.context.pop_compile_time_environment(); - field_compiler - .code_block - .compile_environments - .push(compile_environment); + field_compiler.push_compile_environment(compile_environment); field_compiler.code_block.num_bindings = num_bindings; field_compiler.emit_opcode(Opcode::Return); @@ -3539,10 +3559,7 @@ impl<'b> ByteCompiler<'b> { compiler.compile_statement_list(statement_list, false, false)?; let (num_bindings, compile_environment) = compiler.context.pop_compile_time_environment(); - compiler - .code_block - .compile_environments - .push(compile_environment); + compiler.push_compile_environment(compile_environment); compiler.code_block.num_bindings = num_bindings; let code = Gc::new(compiler.finish()); diff --git a/boa_engine/src/environments/compile.rs b/boa_engine/src/environments/compile.rs index 79e30e1acc8..37aa9a64ec4 100644 --- a/boa_engine/src/environments/compile.rs +++ b/boa_engine/src/environments/compile.rs @@ -14,6 +14,7 @@ struct CompileTimeBinding { index: usize, mutable: bool, lex: bool, + strict: bool, } /// A compile time environment maps bound identifiers to their binding positions. @@ -111,6 +112,7 @@ impl CompileTimeEnvironment { index: binding_index, mutable: true, lex: !function_scope, + strict: false, }, ); } @@ -131,6 +133,7 @@ impl CompileTimeEnvironment { index: binding_index, mutable: true, lex: !function_scope, + strict: false, }, ); } @@ -140,7 +143,7 @@ impl CompileTimeEnvironment { /// Crate an immutable binding. #[inline] - pub(crate) fn create_immutable_binding(&mut self, name: Identifier) { + pub(crate) fn create_immutable_binding(&mut self, name: Identifier, strict: bool) { let binding_index = self.bindings.len(); self.bindings.insert( name, @@ -148,6 +151,7 @@ impl CompileTimeEnvironment { index: binding_index, mutable: false, lex: true, + strict, }, ); } @@ -197,7 +201,8 @@ impl CompileTimeEnvironment { Some(binding) if binding.mutable => { BindingLocator::declarative(name, self.environment_index, binding.index) } - Some(_) => BindingLocator::mutate_immutable(name), + Some(binding) if binding.strict => BindingLocator::mutate_immutable(name), + Some(_) => BindingLocator::silent(name), None => { if let Some(outer) = &self.outer { outer.borrow().set_mutable_binding_recursive(name) @@ -347,11 +352,11 @@ impl Context { /// /// Panics if the global environment does not exist. #[inline] - pub(crate) fn create_immutable_binding(&mut self, name: Identifier) { + pub(crate) fn create_immutable_binding(&mut self, name: Identifier, strict: bool) { self.realm .compile_env .borrow_mut() - .create_immutable_binding(name); + .create_immutable_binding(name, strict); } /// Initialize an immutable binding at bytecode compile time and return it's binding locator. diff --git a/boa_engine/src/environments/runtime.rs b/boa_engine/src/environments/runtime.rs index 7d2f9569417..3314eb75e51 100644 --- a/boa_engine/src/environments/runtime.rs +++ b/boa_engine/src/environments/runtime.rs @@ -348,7 +348,7 @@ impl DeclarativeEnvironmentStack { panic!("global environment must exist") } - /// Push a declarative environment on the environments stack. + /// Push a declarative environment on the environments stack and return it's index. /// /// # Panics /// @@ -358,7 +358,7 @@ impl DeclarativeEnvironmentStack { &mut self, num_bindings: usize, compile_environment: Gc>, - ) { + ) -> usize { let poisoned = self .stack .last() @@ -366,12 +366,16 @@ impl DeclarativeEnvironmentStack { .poisoned .get(); + let index = self.stack.len(); + self.stack.push(Gc::new(DeclarativeEnvironment { bindings: GcCell::new(vec![None; num_bindings]), compile: compile_environment, poisoned: Cell::new(poisoned), slots: None, })); + + index } /// Push a function environment on the environments stack. @@ -458,6 +462,22 @@ impl DeclarativeEnvironmentStack { .expect("environment stack is cannot be empty") } + /// Get the most outer function environment slots. + /// + /// # Panics + /// + /// Panics if no environment exists on the stack. + #[inline] + pub(crate) fn current_function_slots(&self) -> &EnvironmentSlots { + for env in self.stack.iter().rev() { + if let Some(slots) = &env.slots { + return slots; + } + } + + panic!("global environment must exist") + } + /// Get the most outer environment. /// /// # Panics @@ -760,6 +780,7 @@ pub(crate) struct BindingLocator { binding_index: usize, global: bool, mutate_immutable: bool, + silent: bool, } impl BindingLocator { @@ -776,6 +797,7 @@ impl BindingLocator { binding_index, global: false, mutate_immutable: false, + silent: false, } } @@ -788,6 +810,7 @@ impl BindingLocator { binding_index: 0, global: true, mutate_immutable: false, + silent: false, } } @@ -801,6 +824,20 @@ impl BindingLocator { binding_index: 0, global: false, mutate_immutable: true, + silent: false, + } + } + + /// Creates a binding locator that indicates that any action is silently ignored. + #[inline] + pub(in crate::environments) fn silent(name: Identifier) -> Self { + Self { + name, + environment_index: 0, + binding_index: 0, + global: false, + mutate_immutable: false, + silent: true, } } @@ -828,6 +865,12 @@ impl BindingLocator { self.binding_index } + /// Returns if the binding is a silent operation. + #[inline] + pub(crate) fn is_silent(&self) -> bool { + self.silent + } + /// Helper method to throws an error if the binding access is illegal. #[inline] pub(crate) fn throw_mutate_immutable( diff --git a/boa_engine/src/syntax/parser/expression/assignment/arrow_function.rs b/boa_engine/src/syntax/parser/expression/assignment/arrow_function.rs index 7ec4bd99c6c..d0885aa825b 100644 --- a/boa_engine/src/syntax/parser/expression/assignment/arrow_function.rs +++ b/boa_engine/src/syntax/parser/expression/assignment/arrow_function.rs @@ -22,6 +22,7 @@ use boa_ast::{ declaration::Variable, expression::Identifier, function::{FormalParameter, FormalParameterList}, + operations::{contains, ContainsSymbol}, statement::Return, Expression, Punctuator, StatementList, }; @@ -117,6 +118,7 @@ where cursor.set_arrow(true); let body = ConciseBody::new(self.allow_in).parse(cursor, interner)?; cursor.set_arrow(arrow); + // Early Error: ArrowFormalParameters are UniqueFormalParameters. if params.has_duplicates() { return Err(ParseError::lex(LexError::Syntax( @@ -125,6 +127,22 @@ where ))); } + // Early Error: It is a Syntax Error if ArrowParameters Contains YieldExpression is true. + if contains(¶ms, ContainsSymbol::YieldExpression) { + return Err(ParseError::lex(LexError::Syntax( + "Yield expression not allowed in this context".into(), + params_start_position, + ))); + } + + // Early Error: It is a Syntax Error if ArrowParameters Contains AwaitExpression is true. + if contains(¶ms, ContainsSymbol::AwaitExpression) { + return Err(ParseError::lex(LexError::Syntax( + "Await expression not allowed in this context".into(), + params_start_position, + ))); + } + // Early Error: It is a Syntax Error if ConciseBodyContainsUseStrict of ConciseBody is true // and IsSimpleParameterList of ArrowParameters is false. if body.strict() && !params.is_simple() { @@ -197,14 +215,14 @@ where /// #[derive(Debug, Clone, Copy)] -struct ExpressionBody { +pub(super) struct ExpressionBody { allow_in: AllowIn, allow_await: AllowAwait, } impl ExpressionBody { /// Creates a new `ExpressionBody` parser. - fn new(allow_in: I, allow_await: A) -> Self + pub(super) fn new(allow_in: I, allow_await: A) -> Self where I: Into, A: Into, diff --git a/boa_engine/src/syntax/parser/expression/assignment/async_arrow_function.rs b/boa_engine/src/syntax/parser/expression/assignment/async_arrow_function.rs new file mode 100644 index 00000000000..f2317530418 --- /dev/null +++ b/boa_engine/src/syntax/parser/expression/assignment/async_arrow_function.rs @@ -0,0 +1,204 @@ +//! Async arrow function parsing. +//! +//! More information: +//! - [MDN documentation][mdn] +//! - [ECMAScript specification][spec] +//! +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions +//! [spec]: https://tc39.es/ecma262/#sec-async-arrow-function-definitions + +use super::arrow_function::ExpressionBody; +use crate::syntax::{ + lexer::{Error as LexError, TokenKind}, + parser::{ + error::{ErrorContext, ParseError, ParseResult}, + expression::BindingIdentifier, + function::{FormalParameters, FunctionBody}, + name_in_lexically_declared_names, AllowIn, AllowYield, Cursor, TokenParser, + }, +}; +use ast::{ + operations::{contains, ContainsSymbol}, + Keyword, +}; +use boa_ast::{ + self as ast, + declaration::Variable, + expression::Identifier, + function::{FormalParameter, FormalParameterList}, + statement::Return, + Punctuator, StatementList, +}; +use boa_interner::Interner; +use boa_profiler::Profiler; +use std::io::Read; + +/// Async arrow function parsing. +/// +/// More information: +/// - [MDN documentation][mdn] +/// - [ECMAScript specification][spec] +/// +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions +/// [spec]: https://tc39.es/ecma262/#prod-AsyncArrowFunction +#[derive(Debug, Clone, Copy)] +pub(in crate::syntax::parser) struct AsyncArrowFunction { + name: Option, + allow_in: AllowIn, + allow_yield: AllowYield, +} + +impl AsyncArrowFunction { + /// Creates a new `AsyncArrowFunction` parser. + pub(in crate::syntax::parser) fn new(name: N, allow_in: I, allow_yield: Y) -> Self + where + N: Into>, + I: Into, + Y: Into, + { + Self { + name: name.into(), + allow_in: allow_in.into(), + allow_yield: allow_yield.into(), + } + } +} + +impl TokenParser for AsyncArrowFunction +where + R: Read, +{ + type Output = ast::function::AsyncArrowFunction; + + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { + let _timer = Profiler::global().start_event("AsyncArrowFunction", "Parsing"); + + cursor.expect((Keyword::Async, false), "async arrow function", interner)?; + cursor.peek_expect_no_lineterminator(0, "async arrow function", interner)?; + + let next_token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; + let (params, params_start_position) = if let TokenKind::Punctuator(Punctuator::OpenParen) = + &next_token.kind() + { + let params_start_position = cursor + .expect(Punctuator::OpenParen, "async arrow function", interner)? + .span() + .end(); + + let params = FormalParameters::new(false, true).parse(cursor, interner)?; + cursor.expect(Punctuator::CloseParen, "async arrow function", interner)?; + (params, params_start_position) + } else { + let params_start_position = next_token.span().start(); + let param = BindingIdentifier::new(self.allow_yield, true) + .parse(cursor, interner) + .context("async arrow function")?; + ( + FormalParameterList::try_from(FormalParameter::new( + Variable::from_identifier(param, None), + false, + )) + .expect("a single binding identifier without init is always a valid param list"), + params_start_position, + ) + }; + + cursor.peek_expect_no_lineterminator(0, "async arrow function", interner)?; + cursor.expect(Punctuator::Arrow, "async arrow function", interner)?; + + let body = AsyncConciseBody::new(self.allow_in).parse(cursor, interner)?; + + // Early Error: ArrowFormalParameters are UniqueFormalParameters. + if params.has_duplicates() { + return Err(ParseError::lex(LexError::Syntax( + "Duplicate parameter name not allowed in this context".into(), + params_start_position, + ))); + } + + // Early Error: It is a Syntax Error if CoverCallExpressionAndAsyncArrowHead Contains YieldExpression is true. + if contains(¶ms, ContainsSymbol::YieldExpression) { + return Err(ParseError::lex(LexError::Syntax( + "Yield expression not allowed in this context".into(), + params_start_position, + ))); + } + + // Early Error: It is a Syntax Error if CoverCallExpressionAndAsyncArrowHead Contains AwaitExpression is true. + if contains(¶ms, ContainsSymbol::AwaitExpression) { + return Err(ParseError::lex(LexError::Syntax( + "Await expression not allowed in this context".into(), + params_start_position, + ))); + } + + // Early Error: It is a Syntax Error if AsyncConciseBodyContainsUseStrict of AsyncConciseBody is true and + // IsSimpleParameterList of CoverCallExpressionAndAsyncArrowHead is false. + if body.strict() && !params.is_simple() { + return Err(ParseError::lex(LexError::Syntax( + "Illegal 'use strict' directive in function with non-simple parameter list".into(), + params_start_position, + ))); + } + + // Early Error: It is a Syntax Error if any element of the BoundNames of CoverCallExpressionAndAsyncArrowHead + // also occurs in the LexicallyDeclaredNames of AsyncConciseBody. + name_in_lexically_declared_names( + ¶ms, + &body.lexically_declared_names_top_level(), + params_start_position, + )?; + + Ok(ast::function::AsyncArrowFunction::new( + self.name, params, body, + )) + } +} + +/// +#[derive(Debug, Clone, Copy)] +pub(in crate::syntax::parser) struct AsyncConciseBody { + allow_in: AllowIn, +} + +impl AsyncConciseBody { + /// Creates a new `AsyncConciseBody` parser. + pub(in crate::syntax::parser) fn new(allow_in: I) -> Self + where + I: Into, + { + Self { + allow_in: allow_in.into(), + } + } +} + +impl TokenParser for AsyncConciseBody +where + R: Read, +{ + type Output = StatementList; + + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { + match cursor + .peek(0, interner)? + .ok_or(ParseError::AbruptEnd)? + .kind() + { + TokenKind::Punctuator(Punctuator::OpenBlock) => { + cursor.next(interner)?; + let body = FunctionBody::new(false, true).parse(cursor, interner)?; + cursor.expect(Punctuator::CloseBlock, "async arrow function", interner)?; + Ok(body) + } + _ => Ok(StatementList::from(vec![ast::Statement::Return( + Return::new( + ExpressionBody::new(self.allow_in, true) + .parse(cursor, interner)? + .into(), + ), + ) + .into()])), + } + } +} diff --git a/boa_engine/src/syntax/parser/expression/assignment/mod.rs b/boa_engine/src/syntax/parser/expression/assignment/mod.rs index f7c8e02c05a..dd8d435900b 100644 --- a/boa_engine/src/syntax/parser/expression/assignment/mod.rs +++ b/boa_engine/src/syntax/parser/expression/assignment/mod.rs @@ -8,6 +8,7 @@ //! [spec]: https://tc39.es/ecma262/#sec-assignment-operators mod arrow_function; +mod async_arrow_function; mod conditional; mod exponentiation; mod r#yield; @@ -17,6 +18,7 @@ use crate::syntax::{ parser::{ expression::assignment::{ arrow_function::{ArrowFunction, ConciseBody}, + async_arrow_function::AsyncArrowFunction, conditional::ConditionalExpression, r#yield::YieldExpression, }, @@ -24,6 +26,7 @@ use crate::syntax::{ ParseResult, TokenParser, }, }; +use ast::operations::{contains, ContainsSymbol}; use boa_ast::{ self as ast, expression::{ @@ -138,6 +141,37 @@ where } } } + // AsyncArrowFunction[?In, ?Yield, ?Await] + TokenKind::Keyword((Keyword::Async, _)) => { + let skip_n = if cursor + .peek_is_line_terminator(0, interner)? + .ok_or(ParseError::AbruptEnd)? + { + 2 + } else { + 1 + }; + + if !cursor + .peek_is_line_terminator(skip_n, interner)? + .ok_or(ParseError::AbruptEnd)? + && matches!( + cursor + .peek(1, interner)? + .ok_or(ParseError::AbruptEnd)? + .kind(), + TokenKind::Identifier(_) + | TokenKind::Keyword((Keyword::Yield | Keyword::Await, _)) + | TokenKind::Punctuator(Punctuator::OpenParen) + ) + { + return Ok( + AsyncArrowFunction::new(self.name, self.allow_in, self.allow_yield) + .parse(cursor, interner)? + .into(), + ); + } + } _ => {} } @@ -178,6 +212,22 @@ where ))); } + // Early Error: It is a Syntax Error if ArrowParameters Contains YieldExpression is true. + if contains(¶meters, ContainsSymbol::YieldExpression) { + return Err(ParseError::lex(LexError::Syntax( + "Yield expression not allowed in this context".into(), + position, + ))); + } + + // Early Error: It is a Syntax Error if ArrowParameters Contains AwaitExpression is true. + if contains(¶meters, ContainsSymbol::AwaitExpression) { + return Err(ParseError::lex(LexError::Syntax( + "Await expression not allowed in this context".into(), + position, + ))); + } + // Early Error: It is a Syntax Error if ConciseBodyContainsUseStrict of ConciseBody is true // and IsSimpleParameterList of ArrowParameters is false. if body.strict() && !parameters.is_simple() { diff --git a/boa_engine/src/syntax/parser/expression/primary/async_function_expression/mod.rs b/boa_engine/src/syntax/parser/expression/primary/async_function_expression/mod.rs index d9775f19318..2faed43b692 100644 --- a/boa_engine/src/syntax/parser/expression/primary/async_function_expression/mod.rs +++ b/boa_engine/src/syntax/parser/expression/primary/async_function_expression/mod.rs @@ -62,13 +62,16 @@ where interner, )?; - let name = match cursor + let (name, has_binding_identifier) = match cursor .peek(0, interner)? .ok_or(ParseError::AbruptEnd)? .kind() { - TokenKind::Punctuator(Punctuator::OpenParen) => self.name, - _ => Some(BindingIdentifier::new(self.allow_yield, true).parse(cursor, interner)?), + TokenKind::Punctuator(Punctuator::OpenParen) => (self.name, false), + _ => ( + Some(BindingIdentifier::new(self.allow_yield, true).parse(cursor, interner)?), + true, + ), }; // Early Error: If BindingIdentifier is present and the source code matching BindingIdentifier is strict mode code, @@ -134,7 +137,7 @@ where params_start_position, )?; - let function = AsyncFunction::new(name, params, body); + let function = AsyncFunction::new(name, params, body, has_binding_identifier); if contains(&function, ContainsSymbol::Super) { return Err(ParseError::lex(LexError::Syntax( diff --git a/boa_engine/src/syntax/parser/expression/primary/async_function_expression/tests.rs b/boa_engine/src/syntax/parser/expression/primary/async_function_expression/tests.rs index cd11c496d01..ad58521acbb 100644 --- a/boa_engine/src/syntax/parser/expression/primary/async_function_expression/tests.rs +++ b/boa_engine/src/syntax/parser/expression/primary/async_function_expression/tests.rs @@ -30,6 +30,7 @@ fn check_async_expression() { Return::new(Some(Literal::from(1).into())), ))] .into(), + false, ) .into(), ), @@ -73,6 +74,7 @@ fn check_nested_async_expression() { ))) .into()] .into(), + false, ) .into(), ), @@ -82,6 +84,7 @@ fn check_nested_async_expression() { )) .into()] .into(), + false, ) .into(), ), diff --git a/boa_engine/src/syntax/parser/expression/primary/async_generator_expression/mod.rs b/boa_engine/src/syntax/parser/expression/primary/async_generator_expression/mod.rs index 6632d437ee6..30c8f21a1f2 100644 --- a/boa_engine/src/syntax/parser/expression/primary/async_generator_expression/mod.rs +++ b/boa_engine/src/syntax/parser/expression/primary/async_generator_expression/mod.rs @@ -71,13 +71,16 @@ where interner, )?; - let name = match cursor + let (name, has_binding_identifier) = match cursor .peek(0, interner)? .ok_or(ParseError::AbruptEnd)? .kind() { - TokenKind::Punctuator(Punctuator::OpenParen) => self.name, - _ => Some(BindingIdentifier::new(true, true).parse(cursor, interner)?), + TokenKind::Punctuator(Punctuator::OpenParen) => (self.name, false), + _ => ( + Some(BindingIdentifier::new(true, true).parse(cursor, interner)?), + true, + ), }; // Early Error: If BindingIdentifier is present and the source code matching BindingIdentifier is strict @@ -166,7 +169,7 @@ where params_start_position, )?; - let function = AsyncGenerator::new(name, params, body); + let function = AsyncGenerator::new(name, params, body, has_binding_identifier); if contains(&function, ContainsSymbol::Super) { return Err(ParseError::lex(LexError::Syntax( @@ -175,7 +178,6 @@ where ))); } - //implement the below AsyncGeneratorExpr in ast::node Ok(function) } } diff --git a/boa_engine/src/syntax/parser/expression/primary/async_generator_expression/tests.rs b/boa_engine/src/syntax/parser/expression/primary/async_generator_expression/tests.rs index e275bee8970..3a9f3bb5ff9 100644 --- a/boa_engine/src/syntax/parser/expression/primary/async_generator_expression/tests.rs +++ b/boa_engine/src/syntax/parser/expression/primary/async_generator_expression/tests.rs @@ -33,6 +33,7 @@ fn check_async_generator_expr() { Return::new(Some(Literal::from(1).into())), ))] .into(), + false, ) .into(), ), @@ -75,6 +76,7 @@ fn check_nested_async_generator_expr() { Return::new(Some(Literal::from(1).into())), ))] .into(), + false, ) .into(), ), @@ -84,6 +86,7 @@ fn check_nested_async_generator_expr() { )) .into()] .into(), + false, ) .into(), ), diff --git a/boa_engine/src/syntax/parser/expression/primary/function_expression/mod.rs b/boa_engine/src/syntax/parser/expression/primary/function_expression/mod.rs index 68c2d5f17e3..0e1a45ae65c 100644 --- a/boa_engine/src/syntax/parser/expression/primary/function_expression/mod.rs +++ b/boa_engine/src/syntax/parser/expression/primary/function_expression/mod.rs @@ -60,7 +60,7 @@ where fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("FunctionExpression", "Parsing"); - let name = match cursor + let (name, has_binding_identifier) = match cursor .peek(0, interner)? .ok_or(ParseError::AbruptEnd)? .kind() @@ -69,8 +69,11 @@ where | TokenKind::Keyword(( Keyword::Yield | Keyword::Await | Keyword::Async | Keyword::Of, _, - )) => Some(BindingIdentifier::new(false, false).parse(cursor, interner)?), - _ => self.name, + )) => ( + Some(BindingIdentifier::new(false, false).parse(cursor, interner)?), + true, + ), + _ => (self.name, false), }; // Early Error: If BindingIdentifier is present and the source code matching BindingIdentifier is strict mode code, @@ -128,7 +131,8 @@ where params_start_position, )?; - let function = Function::new(name, params, body); + let function = + Function::new_with_binding_identifier(name, params, body, has_binding_identifier); if contains(&function, ContainsSymbol::Super) { return Err(ParseError::lex(LexError::Syntax( diff --git a/boa_engine/src/syntax/parser/expression/primary/function_expression/tests.rs b/boa_engine/src/syntax/parser/expression/primary/function_expression/tests.rs index cf63d1735ce..b3ba91536bc 100644 --- a/boa_engine/src/syntax/parser/expression/primary/function_expression/tests.rs +++ b/boa_engine/src/syntax/parser/expression/primary/function_expression/tests.rs @@ -101,10 +101,11 @@ fn check_function_non_reserved_keyword() { vec![Variable::from_identifier( $interner.get_or_intern_static("add", utf16!("add")).into(), Some( - Function::new( + Function::new_with_binding_identifier( Some($interner.get_or_intern_static($keyword, utf16!($keyword)).into()), FormalParameterList::default(), vec![StatementListItem::Statement(Statement::Return(Return::new(Some(Literal::from(1).into()))))].into(), + true, ) .into(), ), diff --git a/boa_engine/src/syntax/parser/expression/primary/generator_expression/mod.rs b/boa_engine/src/syntax/parser/expression/primary/generator_expression/mod.rs index 6b4eb1595b1..9c1c8a883a1 100644 --- a/boa_engine/src/syntax/parser/expression/primary/generator_expression/mod.rs +++ b/boa_engine/src/syntax/parser/expression/primary/generator_expression/mod.rs @@ -66,13 +66,16 @@ where interner, )?; - let name = match cursor + let (name, has_binding_identifier) = match cursor .peek(0, interner)? .ok_or(ParseError::AbruptEnd)? .kind() { - TokenKind::Punctuator(Punctuator::OpenParen) => self.name, - _ => Some(BindingIdentifier::new(true, false).parse(cursor, interner)?), + TokenKind::Punctuator(Punctuator::OpenParen) => (self.name, false), + _ => ( + Some(BindingIdentifier::new(true, false).parse(cursor, interner)?), + true, + ), }; // Early Error: If BindingIdentifier is present and the source code matching BindingIdentifier is strict mode code, @@ -130,7 +133,7 @@ where params_start_position, )?; - let function = Generator::new(name, params, body); + let function = Generator::new(name, params, body, has_binding_identifier); if contains(&function, ContainsSymbol::Super) { return Err(ParseError::lex(LexError::Syntax( diff --git a/boa_engine/src/syntax/parser/expression/primary/generator_expression/tests.rs b/boa_engine/src/syntax/parser/expression/primary/generator_expression/tests.rs index 819dafcb3a4..b1bce83084b 100644 --- a/boa_engine/src/syntax/parser/expression/primary/generator_expression/tests.rs +++ b/boa_engine/src/syntax/parser/expression/primary/generator_expression/tests.rs @@ -28,6 +28,7 @@ fn check_generator_function_expression() { Expression::from(Yield::new(Some(Literal::from(1).into()), false)), ))] .into(), + false, ) .into(), ), @@ -60,6 +61,7 @@ fn check_generator_function_delegate_yield_expression() { Expression::from(Yield::new(Some(Literal::from(1).into()), true)), ))] .into(), + false, ) .into(), ), diff --git a/boa_engine/src/syntax/parser/expression/primary/mod.rs b/boa_engine/src/syntax/parser/expression/primary/mod.rs index 11306c8b995..5732e6c729a 100644 --- a/boa_engine/src/syntax/parser/expression/primary/mod.rs +++ b/boa_engine/src/syntax/parser/expression/primary/mod.rs @@ -332,51 +332,123 @@ where let mut expressions = Vec::new(); let mut tailing_comma = None; - let close_span = loop { - let next = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; - match next.kind() { - TokenKind::Punctuator(Punctuator::CloseParen) => { - let span = next.span(); - cursor.next(interner).expect("token disappeared"); - break span; - } - TokenKind::Punctuator(Punctuator::Comma) => { - let span = next.span(); - cursor.next(interner).expect("token disappeared"); - if let Some(token) = cursor.next_if(Punctuator::CloseParen, interner)? { - tailing_comma = Some(span); - break token.span(); + let next = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; + let span = match next.kind() { + TokenKind::Punctuator(Punctuator::CloseParen) => { + let span = next.span(); + cursor.next(interner).expect("token disappeared"); + span + } + TokenKind::Punctuator(Punctuator::Spread) => { + cursor.next(interner).expect("token disappeared"); + let next = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; + match next.kind() { + TokenKind::Punctuator(Punctuator::OpenBlock) => { + let bindings = + ObjectBindingPattern::new(self.allow_yield, self.allow_await) + .parse(cursor, interner)?; + expressions.push(InnerExpression::SpreadObject(bindings)); + } + TokenKind::Punctuator(Punctuator::OpenBracket) => { + let bindings = ArrayBindingPattern::new(self.allow_yield, self.allow_await) + .parse(cursor, interner)?; + expressions.push(InnerExpression::SpreadArray(bindings)); + } + _ => { + let binding = BindingIdentifier::new(self.allow_yield, self.allow_await) + .parse(cursor, interner)?; + expressions.push(InnerExpression::SpreadBinding(binding)); } } - TokenKind::Punctuator(Punctuator::Spread) => { - cursor.next(interner).expect("token disappeared"); - let next = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; - match next.kind() { - TokenKind::Punctuator(Punctuator::OpenBlock) => { - let bindings = - ObjectBindingPattern::new(self.allow_yield, self.allow_await) - .parse(cursor, interner)?; - expressions.push(InnerExpression::SpreadObject(bindings)); - } - TokenKind::Punctuator(Punctuator::OpenBracket) => { - let bindings = - ArrayBindingPattern::new(self.allow_yield, self.allow_await) - .parse(cursor, interner)?; - expressions.push(InnerExpression::SpreadArray(bindings)); - } - _ => { - let binding = - BindingIdentifier::new(self.allow_yield, self.allow_await) - .parse(cursor, interner)?; - expressions.push(InnerExpression::SpreadBinding(binding)); + + cursor + .expect( + Punctuator::CloseParen, + "CoverParenthesizedExpressionAndArrowParameterList", + interner, + )? + .span() + } + _ => { + let expression = + Expression::new(self.name, true, self.allow_yield, self.allow_await) + .parse(cursor, interner)?; + expressions.push(InnerExpression::Expression(expression)); + + let next = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; + match next.kind() { + TokenKind::Punctuator(Punctuator::CloseParen) => { + let span = next.span(); + cursor.next(interner).expect("token disappeared"); + span + } + TokenKind::Punctuator(Punctuator::Comma) => { + cursor.next(interner).expect("token disappeared"); + let next = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; + match next.kind() { + TokenKind::Punctuator(Punctuator::CloseParen) => { + let span = next.span(); + tailing_comma = Some(next.span()); + cursor.next(interner).expect("token disappeared"); + span + } + TokenKind::Punctuator(Punctuator::Spread) => { + cursor.next(interner).expect("token disappeared"); + let next = + cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; + match next.kind() { + TokenKind::Punctuator(Punctuator::OpenBlock) => { + let bindings = ObjectBindingPattern::new( + self.allow_yield, + self.allow_await, + ) + .parse(cursor, interner)?; + expressions.push(InnerExpression::SpreadObject(bindings)); + } + TokenKind::Punctuator(Punctuator::OpenBracket) => { + let bindings = ArrayBindingPattern::new( + self.allow_yield, + self.allow_await, + ) + .parse(cursor, interner)?; + expressions.push(InnerExpression::SpreadArray(bindings)); + } + _ => { + let binding = BindingIdentifier::new( + self.allow_yield, + self.allow_await, + ) + .parse(cursor, interner)?; + expressions.push(InnerExpression::SpreadBinding(binding)); + } + } + + cursor + .expect( + Punctuator::CloseParen, + "CoverParenthesizedExpressionAndArrowParameterList", + interner, + )? + .span() + } + _ => { + return Err(ParseError::expected( + vec![")".to_owned(), "...".to_owned()], + next.kind().to_string(interner), + next.span(), + "CoverParenthesizedExpressionAndArrowParameterList", + )) + } } } - } - _ => { - let expression = - Expression::new(self.name, true, self.allow_yield, self.allow_await) - .parse(cursor, interner)?; - expressions.push(InnerExpression::Expression(expression)); + _ => { + return Err(ParseError::expected( + vec![")".to_owned(), ",".to_owned()], + next.kind().to_string(interner), + next.span(), + "CoverParenthesizedExpressionAndArrowParameterList", + )) + } } } }; @@ -403,14 +475,14 @@ where if expressions.is_empty() { return Err(ParseError::unexpected( Punctuator::CloseParen, - close_span, + span, "empty parenthesized expression", )); } if expressions.len() != 1 { return Err(ParseError::unexpected( Punctuator::CloseParen, - close_span, + span, "multiple expressions in parenthesized expression", )); } @@ -419,7 +491,7 @@ where } return Err(ParseError::unexpected( Punctuator::CloseParen, - close_span, + span, "parenthesized expression with spread expressions", )); } diff --git a/boa_engine/src/syntax/parser/expression/primary/object_initializer/mod.rs b/boa_engine/src/syntax/parser/expression/primary/object_initializer/mod.rs index 603aae48476..4e1fd01e3a2 100644 --- a/boa_engine/src/syntax/parser/expression/primary/object_initializer/mod.rs +++ b/boa_engine/src/syntax/parser/expression/primary/object_initializer/mod.rs @@ -729,7 +729,7 @@ where body_start, )?; - let method = MethodDefinition::Generator(Generator::new(None, params, body)); + let method = MethodDefinition::Generator(Generator::new(None, params, body, false)); if contains(&method, ContainsSymbol::Super) { return Err(ParseError::lex(LexError::Syntax( @@ -843,7 +843,8 @@ where body_start, )?; - let method = MethodDefinition::AsyncGenerator(AsyncGenerator::new(None, params, body)); + let method = + MethodDefinition::AsyncGenerator(AsyncGenerator::new(None, params, body, false)); if contains(&method, ContainsSymbol::Super) { return Err(ParseError::lex(LexError::Syntax( @@ -928,7 +929,7 @@ where body_start, )?; - let method = MethodDefinition::Async(AsyncFunction::new(None, params, body)); + let method = MethodDefinition::Async(AsyncFunction::new(None, params, body, false)); if contains(&method, ContainsSymbol::Super) { return Err(ParseError::lex(LexError::Syntax( diff --git a/boa_engine/src/syntax/parser/expression/primary/object_initializer/tests.rs b/boa_engine/src/syntax/parser/expression/primary/object_initializer/tests.rs index 37b0c4738a0..ba851af67f4 100644 --- a/boa_engine/src/syntax/parser/expression/primary/object_initializer/tests.rs +++ b/boa_engine/src/syntax/parser/expression/primary/object_initializer/tests.rs @@ -410,6 +410,7 @@ fn check_async_method() { None, FormalParameterList::default(), StatementList::default(), + false, )), )]; @@ -443,6 +444,7 @@ fn check_async_generator_method() { None, FormalParameterList::default(), StatementList::default(), + false, )), )]; diff --git a/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_function_decl/mod.rs b/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_function_decl/mod.rs index 0d7abe6b6dd..a5617850c2f 100644 --- a/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_function_decl/mod.rs +++ b/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_function_decl/mod.rs @@ -88,6 +88,11 @@ where let result = parse_callable_declaration(&self, cursor, interner)?; - Ok(AsyncFunction::new(Some(result.0), result.1, result.2)) + Ok(AsyncFunction::new( + Some(result.0), + result.1, + result.2, + false, + )) } } diff --git a/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_function_decl/tests.rs b/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_function_decl/tests.rs index 6b5f8382b05..404b0429f71 100644 --- a/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_function_decl/tests.rs +++ b/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_function_decl/tests.rs @@ -20,6 +20,7 @@ fn async_function_declaration() { ), FormalParameterList::default(), StatementList::default(), + false, )) .into()], interner, @@ -40,6 +41,7 @@ fn async_function_declaration_keywords() { ), FormalParameterList::default(), StatementList::default(), + false, )) .into()], interner, @@ -56,6 +58,7 @@ fn async_function_declaration_keywords() { ), FormalParameterList::default(), StatementList::default(), + false, )) .into()], interner, diff --git a/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_generator_decl/mod.rs b/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_generator_decl/mod.rs index 2f5b0b2f4e1..3846ac2d399 100644 --- a/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_generator_decl/mod.rs +++ b/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_generator_decl/mod.rs @@ -107,6 +107,11 @@ where let result = parse_callable_declaration(&self, cursor, interner)?; - Ok(AsyncGenerator::new(Some(result.0), result.1, result.2)) + Ok(AsyncGenerator::new( + Some(result.0), + result.1, + result.2, + false, + )) } } diff --git a/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_generator_decl/tests.rs b/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_generator_decl/tests.rs index 15ad31e4bfb..3bf42d4c543 100644 --- a/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_generator_decl/tests.rs +++ b/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_generator_decl/tests.rs @@ -15,6 +15,7 @@ fn async_generator_function_declaration() { Some(interner.get_or_intern_static("gen", utf16!("gen")).into()), FormalParameterList::default(), StatementList::default(), + false, )) .into()], interner, diff --git a/boa_engine/src/syntax/parser/statement/declaration/hoistable/generator_decl/mod.rs b/boa_engine/src/syntax/parser/statement/declaration/hoistable/generator_decl/mod.rs index 8ebdbb14ce6..4874fdee02b 100644 --- a/boa_engine/src/syntax/parser/statement/declaration/hoistable/generator_decl/mod.rs +++ b/boa_engine/src/syntax/parser/statement/declaration/hoistable/generator_decl/mod.rs @@ -83,6 +83,6 @@ where let result = parse_callable_declaration(&self, cursor, interner)?; - Ok(Generator::new(Some(result.0), result.1, result.2)) + Ok(Generator::new(Some(result.0), result.1, result.2, false)) } } diff --git a/boa_engine/src/syntax/parser/statement/declaration/hoistable/generator_decl/tests.rs b/boa_engine/src/syntax/parser/statement/declaration/hoistable/generator_decl/tests.rs index 0d568a724e4..a78d4f70873 100644 --- a/boa_engine/src/syntax/parser/statement/declaration/hoistable/generator_decl/tests.rs +++ b/boa_engine/src/syntax/parser/statement/declaration/hoistable/generator_decl/tests.rs @@ -15,6 +15,7 @@ fn generator_function_declaration() { Some(interner.get_or_intern_static("gen", utf16!("gen")).into()), FormalParameterList::default(), StatementList::default(), + false, )) .into()], interner, diff --git a/boa_engine/src/vm/code_block.rs b/boa_engine/src/vm/code_block.rs index 7b2e7fd53f1..f42bd717525 100644 --- a/boa_engine/src/vm/code_block.rs +++ b/boa_engine/src/vm/code_block.rs @@ -62,6 +62,9 @@ pub struct CodeBlock { #[unsafe_ignore_trace] pub(crate) name: Sym, + /// Indicates if the function is an expression and has a binding identifier. + pub(crate) has_binding_identifier: bool, + /// The number of arguments expected. pub(crate) length: u32, @@ -123,6 +126,7 @@ impl CodeBlock { num_bindings: 0, functions: Vec::new(), name, + has_binding_identifier: false, length, strict, this_mode: ThisMode::Global, @@ -230,6 +234,7 @@ impl CodeBlock { format!("{operand1}, {operand2}") } Opcode::GetArrowFunction + | Opcode::GetAsyncArrowFunction | Opcode::GetFunction | Opcode::GetFunctionAsync | Opcode::GetGenerator @@ -735,26 +740,28 @@ impl JsObject { ) }; - if code.params.has_expressions() { - context.realm.environments.push_function( - code.num_bindings, - code.compile_environments[1].clone(), - this, - self.clone(), - None, - lexical_this_mode, - ); - } else { - context.realm.environments.push_function( - code.num_bindings, - code.compile_environments[0].clone(), - this, - self.clone(), - None, - lexical_this_mode, + let compile_time_environment_index = usize::from(code.params.has_expressions()); + + if code.has_binding_identifier { + let index = context.realm.environments.push_declarative( + 1, + code.compile_environments[compile_time_environment_index + 1].clone(), ); + context + .realm + .environments + .put_value(index, 0, self.clone().into()); } + context.realm.environments.push_function( + code.num_bindings, + code.compile_environments[compile_time_environment_index].clone(), + this, + self.clone(), + None, + lexical_this_mode, + ); + if let Some(binding) = code.arguments_binding { let arguments_obj = if code.strict || !code.params.is_simple() { Arguments::create_unmapped_arguments_object(args, context) @@ -795,6 +802,7 @@ impl JsObject { let param_count = code.params.as_ref().len(); let has_expressions = code.params.has_expressions(); + let has_binding_identifier = code.has_binding_identifier; context.vm.push_frame(CallFrame { code, @@ -825,6 +833,10 @@ impl JsObject { context.realm.environments.pop(); } + if has_binding_identifier { + context.realm.environments.pop(); + } + std::mem::swap(&mut environments, &mut context.realm.environments); let (result, _) = result?; @@ -859,26 +871,28 @@ impl JsObject { ) }; - if code.params.has_expressions() { - context.realm.environments.push_function( - code.num_bindings, - code.compile_environments[1].clone(), - this, - self.clone(), - None, - lexical_this_mode, - ); - } else { - context.realm.environments.push_function( - code.num_bindings, - code.compile_environments[0].clone(), - this, - self.clone(), - None, - lexical_this_mode, + let compile_time_environment_index = usize::from(code.params.has_expressions()); + + if code.has_binding_identifier { + let index = context.realm.environments.push_declarative( + 1, + code.compile_environments[compile_time_environment_index + 1].clone(), ); + context + .realm + .environments + .put_value(index, 0, self.clone().into()); } + context.realm.environments.push_function( + code.num_bindings, + code.compile_environments[compile_time_environment_index].clone(), + this, + self.clone(), + None, + lexical_this_mode, + ); + if let Some(binding) = code.arguments_binding { let arguments_obj = if code.strict || !code.params.is_simple() { Arguments::create_unmapped_arguments_object(args, context) @@ -919,6 +933,7 @@ impl JsObject { let param_count = code.params.as_ref().len(); let has_expressions = code.params.has_expressions(); + let has_binding_identifier = code.has_binding_identifier; context.vm.push_frame(CallFrame { code, @@ -949,8 +964,11 @@ impl JsObject { context.realm.environments.pop(); } - std::mem::swap(&mut environments, &mut context.realm.environments); + if has_binding_identifier { + context.realm.environments.pop(); + } + std::mem::swap(&mut environments, &mut context.realm.environments); Ok(promise.into()) } Function::Generator { @@ -978,26 +996,28 @@ impl JsObject { ) }; - if code.params.has_expressions() { - context.realm.environments.push_function( - code.num_bindings, - code.compile_environments[1].clone(), - this, - self.clone(), - None, - lexical_this_mode, - ); - } else { - context.realm.environments.push_function( - code.num_bindings, - code.compile_environments[0].clone(), - this, - self.clone(), - None, - lexical_this_mode, + let compile_time_environment_index = usize::from(code.params.has_expressions()); + + if code.has_binding_identifier { + let index = context.realm.environments.push_declarative( + 1, + code.compile_environments[compile_time_environment_index + 1].clone(), ); + context + .realm + .environments + .put_value(index, 0, self.clone().into()); } + context.realm.environments.push_function( + code.num_bindings, + code.compile_environments[compile_time_environment_index].clone(), + this, + self.clone(), + None, + lexical_this_mode, + ); + if let Some(binding) = code.arguments_binding { let arguments_obj = if code.strict || !code.params.is_simple() { Arguments::create_unmapped_arguments_object(args, context) @@ -1115,26 +1135,28 @@ impl JsObject { ) }; - if code.params.has_expressions() { - context.realm.environments.push_function( - code.num_bindings, - code.compile_environments[1].clone(), - this, - self.clone(), - None, - lexical_this_mode, - ); - } else { - context.realm.environments.push_function( - code.num_bindings, - code.compile_environments[0].clone(), - this, - self.clone(), - None, - lexical_this_mode, + let compile_time_environment_index = usize::from(code.params.has_expressions()); + + if code.has_binding_identifier { + let index = context.realm.environments.push_declarative( + 1, + code.compile_environments[compile_time_environment_index + 1].clone(), ); + context + .realm + .environments + .put_value(index, 0, self.clone().into()); } + context.realm.environments.push_function( + code.num_bindings, + code.compile_environments[compile_time_environment_index].clone(), + this, + self.clone(), + None, + lexical_this_mode, + ); + if let Some(binding) = code.arguments_binding { let arguments_obj = if code.strict || !code.params.is_simple() { Arguments::create_unmapped_arguments_object(args, context) @@ -1352,26 +1374,28 @@ impl JsObject { let new_target = this_target.as_object().expect("must be object"); - if code.params.has_expressions() { - context.realm.environments.push_function( - code.num_bindings, - code.compile_environments[1].clone(), - this.clone().map(Into::into), - self.clone(), - Some(new_target.clone()), - false, - ); - } else { - context.realm.environments.push_function( - code.num_bindings, - code.compile_environments[0].clone(), - this.clone().map(Into::into), - self.clone(), - Some(new_target.clone()), - false, + let compile_time_environment_index = usize::from(code.params.has_expressions()); + + if code.has_binding_identifier { + let index = context.realm.environments.push_declarative( + 1, + code.compile_environments[compile_time_environment_index + 1].clone(), ); + context + .realm + .environments + .put_value(index, 0, self.clone().into()); } + context.realm.environments.push_function( + code.num_bindings, + code.compile_environments[compile_time_environment_index].clone(), + this.clone().map(Into::into), + self.clone(), + Some(new_target.clone()), + false, + ); + let mut arguments_in_parameter_names = false; let mut is_simple_parameter_list = true; let mut has_parameter_expressions = false; @@ -1425,6 +1449,7 @@ impl JsObject { } let param_count = code.params.as_ref().len(); + let has_binding_identifier = code.has_binding_identifier; context.vm.push_frame(CallFrame { code, @@ -1454,6 +1479,10 @@ impl JsObject { environment = context.realm.environments.pop(); } + if has_binding_identifier { + context.realm.environments.pop(); + } + std::mem::swap(&mut environments, &mut context.realm.environments); let (result, _) = result?; diff --git a/boa_engine/src/vm/mod.rs b/boa_engine/src/vm/mod.rs index 0276380183e..49ba10667e1 100644 --- a/boa_engine/src/vm/mod.rs +++ b/boa_engine/src/vm/mod.rs @@ -167,7 +167,7 @@ impl Context { let promise_capability = self .realm .environments - .get_this_environment() + .current_function_slots() .as_function_slots() .and_then(|slots| { let slots_borrow = slots.borrow(); diff --git a/boa_engine/src/vm/opcode/define/mod.rs b/boa_engine/src/vm/opcode/define/mod.rs index 785dea144ac..8e02d4e8105 100644 --- a/boa_engine/src/vm/opcode/define/mod.rs +++ b/boa_engine/src/vm/opcode/define/mod.rs @@ -64,6 +64,9 @@ impl Operation for DefInitVar { let index = context.vm.read::(); let value = context.vm.pop(); let binding_locator = context.vm.frame().code.bindings[index as usize]; + if binding_locator.is_silent() { + return Ok(ShouldExit::False); + } binding_locator.throw_mutate_immutable(context)?; if binding_locator.is_global() { diff --git a/boa_engine/src/vm/opcode/get/function.rs b/boa_engine/src/vm/opcode/get/function.rs index 428aebe8fcd..82d93dc4a68 100644 --- a/boa_engine/src/vm/opcode/get/function.rs +++ b/boa_engine/src/vm/opcode/get/function.rs @@ -23,6 +23,26 @@ impl Operation for GetArrowFunction { } } +/// `GetAsyncArrowFunction` implements the Opcode Operation for `Opcode::GetAsyncArrowFunction` +/// +/// Operation: +/// - Get async arrow function from the pre-compiled inner functions. +#[derive(Debug, Clone, Copy)] +pub(crate) struct GetAsyncArrowFunction; + +impl Operation for GetAsyncArrowFunction { + const NAME: &'static str = "GetAsyncArrowFunction"; + const INSTRUCTION: &'static str = "INST - GetAsyncArrowFunction"; + + fn execute(context: &mut Context) -> JsResult { + let index = context.vm.read::(); + let code = context.vm.frame().code.functions[index as usize].clone(); + let function = create_function_object(code, true, true, None, context); + context.vm.push(function); + Ok(ShouldExit::False) + } +} + /// `GetFunction` implements the Opcode Operation for `Opcode::GetFunction` /// /// Operation: diff --git a/boa_engine/src/vm/opcode/mod.rs b/boa_engine/src/vm/opcode/mod.rs index e39ac141391..8d8dc6f5f9e 100644 --- a/boa_engine/src/vm/opcode/mod.rs +++ b/boa_engine/src/vm/opcode/mod.rs @@ -1130,6 +1130,13 @@ generate_impl! { /// Stack: **=>** func GetArrowFunction, + /// Get async arrow function from the pre-compiled inner functions. + /// + /// Operands: address: `u32` + /// + /// Stack: **=>** func + GetAsyncArrowFunction, + /// Get function from the pre-compiled inner functions. /// /// Operands: address: `u32` diff --git a/boa_engine/src/vm/opcode/set/name.rs b/boa_engine/src/vm/opcode/set/name.rs index 6dfdf57a0b3..d0fe02c0375 100644 --- a/boa_engine/src/vm/opcode/set/name.rs +++ b/boa_engine/src/vm/opcode/set/name.rs @@ -19,6 +19,9 @@ impl Operation for SetName { let index = context.vm.read::(); let binding_locator = context.vm.frame().code.bindings[index as usize]; let value = context.vm.pop(); + if binding_locator.is_silent() { + return Ok(ShouldExit::False); + } binding_locator.throw_mutate_immutable(context)?; if binding_locator.is_global() {