diff --git a/Libraries/LibJS/Parser.cpp b/Libraries/LibJS/Parser.cpp index 1622220c81..bafb72454a 100644 --- a/Libraries/LibJS/Parser.cpp +++ b/Libraries/LibJS/Parser.cpp @@ -29,7 +29,6 @@ #include #include #include -#include namespace JS { @@ -1385,12 +1384,11 @@ void Parser::expected(const char* what) void Parser::syntax_error(const String& message, size_t line, size_t column) { - m_parser_state.m_has_errors = true; if (line == 0 || column == 0) { line = m_parser_state.m_current_token.line_number(); column = m_parser_state.m_current_token.line_column(); } - fprintf(stderr, "Syntax Error: %s (line: %zu, column: %zu)\n", message.characters(), line, column); + m_parser_state.m_errors.append({ message, line, column }); } void Parser::save_state() diff --git a/Libraries/LibJS/Parser.h b/Libraries/LibJS/Parser.h index 7cbd0b2b80..91f90953ea 100644 --- a/Libraries/LibJS/Parser.h +++ b/Libraries/LibJS/Parser.h @@ -29,6 +29,7 @@ #include "AST.h" #include "Lexer.h" #include +#include namespace JS { @@ -75,7 +76,26 @@ public: NonnullRefPtr parse_new_expression(); RefPtr try_parse_arrow_function_expression(bool expect_parens); - bool has_errors() const { m_parser_state.m_has_errors; } + struct Error { + String message; + size_t line; + size_t column; + + String to_string() const + { + if (line == 0 || column == 0) + return message; + return String::format("%s (line: %zu, column: %zu)", message.characters(), line, column); + } + }; + + bool has_errors() const { return m_parser_state.m_errors.size(); } + const Vector& errors() const { return m_parser_state.m_errors; } + void print_errors() const + { + for (auto& error : m_parser_state.m_errors) + fprintf(stderr, "SyntaxError: %s\n", error.to_string().characters()); + } private: friend class ScopePusher; @@ -101,7 +121,7 @@ private: struct ParserState { Lexer m_lexer; Token m_current_token; - bool m_has_errors = false; + Vector m_errors; Vector> m_var_scopes; Vector> m_let_scopes; diff --git a/Libraries/LibJS/Runtime/FunctionConstructor.cpp b/Libraries/LibJS/Runtime/FunctionConstructor.cpp index bb8db2d28c..7a1a609992 100644 --- a/Libraries/LibJS/Runtime/FunctionConstructor.cpp +++ b/Libraries/LibJS/Runtime/FunctionConstructor.cpp @@ -70,8 +70,8 @@ Value FunctionConstructor::construct(Interpreter& interpreter) auto parser = Parser(Lexer(source)); auto function_expression = parser.parse_function_node(); if (parser.has_errors()) { - // FIXME: The parser should expose parsing error strings rather than just fprintf()'ing them - interpreter.throw_exception(""); + auto error = parser.errors()[0]; + interpreter.throw_exception(error.to_string()); return {}; } return function_expression->execute(interpreter); diff --git a/Libraries/LibJS/Tests/Function.js b/Libraries/LibJS/Tests/Function.js index 146976df90..52f07c103b 100644 --- a/Libraries/LibJS/Tests/Function.js +++ b/Libraries/LibJS/Tests/Function.js @@ -29,7 +29,11 @@ try { assertThrowsError(() => { new Function("["); }, { - error: SyntaxError + error: SyntaxError, + // 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. + message: "Unexpected token CurlyClose. Expected BracketClose (line: 1, column: 26)" }); console.log("PASS"); diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index 1507472598..ba381d7ac0 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -378,6 +378,7 @@ JS::Value Document::run_javascript(const StringView& source) auto parser = JS::Parser(JS::Lexer(source)); auto program = parser.parse_program(); if (parser.has_errors()) { + parser.print_errors(); return JS::js_undefined(); } dbg() << "Document::run_javascript('" << source << "') will run:"; diff --git a/Libraries/LibWeb/DOM/HTMLScriptElement.cpp b/Libraries/LibWeb/DOM/HTMLScriptElement.cpp index a6634da538..d2030dddd2 100644 --- a/Libraries/LibWeb/DOM/HTMLScriptElement.cpp +++ b/Libraries/LibWeb/DOM/HTMLScriptElement.cpp @@ -61,9 +61,10 @@ void HTMLScriptElement::children_changed() auto parser = JS::Parser(JS::Lexer(source)); auto program = parser.parse_program(); - if (parser.has_errors()) + if (parser.has_errors()) { + parser.print_errors(); return; - + } document().interpreter().run(*program); } @@ -96,9 +97,10 @@ void HTMLScriptElement::inserted_into(Node& new_parent) auto parser = JS::Parser(JS::Lexer(source)); auto program = parser.parse_program(); - if (parser.has_errors()) + if (parser.has_errors()) { + parser.print_errors(); return; - + } document().interpreter().run(*program); } diff --git a/Meta/Lagom/Fuzzers/FuzzJs.cpp b/Meta/Lagom/Fuzzers/FuzzJs.cpp index 86c878c9e7..f5fcafd957 100644 --- a/Meta/Lagom/Fuzzers/FuzzJs.cpp +++ b/Meta/Lagom/Fuzzers/FuzzJs.cpp @@ -36,5 +36,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) auto lexer = JS::Lexer(js); auto parser = JS::Parser(lexer); parser.parse_program(); + if (parser.has_errors()) + parser.print_errors(); return 0; }