diff options
9 files changed, 298 insertions, 86 deletions
diff --git a/Userland/Libraries/LibJS/Parser.cpp b/Userland/Libraries/LibJS/Parser.cpp index a30f7fd473..6772e2fff8 100644 --- a/Userland/Libraries/LibJS/Parser.cpp +++ b/Userland/Libraries/LibJS/Parser.cpp @@ -2473,13 +2473,13 @@ NonnullRefPtr<FunctionNodeType> Parser::parse_function_node(u8 parse_options) if (function_kind == FunctionKind::Normal && match(TokenType::Async) && !next_token().trivia_contains_line_terminator()) { function_kind = FunctionKind::Async; consume(TokenType::Async); - parse_options = parse_options | FunctionNodeParseOptions::IsAsyncFunction; + parse_options |= FunctionNodeParseOptions::IsAsyncFunction; } consume(TokenType::Function); if (match(TokenType::Asterisk)) { function_kind = function_kind == FunctionKind::Normal ? FunctionKind::Generator : FunctionKind::AsyncGenerator; consume(TokenType::Asterisk); - parse_options = parse_options | FunctionNodeParseOptions::IsGeneratorFunction; + parse_options |= FunctionNodeParseOptions::IsGeneratorFunction; } if (FunctionNodeType::must_have_name() || match_identifier()) diff --git a/Userland/Libraries/LibJS/Parser.h b/Userland/Libraries/LibJS/Parser.h index 738da60dd9..4f601af044 100644 --- a/Userland/Libraries/LibJS/Parser.h +++ b/Userland/Libraries/LibJS/Parser.h @@ -12,6 +12,7 @@ #include <AK/StringBuilder.h> #include <LibJS/AST.h> #include <LibJS/Lexer.h> +#include <LibJS/Runtime/FunctionConstructor.h> #include <LibJS/SourceRange.h> #include <stdio.h> @@ -173,6 +174,9 @@ public: bool try_parse_arrow_function_expression_failed; }; + // Needs to mess with m_state, and we're not going to expose a non-const getter for that :^) + friend ThrowCompletionOr<ECMAScriptFunctionObject*> FunctionConstructor::create_dynamic_function(GlobalObject&, FunctionObject&, FunctionObject*, FunctionKind, MarkedValueList const&); + private: friend class ScopePusher; diff --git a/Userland/Libraries/LibJS/Runtime/AsyncFunctionConstructor.cpp b/Userland/Libraries/LibJS/Runtime/AsyncFunctionConstructor.cpp index efdf6ad120..96759c7d81 100644 --- a/Userland/Libraries/LibJS/Runtime/AsyncFunctionConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/AsyncFunctionConstructor.cpp @@ -4,7 +4,6 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include <LibJS/Interpreter.h> #include <LibJS/Runtime/AsyncFunctionConstructor.h> #include <LibJS/Runtime/ECMAScriptFunctionObject.h> #include <LibJS/Runtime/FunctionConstructor.h> @@ -39,20 +38,16 @@ ThrowCompletionOr<Value> AsyncFunctionConstructor::call() ThrowCompletionOr<Object*> AsyncFunctionConstructor::construct(FunctionObject& new_target) { auto& vm = this->vm(); - auto function = TRY(FunctionConstructor::create_dynamic_function_node(global_object(), new_target, FunctionKind::Async)); + auto& global_object = this->global_object(); - OwnPtr<Interpreter> local_interpreter; - Interpreter* interpreter = vm.interpreter_if_exists(); + // 1. Let C be the active function object. + auto* constructor = vm.active_function_object(); - if (!interpreter) { - local_interpreter = Interpreter::create_with_existing_realm(*realm()); - interpreter = local_interpreter.ptr(); - } + // 2. Let args be the argumentsList that was passed to this function by [[Call]] or [[Construct]]. + auto& args = vm.running_execution_context().arguments; - VM::InterpreterExecutionScope scope(*interpreter); - auto result = TRY(function->execute(*interpreter, global_object())).release_value(); - VERIFY(result.is_object() && is<ECMAScriptFunctionObject>(result.as_object())); - return &result.as_object(); + // 3. Return CreateDynamicFunction(C, NewTarget, async, args). + return TRY(FunctionConstructor::create_dynamic_function(global_object, *constructor, &new_target, FunctionKind::Async, args)); } } diff --git a/Userland/Libraries/LibJS/Runtime/AsyncGeneratorFunctionConstructor.cpp b/Userland/Libraries/LibJS/Runtime/AsyncGeneratorFunctionConstructor.cpp index 485b24b69b..effb67c579 100644 --- a/Userland/Libraries/LibJS/Runtime/AsyncGeneratorFunctionConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/AsyncGeneratorFunctionConstructor.cpp @@ -4,7 +4,6 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include <LibJS/Interpreter.h> #include <LibJS/Runtime/AsyncGeneratorFunctionConstructor.h> #include <LibJS/Runtime/ECMAScriptFunctionObject.h> #include <LibJS/Runtime/FunctionConstructor.h> @@ -43,20 +42,16 @@ ThrowCompletionOr<Value> AsyncGeneratorFunctionConstructor::call() ThrowCompletionOr<Object*> AsyncGeneratorFunctionConstructor::construct(FunctionObject& new_target) { auto& vm = this->vm(); - auto function = TRY(FunctionConstructor::create_dynamic_function_node(global_object(), new_target, FunctionKind::AsyncGenerator)); + auto& global_object = this->global_object(); - OwnPtr<Interpreter> local_interpreter; - Interpreter* interpreter = vm.interpreter_if_exists(); + // 1. Let C be the active function object. + auto* constructor = vm.active_function_object(); - if (!interpreter) { - local_interpreter = Interpreter::create_with_existing_realm(*realm()); - interpreter = local_interpreter.ptr(); - } + // 2. Let args be the argumentsList that was passed to this function by [[Call]] or [[Construct]]. + auto& args = vm.running_execution_context().arguments; - VM::InterpreterExecutionScope scope(*interpreter); - auto result = TRY(function->execute(*interpreter, global_object())).release_value(); - VERIFY(result.is_object() && is<ECMAScriptFunctionObject>(result.as_object())); - return &result.as_object(); + // 3. Return ? CreateDynamicFunction(C, NewTarget, asyncGenerator, args). + return TRY(FunctionConstructor::create_dynamic_function(global_object, *constructor, &new_target, FunctionKind::AsyncGenerator, args)); } } diff --git a/Userland/Libraries/LibJS/Runtime/FunctionConstructor.cpp b/Userland/Libraries/LibJS/Runtime/FunctionConstructor.cpp index 8d2e637c9d..12b76a334e 100644 --- a/Userland/Libraries/LibJS/Runtime/FunctionConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/FunctionConstructor.cpp @@ -1,18 +1,20 @@ /* - * Copyright (c) 2020, Linus Groh <linusg@serenityos.org> + * Copyright (c) 2020-2022, Linus Groh <linusg@serenityos.org> * * SPDX-License-Identifier: BSD-2-Clause */ #include <LibJS/AST.h> -#include <LibJS/Interpreter.h> #include <LibJS/Lexer.h> #include <LibJS/Parser.h> +#include <LibJS/Runtime/AbstractOperations.h> #include <LibJS/Runtime/ECMAScriptFunctionObject.h> #include <LibJS/Runtime/Error.h> #include <LibJS/Runtime/FunctionConstructor.h> #include <LibJS/Runtime/FunctionObject.h> +#include <LibJS/Runtime/GeneratorObjectPrototype.h> #include <LibJS/Runtime/GlobalObject.h> +#include <LibJS/Runtime/Realm.h> namespace JS { @@ -37,32 +39,234 @@ FunctionConstructor::~FunctionConstructor() } // 20.2.1.1.1 CreateDynamicFunction ( constructor, newTarget, kind, args ), https://tc39.es/ecma262/#sec-createdynamicfunction -ThrowCompletionOr<RefPtr<FunctionExpression>> FunctionConstructor::create_dynamic_function_node(GlobalObject& global_object, FunctionObject&, FunctionKind kind) +ThrowCompletionOr<ECMAScriptFunctionObject*> FunctionConstructor::create_dynamic_function(GlobalObject& global_object, FunctionObject& constructor, FunctionObject* new_target, FunctionKind kind, MarkedValueList const& args) { auto& vm = global_object.vm(); - String parameters_source = ""; - String body_source = ""; - if (vm.argument_count() == 1) - body_source = TRY(vm.argument(0).to_string(global_object)); - if (vm.argument_count() > 1) { + + // 1. Assert: The execution context stack has at least two elements. + VERIFY(vm.execution_context_stack().size() >= 2); + + // 2. Let callerContext be the second to top element of the execution context stack. + // 3. Let callerRealm be callerContext's Realm. + // 4. Let calleeRealm be the current Realm Record. + // NOTE: All of these are only needed for the next step. + + // 5. Perform ? HostEnsureCanCompileStrings(callerRealm, calleeRealm). + // NOTE: We don't have this yet. + + // 6. If newTarget is undefined, set newTarget to constructor. + if (new_target == nullptr) + new_target = &constructor; + + StringView prefix; + Object* (GlobalObject::*fallback_prototype)() = nullptr; + + switch (kind) { + // 7. If kind is normal, then + case FunctionKind::Normal: + // a. Let prefix be "function". + prefix = "function"sv; + + // b. Let exprSym be the grammar symbol FunctionExpression. + // c. Let bodySym be the grammar symbol FunctionBody[~Yield, ~Await]. + // d. Let parameterSym be the grammar symbol FormalParameters[~Yield, ~Await]. + + // e. Let fallbackProto be "%Function.prototype%". + fallback_prototype = &GlobalObject::function_prototype; + break; + + // 8. Else if kind is generator, then + case FunctionKind::Generator: + // a. Let prefix be "function*". + prefix = "function*"sv; + + // b. Let exprSym be the grammar symbol GeneratorExpression. + // c. Let bodySym be the grammar symbol GeneratorBody. + // d. Let parameterSym be the grammar symbol FormalParameters[+Yield, ~Await]. + + // e. Let fallbackProto be "%GeneratorFunction.prototype%". + fallback_prototype = &GlobalObject::generator_function_prototype; + break; + + // 9. Else if kind is async, then + case FunctionKind::Async: + // a. Let prefix be "async function". + prefix = "async function"sv; + + // b. Let exprSym be the grammar symbol AsyncFunctionExpression. + // c. Let bodySym be the grammar symbol AsyncFunctionBody. + // d. Let parameterSym be the grammar symbol FormalParameters[~Yield, +Await]. + + // e. Let fallbackProto be "%AsyncFunction.prototype%". + fallback_prototype = &GlobalObject::async_function_prototype; + break; + + // 10. Else, + case FunctionKind::AsyncGenerator: + // a. Assert: kind is asyncGenerator. + + // b. Let prefix be "async function*". + prefix = "async function*"sv; + + // c. Let exprSym be the grammar symbol AsyncGeneratorExpression. + // d. Let bodySym be the grammar symbol AsyncGeneratorBody. + // e. Let parameterSym be the grammar symbol FormalParameters[+Yield, +Await]. + + // f. Let fallbackProto be "%AsyncGeneratorFunction.prototype%". + fallback_prototype = &GlobalObject::async_generator_function_prototype; + break; + + default: + VERIFY_NOT_REACHED(); + } + + // 11. Let argCount be the number of elements in args. + auto arg_count = args.size(); + + // 12. Let P be the empty String. + String parameters_string = ""; + + Optional<Value> body_arg; + + // 13. If argCount = 0, let bodyArg be the empty String. + if (arg_count == 0) { + // Optimization: Instead of creating a js_string() here, we just check if body_arg is empty in step 16. + } + // 14. Else if argCount = 1, let bodyArg be args[0]. + else if (arg_count == 1) { + body_arg = args[0]; + } + // 15. Else, + else { + // a. Assert: argCount > 1. + VERIFY(arg_count > 1); + + // b. Let firstArg be args[0]. + // c. Set P to ? ToString(firstArg). + // NOTE: Also done in the loop. We start at 0 instead and then join() with a comma. + + // d. Let k be 1. + size_t k = 0; + + // e. Repeat, while k < argCount - 1, Vector<String> parameters; - for (size_t i = 0; i < vm.argument_count() - 1; ++i) - parameters.append(TRY(vm.argument(i).to_string(global_object))); - StringBuilder parameters_builder; - parameters_builder.join(',', parameters); - parameters_source = parameters_builder.build(); - body_source = TRY(vm.argument(vm.argument_count() - 1).to_string(global_object)); + for (; k < arg_count - 1; ++k) { + // i. Let nextArg be args[k]. + auto next_arg = args[k]; + + // ii. Let nextArgString be ? ToString(nextArg). + // iii. Set P to the string-concatenation of P, "," (a comma), and nextArgString. + parameters.append(TRY(next_arg.to_string(global_object))); + + // iv. Set k to k + 1. + } + parameters_string = String::join(',', parameters); + + // f. Let bodyArg be args[k]. + body_arg = args[k]; } - auto is_generator = kind == FunctionKind::Generator || kind == FunctionKind::AsyncGenerator; - auto is_async = kind == FunctionKind::Async || kind == FunctionKind::AsyncGenerator; - auto source = String::formatted("{}function{} anonymous({}\n) {{\n{}\n}}", is_async ? "async " : "", is_generator ? "*" : "", parameters_source, body_source); - auto parser = Parser(Lexer(source)); - auto function = parser.parse_function_node<FunctionExpression>(); - if (parser.has_errors()) { - auto error = parser.errors()[0]; + + // 16. Let bodyString be the string-concatenation of 0x000A (LINE FEED), ? ToString(bodyArg), and 0x000A (LINE FEED). + auto body_string = String::formatted("\n{}\n", body_arg.has_value() ? TRY(body_arg->to_string(global_object)) : ""); + + // 17. Let sourceString be the string-concatenation of prefix, " anonymous(", P, 0x000A (LINE FEED), ") {", bodyString, and "}". + // 18. Let sourceText be ! StringToCodePoints(sourceString). + auto source_text = String::formatted("{} anonymous({}\n) {{{}}}", prefix, parameters_string, body_string); + + u8 parse_options = FunctionNodeParseOptions::CheckForFunctionAndName; + if (kind == FunctionKind::Async || kind == FunctionKind::AsyncGenerator) + parse_options |= FunctionNodeParseOptions::IsAsyncFunction; + if (kind == FunctionKind::Generator || kind == FunctionKind::AsyncGenerator) + parse_options |= FunctionNodeParseOptions::IsGeneratorFunction; + + // 19. Let parameters be ParseText(! StringToCodePoints(P), parameterSym). + i32 function_length = 0; + auto parameters_parser = Parser { Lexer { parameters_string } }; + auto parameters = parameters_parser.parse_formal_parameters(function_length, parse_options); + + // 20. If parameters is a List of errors, throw a SyntaxError exception. + if (parameters_parser.has_errors()) { + auto error = parameters_parser.errors()[0]; + return vm.throw_completion<SyntaxError>(global_object, error.to_string()); + } + + // 21. Let body be ParseText(! StringToCodePoints(bodyString), bodySym). + bool contains_direct_call_to_eval = false; + auto body_parser = Parser { Lexer { body_string } }; + // Set up some parser state to accept things like return await, and yield in the plain function body. + body_parser.m_state.in_function_context = true; + if ((parse_options & FunctionNodeParseOptions::IsAsyncFunction) != 0) + body_parser.m_state.await_expression_is_valid = true; + if ((parse_options & FunctionNodeParseOptions::IsGeneratorFunction) != 0) + body_parser.m_state.in_generator_function_context = true; + (void)body_parser.parse_function_body(parameters, kind, contains_direct_call_to_eval); + + // 22. If body is a List of errors, throw a SyntaxError exception. + if (body_parser.has_errors()) { + auto error = body_parser.errors()[0]; + return vm.throw_completion<SyntaxError>(global_object, error.to_string()); + } + + // 23. NOTE: The parameters and body are parsed separately to ensure that each is valid alone. For example, new Function("/*", "*/ ) {") is not legal. + // 24. 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. + + // 25. Let expr be ParseText(sourceText, exprSym). + auto source_parser = Parser { Lexer { source_text } }; + // This doesn't need any parse_options, it determines those & the function type based on the tokens that were found. + auto expr = source_parser.parse_function_node<FunctionExpression>(); + + // 26. If expr is a List of errors, throw a SyntaxError exception. + if (source_parser.has_errors()) { + auto error = source_parser.errors()[0]; return vm.throw_completion<SyntaxError>(global_object, error.to_string()); } + // 27. Let proto be ? GetPrototypeFromConstructor(newTarget, fallbackProto). + auto* prototype = TRY(get_prototype_from_constructor(global_object, *new_target, fallback_prototype)); + + // 28. Let realmF be the current Realm Record. + auto* realm = vm.current_realm(); + + // 29. Let scope be realmF.[[GlobalEnv]]. + auto* scope = &realm->global_environment(); + + // 30. Let privateScope be null. + PrivateEnvironment* private_scope = nullptr; + + // 31. Let F be ! OrdinaryFunctionCreate(proto, sourceText, parameters, body, non-lexical-this, scope, privateScope). + auto* function = ECMAScriptFunctionObject::create(global_object, "anonymous", *prototype, expr->body(), expr->parameters(), expr->function_length(), scope, private_scope, expr->kind(), expr->is_strict_mode(), expr->might_need_arguments_object(), contains_direct_call_to_eval); + + // FIXME: Remove the name argument from create() and do this instead. + // 32. Perform SetFunctionName(F, "anonymous"). + + // 33. If kind is generator, then + if (kind == FunctionKind::Generator) { + // a. Let prototype be ! OrdinaryObjectCreate(%GeneratorFunction.prototype.prototype%). + prototype = Object::create(global_object, global_object.generator_object_prototype()); + + // b. Perform DefinePropertyOrThrow(F, "prototype", PropertyDescriptor { [[Value]]: prototype, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false }). + function->define_direct_property(vm.names.prototype, prototype, Attribute::Writable); + } + // 34. Else if kind is asyncGenerator, then + else if (kind == FunctionKind::AsyncGenerator) { + // FIXME: We only have %AsyncGeneratorFunction.prototype%, not %AsyncGeneratorFunction.prototype.prototype%! + // a. Let prototype be ! OrdinaryObjectCreate(%AsyncGeneratorFunction.prototype.prototype%). + // prototype = Object::create(global_object, global_object.async_generator_prototype()); + + // b. Perform DefinePropertyOrThrow(F, "prototype", PropertyDescriptor { [[Value]]: prototype, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false }). + // function->define_direct_property(vm.names.prototype, prototype, Attribute::Writable); + } + // 35. Else if kind is normal, perform MakeConstructor(F). + else if (kind == FunctionKind::Normal) { + // FIXME: Implement MakeConstructor + prototype = Object::create(global_object, global_object.object_prototype()); + prototype->define_direct_property(vm.names.constructor, function, Attribute::Writable | Attribute::Configurable); + function->define_direct_property(vm.names.prototype, prototype, Attribute::Writable); + } + + // 36. NOTE: Functions whose kind is async are not constructible and do not have a [[Construct]] internal method or a "prototype" property. + + // 37. Return F. return function; } @@ -76,20 +280,16 @@ ThrowCompletionOr<Value> FunctionConstructor::call() ThrowCompletionOr<Object*> FunctionConstructor::construct(FunctionObject& new_target) { auto& vm = this->vm(); - auto function = TRY(create_dynamic_function_node(global_object(), new_target, FunctionKind::Normal)); + auto& global_object = this->global_object(); - OwnPtr<Interpreter> local_interpreter; - Interpreter* interpreter = vm.interpreter_if_exists(); + // 1. Let C be the active function object. + auto* constructor = vm.active_function_object(); - if (!interpreter) { - local_interpreter = Interpreter::create_with_existing_realm(*realm()); - interpreter = local_interpreter.ptr(); - } + // 2. Let args be the argumentsList that was passed to this function by [[Call]] or [[Construct]]. + auto& args = vm.running_execution_context().arguments; - VM::InterpreterExecutionScope scope(*interpreter); - auto result = TRY(function->execute(*interpreter, global_object())).release_value(); - VERIFY(result.is_object() && is<ECMAScriptFunctionObject>(result.as_object())); - return &result.as_object(); + // 3. Return ? CreateDynamicFunction(C, NewTarget, normal, args). + return TRY(create_dynamic_function(global_object, *constructor, &new_target, FunctionKind::Normal, args)); } } diff --git a/Userland/Libraries/LibJS/Runtime/FunctionConstructor.h b/Userland/Libraries/LibJS/Runtime/FunctionConstructor.h index aa117d3801..e4a90634bf 100644 --- a/Userland/Libraries/LibJS/Runtime/FunctionConstructor.h +++ b/Userland/Libraries/LibJS/Runtime/FunctionConstructor.h @@ -6,7 +6,7 @@ #pragma once -#include <LibJS/AST.h> +#include <LibJS/Runtime/FunctionKind.h> #include <LibJS/Runtime/NativeFunction.h> namespace JS { @@ -15,7 +15,7 @@ class FunctionConstructor final : public NativeFunction { JS_OBJECT(FunctionConstructor, NativeFunction); public: - static ThrowCompletionOr<RefPtr<FunctionExpression>> create_dynamic_function_node(GlobalObject& global_object, FunctionObject& new_target, FunctionKind kind); + static ThrowCompletionOr<ECMAScriptFunctionObject*> create_dynamic_function(GlobalObject& global_object, FunctionObject& constructor, FunctionObject* new_target, FunctionKind kind, MarkedValueList const& args); explicit FunctionConstructor(GlobalObject&); virtual void initialize(GlobalObject&) override; diff --git a/Userland/Libraries/LibJS/Runtime/GeneratorFunctionConstructor.cpp b/Userland/Libraries/LibJS/Runtime/GeneratorFunctionConstructor.cpp index 2550f6e1d2..9387906fdd 100644 --- a/Userland/Libraries/LibJS/Runtime/GeneratorFunctionConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/GeneratorFunctionConstructor.cpp @@ -4,11 +4,6 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include <AK/Debug.h> -#include <AK/Optional.h> -#include <LibJS/Bytecode/Interpreter.h> -#include <LibJS/Lexer.h> -#include <LibJS/Parser.h> #include <LibJS/Runtime/ECMAScriptFunctionObject.h> #include <LibJS/Runtime/FunctionConstructor.h> #include <LibJS/Runtime/GeneratorFunctionConstructor.h> @@ -45,22 +40,17 @@ ThrowCompletionOr<Value> GeneratorFunctionConstructor::call() // 27.3.1.1 GeneratorFunction ( p1, p2, … , pn, body ), https://tc39.es/ecma262/#sec-generatorfunction ThrowCompletionOr<Object*> GeneratorFunctionConstructor::construct(FunctionObject& new_target) { - auto function = TRY(FunctionConstructor::create_dynamic_function_node(global_object(), new_target, FunctionKind::Generator)); + auto& vm = this->vm(); + auto& global_object = this->global_object(); - auto* bytecode_interpreter = Bytecode::Interpreter::current(); - VERIFY(bytecode_interpreter); + // 1. Let C be the active function object. + auto* constructor = vm.active_function_object(); - auto executable = Bytecode::Generator::generate(function->body(), FunctionKind::Generator); - auto& passes = JS::Bytecode::Interpreter::optimization_pipeline(); - passes.perform(executable); - if constexpr (JS_BYTECODE_DEBUG) { - dbgln("Optimisation passes took {}us", passes.elapsed()); - dbgln("Compiled Bytecode::Block for function '{}':", function->name()); - for (auto& block : executable.basic_blocks) - block.dump(executable); - } + // 2. Let args be the argumentsList that was passed to this function by [[Call]] or [[Construct]]. + auto& args = vm.running_execution_context().arguments; - return ECMAScriptFunctionObject::create(global_object(), function->name(), function->body(), function->parameters(), function->function_length(), vm().lexical_environment(), nullptr, FunctionKind::Generator, function->is_strict_mode(), function->might_need_arguments_object()); + // 3. Return ? CreateDynamicFunction(C, NewTarget, generator, args). + return TRY(FunctionConstructor::create_dynamic_function(global_object, *constructor, &new_target, FunctionKind::Generator, args)); } } diff --git a/Userland/Libraries/LibJS/Tests/builtins/Function/Function.js b/Userland/Libraries/LibJS/Tests/builtins/Function/Function.js index 9a972ab81d..a26e5a53db 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/Function/Function.js +++ b/Userland/Libraries/LibJS/Tests/builtins/Function/Function.js @@ -44,10 +44,20 @@ describe("errors", () => { }) // This might be confusing at first but keep in mind it's actually parsing // function anonymous() { [ } - // This is in line with what other engines are reporting. + // Since the body, surrounded by a newline on each side, is first parsed standalone, + // we report unexpected token EOF instead of }. + // FIXME: The position is odd though, I'd expect `line: 2, column: 2` and `line: 3, column: 1`... + // > eval("\n[") // Uncaught exception: [SyntaxError] Unexpected token Eof. Expected BracketClose (line: 2, column: 2) + // > eval("\n[\n") // Uncaught exception: [SyntaxError] Unexpected token Eof. Expected BracketClose (line: 2, column: 3) .toThrowWithMessage( SyntaxError, - "Unexpected token CurlyClose. Expected BracketClose (line: 4, column: 1)" + "Unexpected token Eof. Expected BracketClose (line: 2, column: 3)" ); }); + + test("parameters and body must be valid standalone", () => { + expect(() => { + new Function("/*", "*/ ) {"); + }).toThrowWithMessage(SyntaxError, "Unterminated multi-line comment (line: 1, column: 3)"); + }); }); diff --git a/Userland/Libraries/LibJS/Tests/parser-line-terminators.js b/Userland/Libraries/LibJS/Tests/parser-line-terminators.js index 9b75eb4af0..63c7009f18 100644 --- a/Userland/Libraries/LibJS/Tests/parser-line-terminators.js +++ b/Userland/Libraries/LibJS/Tests/parser-line-terminators.js @@ -13,31 +13,46 @@ function anonymous( test("LINE FEED is a line terminator", () => { expect(() => { Function("\n\n@"); - }).toThrowWithMessage(SyntaxError, "line: 5, column: 1"); + }).toThrowWithMessage( + SyntaxError, + "Unexpected token Invalid. Expected CurlyClose (line: 4, column: 1)" + ); }); test("CARRIAGE RETURN is a line terminator", () => { expect(() => { Function("\r\r@"); - }).toThrowWithMessage(SyntaxError, "line: 5, column: 1"); + }).toThrowWithMessage( + SyntaxError, + "Unexpected token Invalid. Expected CurlyClose (line: 4, column: 1)" + ); }); test("LINE SEPARATOR is a line terminator", () => { expect(() => { Function("
@"); - }).toThrowWithMessage(SyntaxError, "line: 5, column: 1"); + }).toThrowWithMessage( + SyntaxError, + "Unexpected token Invalid. Expected CurlyClose (line: 4, column: 1)" + ); }); test("PARAGRAPH SEPARATOR is a line terminator", () => { expect(() => { Function("
@"); - }).toThrowWithMessage(SyntaxError, "line: 5, column: 1"); + }).toThrowWithMessage( + SyntaxError, + "Unexpected token Invalid. Expected CurlyClose (line: 4, column: 1)" + ); }); test("CR LF is counted as only one line terminator", () => { expect(() => { Function("\r\n\r\n@"); - }).toThrowWithMessage(SyntaxError, "line: 5, column: 1"); + }).toThrowWithMessage( + SyntaxError, + "Unexpected token Invalid. Expected CurlyClose (line: 4, column: 1)" + ); }); test("LF/CR are not allowed in string literal", () => { @@ -55,7 +70,10 @@ test("LS/PS are allowed in string literal", () => { test("line terminators can be mixed (but please don't)", () => { expect(() => { Function("\r
\r\n
\n\r@"); - }).toThrowWithMessage(SyntaxError, "line: 9, column: 1"); + }).toThrowWithMessage( + SyntaxError, + "Unexpected token Invalid. Expected CurlyClose (line: 8, column: 1)" + ); }); test("all line terminators are valid for line continuations", () => { |