diff --git a/boa_engine/src/builtins/async_function/mod.rs b/boa_engine/src/builtins/async_function/mod.rs index 51ee1d25c79..12ba9e4b7a2 100644 --- a/boa_engine/src/builtins/async_function/mod.rs +++ b/boa_engine/src/builtins/async_function/mod.rs @@ -100,7 +100,7 @@ impl AsyncFunction { context: &mut Context, ) -> JsResult { crate::builtins::function::BuiltInFunctionObject::create_dynamic_function( - new_target, args, true, context, + new_target, args, true, false, context, ) .map(Into::into) } diff --git a/boa_engine/src/builtins/function/mod.rs b/boa_engine/src/builtins/function/mod.rs index 2d401b721e9..1e237406d66 100644 --- a/boa_engine/src/builtins/function/mod.rs +++ b/boa_engine/src/builtins/function/mod.rs @@ -460,7 +460,7 @@ impl BuiltInFunctionObject { args: &[JsValue], context: &mut Context, ) -> JsResult { - Self::create_dynamic_function(new_target, args, false, context).map(Into::into) + Self::create_dynamic_function(new_target, args, false, false, context).map(Into::into) } /// `CreateDynamicFunction ( constructor, newTarget, kind, args )` @@ -473,40 +473,56 @@ impl BuiltInFunctionObject { new_target: &JsValue, args: &[JsValue], r#async: bool, + generator: bool, context: &mut Context, ) -> JsResult { - let prototype = - get_prototype_from_constructor(new_target, StandardConstructors::function, context)?; + let default = if r#async { + StandardConstructors::async_function + } else if generator { + StandardConstructors::generator_function + } else { + StandardConstructors::function + }; + + let prototype = get_prototype_from_constructor(new_target, default, context)?; if let Some((body_arg, args)) = args.split_last() { - let parameters = - if args.is_empty() { - FormalParameterList::empty() - } else { - let mut parameters = Vec::with_capacity(args.len()); - for arg in args { - parameters.push(arg.to_string(context)?); + let parameters = if args.is_empty() { + FormalParameterList::empty() + } else { + let mut parameters = Vec::with_capacity(args.len()); + for arg in args { + parameters.push(arg.to_string(context)?); + } + let mut parameters = parameters.join(","); + parameters.push(')'); + + let parameters = match Parser::new(parameters.as_bytes()).parse_formal_parameters( + context.interner_mut(), + generator, + r#async, + ) { + Ok(parameters) => parameters, + Err(e) => { + return context.throw_syntax_error(format!( + "failed to parse function parameters: {e}" + )) } - let mut parameters = parameters.join(","); - parameters.push(')'); - - let parameters = match Parser::new(parameters.as_bytes()) - .parse_formal_parameters(context.interner_mut(), false, r#async) - { - Ok(parameters) => parameters, - Err(e) => { - return context.throw_syntax_error(format!( - "failed to parse function parameters: {e}" - )) - } - }; - parameters }; + if generator && parameters.contains_yield_expression() { + return context.throw_syntax_error( + "yield expression is not allowed in formal parameter list of generator function", + ); + } + + parameters + }; + let body_arg = body_arg.to_string(context)?; let body = match Parser::new(body_arg.as_bytes()).parse_function_body( context.interner_mut(), - false, + generator, r#async, ) { Ok(statement_list) => statement_list, @@ -567,23 +583,45 @@ impl BuiltInFunctionObject { let code = crate::bytecompiler::ByteCompiler::compile_function_code( crate::bytecompiler::FunctionKind::Expression, - Some(Sym::EMPTY_STRING), + Some(Sym::ANONYMOUS), ¶meters, &body, + generator, false, + context, + )?; + + let environments = context.realm.environments.pop_to_global(); + + let function_object = if generator { + crate::vm::create_generator_function_object(code, context) + } else { + crate::vm::create_function_object(code, r#async, Some(prototype), context) + }; + + context.realm.environments.extend(environments); + + Ok(function_object) + } else if generator { + let code = crate::bytecompiler::ByteCompiler::compile_function_code( + crate::bytecompiler::FunctionKind::Expression, + Some(Sym::ANONYMOUS), + &FormalParameterList::empty(), + &StatementList::default(), + true, false, context, )?; let environments = context.realm.environments.pop_to_global(); - let function_object = crate::vm::create_function_object(code, r#async, context); + let function_object = crate::vm::create_generator_function_object(code, context); context.realm.environments.extend(environments); Ok(function_object) - } else if r#async { + } else { let code = crate::bytecompiler::ByteCompiler::compile_function_code( crate::bytecompiler::FunctionKind::Expression, - Some(Sym::EMPTY_STRING), + Some(Sym::ANONYMOUS), &FormalParameterList::empty(), &StatementList::default(), false, @@ -592,20 +630,11 @@ impl BuiltInFunctionObject { )?; let environments = context.realm.environments.pop_to_global(); - let function_object = crate::vm::create_function_object(code, r#async, context); + let function_object = + crate::vm::create_function_object(code, r#async, Some(prototype), context); context.realm.environments.extend(environments); Ok(function_object) - } else { - let this = JsObject::from_proto_and_data( - prototype, - ObjectData::function(Function::Native { - function: |_, _, _| Ok(JsValue::undefined()), - constructor: Some(ConstructorKind::Base), - }), - ); - - Ok(this) } } diff --git a/boa_engine/src/builtins/generator_function/mod.rs b/boa_engine/src/builtins/generator_function/mod.rs index 70161a144a3..637b81ef440 100644 --- a/boa_engine/src/builtins/generator_function/mod.rs +++ b/boa_engine/src/builtins/generator_function/mod.rs @@ -11,9 +11,11 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/GeneratorFunction use crate::{ - builtins::{function::Function, BuiltIn}, - context::intrinsics::StandardConstructors, - object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, + builtins::{ + function::{BuiltInFunctionObject, ConstructorKind, Function}, + BuiltIn, + }, + object::ObjectData, property::PropertyDescriptor, symbol::WellKnownSymbols, value::JsValue, @@ -21,8 +23,6 @@ use crate::{ }; use boa_profiler::Profiler; -use super::function::ConstructorKind; - /// The internal representation on a `Generator` object. #[derive(Debug, Clone, Copy)] pub struct GeneratorFunction; @@ -111,25 +111,18 @@ impl BuiltIn for GeneratorFunction { } impl GeneratorFunction { + /// `GeneratorFunction ( p1, p2, … , pn, body )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-generatorfunction pub(crate) fn constructor( new_target: &JsValue, - _: &[JsValue], + args: &[JsValue], context: &mut Context, ) -> JsResult { - let prototype = get_prototype_from_constructor( - new_target, - StandardConstructors::generator_function, - context, - )?; - - let this = JsObject::from_proto_and_data( - prototype, - ObjectData::function(Function::Native { - function: |_, _, _| Ok(JsValue::undefined()), - constructor: Some(ConstructorKind::Base), - }), - ); - - Ok(this.into()) + BuiltInFunctionObject::create_dynamic_function(new_target, args, false, true, context) + .map(Into::into) } } diff --git a/boa_engine/src/syntax/ast/node/yield/mod.rs b/boa_engine/src/syntax/ast/node/yield/mod.rs index 89a6d825202..16ff56979e2 100644 --- a/boa_engine/src/syntax/ast/node/yield/mod.rs +++ b/boa_engine/src/syntax/ast/node/yield/mod.rs @@ -29,13 +29,12 @@ impl Yield { } /// Creates a `Yield` AST node. - pub fn new(expr: OE, delegate: bool) -> Self + pub fn new(expr: Option, delegate: bool) -> Self where E: Into, - OE: Into>, { Self { - expr: expr.into().map(E::into).map(Box::new), + expr: expr.map(Into::into).map(Box::new), delegate, } } diff --git a/boa_engine/src/syntax/parser/expression/assignment/yield.rs b/boa_engine/src/syntax/parser/expression/assignment/yield.rs index b62bef9c047..fe19370b63a 100644 --- a/boa_engine/src/syntax/parser/expression/assignment/yield.rs +++ b/boa_engine/src/syntax/parser/expression/assignment/yield.rs @@ -14,7 +14,7 @@ use crate::syntax::{ Keyword, Punctuator, }, lexer::TokenKind, - parser::{AllowAwait, AllowIn, Cursor, ParseError, ParseResult, TokenParser}, + parser::{AllowAwait, AllowIn, Cursor, ParseResult, TokenParser}, }; use boa_interner::Interner; use boa_profiler::Profiler; @@ -63,16 +63,18 @@ where interner, )?; - let token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; + let token = if let Some(token) = cursor.peek(0, interner)? { + token + } else { + return Ok(Node::Yield(Yield::new::(None, false))); + }; + match token.kind() { TokenKind::Punctuator(Punctuator::Mul) => { cursor.next(interner)?.expect("token disappeared"); let expr = AssignmentExpression::new(None, self.allow_in, true, self.allow_await) .parse(cursor, interner)?; - Ok(Node::Yield(Yield::new::>( - Some(expr), - true, - ))) + Ok(Node::Yield(Yield::new(Some(expr), true))) } TokenKind::Identifier(_) | TokenKind::Punctuator( @@ -109,12 +111,9 @@ where | TokenKind::TemplateMiddle(_) => { let expr = AssignmentExpression::new(None, self.allow_in, true, self.allow_await) .parse(cursor, interner)?; - Ok(Node::Yield(Yield::new::>( - Some(expr), - false, - ))) + Ok(Node::Yield(Yield::new(Some(expr), false))) } - _ => Ok(Node::Yield(Yield::new::>(None, false))), + _ => Ok(Node::Yield(Yield::new::(None, false))), } } } 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 7ed6828e449..6b8b58a0450 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 @@ -25,7 +25,7 @@ fn check_generator_function_expression() { GeneratorExpr::new::<_, _, StatementList>( Some(gen), FormalParameterList::default(), - vec![Yield::new(Const::from(1), false).into()].into(), + vec![Yield::new(Some(Const::from(1)), false).into()].into(), ) .into(), ), @@ -53,7 +53,7 @@ fn check_generator_function_delegate_yield_expression() { GeneratorExpr::new::<_, _, StatementList>( Some(gen), FormalParameterList::default(), - vec![Yield::new(Const::from(1), true).into()].into(), + vec![Yield::new(Some(Const::from(1)), true).into()].into(), ) .into(), ), diff --git a/boa_engine/src/vm/code_block.rs b/boa_engine/src/vm/code_block.rs index 4317bd6bafe..9d89e8fb8fa 100644 --- a/boa_engine/src/vm/code_block.rs +++ b/boa_engine/src/vm/code_block.rs @@ -446,11 +446,14 @@ impl ToInternedString for CodeBlock { pub(crate) fn create_function_object( code: Gc, r#async: bool, + prototype: Option, context: &mut Context, ) -> JsObject { let _timer = Profiler::global().start_event("JsVmFunction::new", "vm"); - let function_prototype = if r#async { + let function_prototype = if let Some(prototype) = prototype { + prototype + } else if r#async { context .intrinsics() .constructors() diff --git a/boa_engine/src/vm/mod.rs b/boa_engine/src/vm/mod.rs index e11a15c7348..625471c6ebd 100644 --- a/boa_engine/src/vm/mod.rs +++ b/boa_engine/src/vm/mod.rs @@ -14,7 +14,7 @@ use crate::{ value::Numeric, vm::{ call_frame::CatchAddresses, - code_block::{create_generator_function_object, initialize_instance_elements, Readable}, + code_block::{initialize_instance_elements, Readable}, }, Context, JsBigInt, JsResult, JsString, JsValue, }; @@ -30,7 +30,7 @@ pub use {call_frame::CallFrame, code_block::CodeBlock, opcode::Opcode}; pub(crate) use { call_frame::{FinallyReturn, GeneratorResumeKind, TryStackEntry}, - code_block::create_function_object, + code_block::{create_function_object, create_generator_function_object}, opcode::BindingOpcode, }; @@ -1683,13 +1683,13 @@ impl Context { Opcode::GetFunction => { let index = self.vm.read::(); let code = self.vm.frame().code.functions[index as usize].clone(); - let function = create_function_object(code, false, self); + let function = create_function_object(code, false, None, self); self.vm.push(function); } Opcode::GetFunctionAsync => { let index = self.vm.read::(); let code = self.vm.frame().code.functions[index as usize].clone(); - let function = create_function_object(code, true, self); + let function = create_function_object(code, true, None, self); self.vm.push(function); } Opcode::GetGenerator => { diff --git a/boa_interner/src/sym.rs b/boa_interner/src/sym.rs index 60f147c707e..727f17d9f74 100644 --- a/boa_interner/src/sym.rs +++ b/boa_interner/src/sym.rs @@ -82,6 +82,9 @@ impl Sym { /// Symbol for the `"public"` string. pub const PUBLIC: Self = unsafe { Self::new_unchecked(22) }; + /// Symbol for the `"anonymous"` string. + pub const ANONYMOUS: Self = unsafe { Self::new_unchecked(23) }; + /// Creates a new [`Sym`] from the provided `value`, or returns `None` if `index` is zero. #[inline] pub(super) fn new(value: usize) -> Option { @@ -141,6 +144,7 @@ pub(super) static COMMON_STRINGS: phf::OrderedSet<&'static str> = { "private", "protected", "public", + "anonymous", }; // A `COMMON_STRINGS` of size `usize::MAX` would cause an overflow on our `Interner` sa::const_assert!(COMMON_STRINGS.len() < usize::MAX);