@@ -45,7 +45,6 @@ use boa_gc::{self, custom_trace, Finalize, Gc, Trace};
4545use boa_interner:: Sym ;
4646use boa_parser:: { Parser , Source } ;
4747use boa_profiler:: Profiler ;
48- use std:: io:: Read ;
4948use thin_vec:: ThinVec ;
5049
5150use super :: Proxy ;
@@ -390,46 +389,40 @@ impl BuiltInFunctionObject {
390389 generator : bool ,
391390 context : & mut Context ,
392391 ) -> JsResult < JsObject > {
393- // 1. Let currentRealm be the current Realm Record.
394- // 2. Perform ? HostEnsureCanCompileStrings(currentRealm).
395- context
396- . host_hooks ( )
397- . ensure_can_compile_strings ( context. realm ( ) . clone ( ) , context) ?;
398-
399- // 3. If newTarget is undefined, set newTarget to constructor.
392+ // 1. If newTarget is undefined, set newTarget to constructor.
400393 let new_target = if new_target. is_undefined ( ) {
401394 constructor. into ( )
402395 } else {
403396 new_target. clone ( )
404397 } ;
405398
406399 let default = if r#async && generator {
407- // 7 . Else,
408- // a. Assert: kind is asyncGenerator .
400+ // 5 . Else,
401+ // a. Assert: kind is async-generator .
409402 // b. Let prefix be "async function*".
410403 // c. Let exprSym be the grammar symbol AsyncGeneratorExpression.
411404 // d. Let bodySym be the grammar symbol AsyncGeneratorBody.
412405 // e. Let parameterSym be the grammar symbol FormalParameters[+Yield, +Await].
413406 // f. Let fallbackProto be "%AsyncGeneratorFunction.prototype%".
414407 StandardConstructors :: async_generator_function
415408 } else if r#async {
416- // 6 . Else if kind is async, then
409+ // 4 . Else if kind is async, then
417410 // a. Let prefix be "async function".
418411 // b. Let exprSym be the grammar symbol AsyncFunctionExpression.
419412 // c. Let bodySym be the grammar symbol AsyncFunctionBody.
420413 // d. Let parameterSym be the grammar symbol FormalParameters[~Yield, +Await].
421414 // e. Let fallbackProto be "%AsyncFunction.prototype%".
422415 StandardConstructors :: async_function
423416 } else if generator {
424- // 5 . Else if kind is generator, then
417+ // 3 . Else if kind is generator, then
425418 // a. Let prefix be "function*".
426419 // b. Let exprSym be the grammar symbol GeneratorExpression.
427420 // c. Let bodySym be the grammar symbol GeneratorBody.
428421 // d. Let parameterSym be the grammar symbol FormalParameters[+Yield, ~Await].
429422 // e. Let fallbackProto be "%GeneratorFunction.prototype%".
430423 StandardConstructors :: generator_function
431424 } else {
432- // 4 . If kind is normal, then
425+ // 2 . If kind is normal, then
433426 // a. Let prefix be "function".
434427 // b. Let exprSym be the grammar symbol FunctionExpression.
435428 // c. Let bodySym be the grammar symbol FunctionBody[~Yield, ~Await].
@@ -441,82 +434,127 @@ impl BuiltInFunctionObject {
441434 // 22. Let proto be ? GetPrototypeFromConstructor(newTarget, fallbackProto).
442435 let prototype = get_prototype_from_constructor ( & new_target, default, context) ?;
443436
444- let ( parameters, body) = if let Some ( ( body_arg, args) ) = args. split_last ( ) {
445- let parameters = if args. is_empty ( ) {
446- FormalParameterList :: default ( )
447- } else {
448- let mut parameters = Vec :: with_capacity ( args. len ( ) ) ;
449- for arg in args {
450- parameters. push ( arg. to_string ( context) ?) ;
451- }
452- let parameters = parameters. join ( utf16 ! ( "," ) ) ;
453-
454- // TODO: make parser generic to u32 iterators
455- let parameters = String :: from_utf16_lossy ( & parameters) ;
456- let mut parser = Parser :: new ( Source :: from_bytes ( & parameters) ) ;
457- parser. set_identifier ( context. next_parser_identifier ( ) ) ;
458-
459- let parameters = match parser. parse_formal_parameters (
460- context. interner_mut ( ) ,
461- generator,
462- r#async,
463- ) {
464- Ok ( parameters) => parameters,
465- Err ( e) => {
466- return Err ( JsNativeError :: syntax ( )
467- . with_message ( format ! ( "failed to parse function parameters: {e}" ) )
468- . into ( ) )
469- }
470- } ;
437+ // 6. Let argCount be the number of elements in parameterArgs.
438+ let ( body, param_list) = if let Some ( ( body, params) ) = args. split_last ( ) {
439+ // 7. Let bodyString be ? ToString(bodyArg).
440+ let body = body. to_string ( context) ?;
441+ // 8. Let parameterStrings be a new empty List.
442+ let mut parameters = Vec :: with_capacity ( args. len ( ) ) ;
443+ // 9. For each element arg of parameterArgs, do
444+ for param in params {
445+ // a. Append ? ToString(arg) to parameterStrings.
446+ parameters. push ( param. to_string ( context) ?) ;
447+ }
448+ ( body, parameters)
449+ } else {
450+ ( js_string ! ( ) , Vec :: new ( ) )
451+ } ;
452+ let current_realm = context. realm ( ) . clone ( ) ;
471453
472- if generator && contains ( & parameters, ContainsSymbol :: YieldExpression ) {
473- return Err ( JsNativeError :: syntax ( ) . with_message (
474- "yield expression is not allowed in formal parameter list of generator function" ,
475- ) . into ( ) ) ;
476- }
454+ context. host_hooks ( ) . ensure_can_compile_strings (
455+ current_realm,
456+ & param_list,
457+ & body,
458+ false ,
459+ context,
460+ ) ?;
477461
478- parameters
479- } ;
462+ let parameters = if param_list. is_empty ( ) {
463+ FormalParameterList :: default ( )
464+ } else {
465+ // 12. Let P be the empty String.
466+ // 13. If argCount > 0, then
467+ // a. Set P to parameterStrings[0].
468+ // b. Let k be 1.
469+ // c. Repeat, while k < argCount,
470+ // i. Let nextArgString be parameterStrings[k].
471+ // ii. Set P to the string-concatenation of P, "," (a comma), and nextArgString.
472+ // iii. Set k to k + 1.
473+ let parameters = param_list. join ( utf16 ! ( "," ) ) ;
474+ let mut parser = Parser :: new ( Source :: from_utf16 ( & parameters) ) ;
475+ parser. set_identifier ( context. next_parser_identifier ( ) ) ;
476+
477+ // 17. Let parameters be ParseText(StringToCodePoints(P), parameterSym).
478+ // 18. If parameters is a List of errors, throw a SyntaxError exception.
479+ let parameters = parser
480+ . parse_formal_parameters ( context. interner_mut ( ) , generator, r#async)
481+ . map_err ( |e| {
482+ JsNativeError :: syntax ( )
483+ . with_message ( format ! ( "failed to parse function parameters: {e}" ) )
484+ } ) ?;
480485
481486 // It is a Syntax Error if FormalParameters Contains YieldExpression is true.
482- if generator && r#async && contains ( & parameters, ContainsSymbol :: YieldExpression ) {
483- return Err ( JsNativeError :: syntax ( )
484- . with_message ( "yield expression not allowed in async generator parameters" )
485- . into ( ) ) ;
487+ if generator && contains ( & parameters, ContainsSymbol :: YieldExpression ) {
488+ return Err ( JsNativeError :: syntax ( ) . with_message (
489+ if r#async {
490+ "yield expression is not allowed in formal parameter list of async generator"
491+ } else {
492+ "yield expression is not allowed in formal parameter list of generator"
493+ }
494+ ) . into ( ) ) ;
486495 }
487496
488497 // It is a Syntax Error if FormalParameters Contains AwaitExpression is true.
489- if generator && r#async && contains ( & parameters, ContainsSymbol :: AwaitExpression ) {
498+ if r#async && contains ( & parameters, ContainsSymbol :: AwaitExpression ) {
490499 return Err ( JsNativeError :: syntax ( )
491- . with_message ( "await expression not allowed in async generator parameters" )
500+ . with_message (
501+ if generator {
502+ "await expression is not allowed in formal parameter list of async function"
503+ } else {
504+ "await expression is not allowed in formal parameter list of async generator"
505+ } )
492506 . into ( ) ) ;
493507 }
494508
495- // 11. Let bodyString be the string-concatenation of 0x000A (LINE FEED), ? ToString(bodyArg), and 0x000A (LINE FEED).
496- let body_arg = body_arg. to_string ( context) ?. to_std_string_escaped ( ) ;
497- let body = b"\n " . chain ( body_arg. as_bytes ( ) ) . chain ( b"\n " . as_slice ( ) ) ;
509+ parameters
510+ } ;
498511
499- // TODO: make parser generic to u32 iterators
500- let mut parser = Parser :: new ( Source :: from_reader ( body, None ) ) ;
512+ let body = if body. is_empty ( ) {
513+ FunctionBody :: default ( )
514+ } else {
515+ // 14. Let bodyParseString be the string-concatenation of 0x000A (LINE FEED), bodyString, and 0x000A (LINE FEED).
516+ let mut body_parse = Vec :: with_capacity ( body. len ( ) ) ;
517+ body_parse. push ( u16:: from ( b'\n' ) ) ;
518+ body_parse. extend_from_slice ( & body) ;
519+ body_parse. push ( u16:: from ( b'\n' ) ) ;
520+
521+ // 19. Let body be ParseText(StringToCodePoints(bodyParseString), bodySym).
522+ // 20. If body is a List of errors, throw a SyntaxError exception.
523+ let mut parser = Parser :: new ( Source :: from_utf16 ( & body_parse) ) ;
501524 parser. set_identifier ( context. next_parser_identifier ( ) ) ;
502525
503- let body = match parser. parse_function_body ( context. interner_mut ( ) , generator, r#async)
504- {
505- Ok ( statement_list) => statement_list,
506- Err ( e) => {
507- return Err ( JsNativeError :: syntax ( )
526+ // 19. Let body be ParseText(StringToCodePoints(bodyParseString), bodySym).
527+ // 20. If body is a List of errors, throw a SyntaxError exception.
528+ let body = parser
529+ . parse_function_body ( context. interner_mut ( ) , generator, r#async)
530+ . map_err ( |e| {
531+ JsNativeError :: syntax ( )
508532 . with_message ( format ! ( "failed to parse function body: {e}" ) )
509- . into ( ) )
510- }
511- } ;
533+ } ) ?;
534+
535+ // It is a Syntax Error if AllPrivateIdentifiersValid of StatementList with argument « »
536+ // is false unless the source text containing ScriptBody is eval code that is being
537+ // processed by a direct eval.
538+ // https://tc39.es/ecma262/#sec-scripts-static-semantics-early-errors
539+ if !all_private_identifiers_valid ( & body, Vec :: new ( ) ) {
540+ return Err ( JsNativeError :: syntax ( )
541+ . with_message ( "invalid private identifier usage" )
542+ . into ( ) ) ;
543+ }
544+
545+ // 21. NOTE: The parameters and body are parsed separately to ensure that each is valid alone. For example, new Function("/*", "*/ ) {") does not evaluate to a function.
546+ // 22. NOTE: If this step is reached, sourceText must have the syntax of exprSym (although the reverse implication does not hold). The purpose of the next two steps is to enforce any Early Error rules which apply to exprSym directly.
547+ // 23. Let expr be ParseText(sourceText, exprSym).
548+ // 24. If expr is a List of errors, throw a SyntaxError exception.
549+ // Check for errors that apply for the combination of body and parameters.
512550
513551 // Early Error: If BindingIdentifier is present and the source text matched by BindingIdentifier is strict mode code,
514552 // it is a Syntax Error if the StringValue of BindingIdentifier is "eval" or "arguments".
515553 if body. strict ( ) {
516554 for name in bound_names ( & parameters) {
517555 if name == Sym :: ARGUMENTS || name == Sym :: EVAL {
518556 return Err ( JsNativeError :: syntax ( )
519- . with_message ( " Unexpected 'eval' or 'arguments' in strict mode" )
557+ . with_message ( "Unexpected 'eval' or 'arguments' in strict mode" )
520558 . into ( ) ) ;
521559 }
522560 }
@@ -571,21 +609,7 @@ impl BuiltInFunctionObject {
571609 }
572610 }
573611
574- if !all_private_identifiers_valid ( & parameters, Vec :: new ( ) ) {
575- return Err ( JsNativeError :: syntax ( )
576- . with_message ( "invalid private identifier usage" )
577- . into ( ) ) ;
578- }
579-
580- if !all_private_identifiers_valid ( & body, Vec :: new ( ) ) {
581- return Err ( JsNativeError :: syntax ( )
582- . with_message ( "invalid private identifier usage" )
583- . into ( ) ) ;
584- }
585-
586- ( parameters, body)
587- } else {
588- ( FormalParameterList :: default ( ) , FunctionBody :: default ( ) )
612+ body
589613 } ;
590614
591615 let code = FunctionCompiler :: new ( )
0 commit comments