From b0b022507b2f73be2f4cef17deb8ece91dae6822 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 21 Nov 2022 17:37:38 +0100 Subject: [PATCH] LibJS: Reduce AST memory usage by shrink-wrapping source range info Before this change, each AST node had a 64-byte SourceRange member. This SourceRange had the following layout: filename: StringView (16 bytes) start: Position (24 bytes) end: Position (24 bytes) The Position structs have { line, column, offset }, all members size_t. To reduce memory consumption, AST nodes now only store the following: source_code: NonnullRefPtr (8 bytes) start_offset: u32 (4 bytes) end_offset: u32 (4 bytes) SourceCode is a new ref-counted data structure that keeps the filename and original parsed source code in a single location, and all AST nodes have a pointer to it. The start_offset and end_offset can be turned into (line, column) when necessary by calling SourceCode::range_from_offsets(). This will walk the source code string and compute line/column numbers on the fly, so it's not necessarily fast, but it should be rare since this information is primarily used for diagnostics and exception stack traces. With this, ASTNode shrinks from 80 bytes to 32 bytes. This gives us a ~23% reduction in memory usage when loading twitter.com/awesomekling (330 MiB before, 253 MiB after!) :^) --- .../Applications/Spreadsheet/Spreadsheet.cpp | 2 +- .../Spreadsheet/SpreadsheetModel.cpp | 6 +- Userland/Libraries/LibJS/AST.cpp | 17 + Userland/Libraries/LibJS/AST.h | 12 +- Userland/Libraries/LibJS/CMakeLists.txt | 1 + Userland/Libraries/LibJS/Forward.h | 2 + Userland/Libraries/LibJS/Lexer.h | 8 +- Userland/Libraries/LibJS/MarkupGenerator.cpp | 2 +- Userland/Libraries/LibJS/Parser.cpp | 311 +++++++++--------- Userland/Libraries/LibJS/Parser.h | 1 + Userland/Libraries/LibJS/Runtime/Error.cpp | 10 +- Userland/Libraries/LibJS/Runtime/VM.cpp | 4 +- Userland/Libraries/LibJS/SourceCode.cpp | 81 +++++ Userland/Libraries/LibJS/SourceCode.h | 30 ++ Userland/Libraries/LibJS/SourceRange.h | 5 +- .../HTML/Scripting/ExceptionReporter.cpp | 2 +- 16 files changed, 315 insertions(+), 179 deletions(-) create mode 100644 Userland/Libraries/LibJS/SourceCode.cpp create mode 100644 Userland/Libraries/LibJS/SourceCode.h diff --git a/Userland/Applications/Spreadsheet/Spreadsheet.cpp b/Userland/Applications/Spreadsheet/Spreadsheet.cpp index f6dc5bde53..8141791e19 100644 --- a/Userland/Applications/Spreadsheet/Spreadsheet.cpp +++ b/Userland/Applications/Spreadsheet/Spreadsheet.cpp @@ -71,7 +71,7 @@ Sheet::Sheet(Workbook& workbook) for (auto& traceback_frame : error.traceback()) { auto& function_name = traceback_frame.function_name; auto& source_range = traceback_frame.source_range; - dbgln(" {} at {}:{}:{}", function_name, source_range.filename, source_range.start.line, source_range.start.column); + dbgln(" {} at {}:{}:{}", function_name, source_range.filename(), source_range.start.line, source_range.start.column); } } else { warnln(); diff --git a/Userland/Applications/Spreadsheet/SpreadsheetModel.cpp b/Userland/Applications/Spreadsheet/SpreadsheetModel.cpp index dd45c10988..8b5d4c8678 100644 --- a/Userland/Applications/Spreadsheet/SpreadsheetModel.cpp +++ b/Userland/Applications/Spreadsheet/SpreadsheetModel.cpp @@ -122,13 +122,13 @@ GUI::Variant SheetModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) StringBuilder builder; builder.appendff("{}\n", error.get_without_side_effects(object.vm().names.message).to_string_without_side_effects()); for (auto const& frame : trace.in_reverse()) { - if (frame.source_range.filename.contains("runtime.js"sv)) { + if (frame.source_range.filename().contains("runtime.js"sv)) { if (frame.function_name == "") builder.appendff(" in a builtin function at line {}, column {}\n", frame.source_range.start.line, frame.source_range.start.column); else builder.appendff(" while evaluating builtin '{}'\n", frame.function_name); - } else if (frame.source_range.filename.starts_with("cell "sv)) { - builder.appendff(" in cell '{}', at line {}, column {}\n", frame.source_range.filename.substring_view(5), frame.source_range.start.line, frame.source_range.start.column); + } else if (frame.source_range.filename().starts_with("cell "sv)) { + builder.appendff(" in cell '{}', at line {}, column {}\n", frame.source_range.filename().substring_view(5), frame.source_range.start.line, frame.source_range.start.column); } } return builder.to_string(); diff --git a/Userland/Libraries/LibJS/AST.cpp b/Userland/Libraries/LibJS/AST.cpp index a128568f11..de4b57a4c2 100644 --- a/Userland/Libraries/LibJS/AST.cpp +++ b/Userland/Libraries/LibJS/AST.cpp @@ -61,6 +61,18 @@ private: ExecutingASTNodeChain m_chain_node; }; +ASTNode::ASTNode(SourceRange source_range) + : m_source_code(source_range.code) + , m_start_offset(source_range.start.offset) + , m_end_offset(source_range.end.offset) +{ +} + +SourceRange ASTNode::source_range() const +{ + return m_source_code->range_from_offsets(m_start_offset, m_end_offset); +} + String ASTNode::class_name() const { // NOTE: We strip the "JS::" prefix. @@ -4794,4 +4806,9 @@ ModuleRequest::ModuleRequest(FlyString module_specifier_, Vector asse }); } +String const& SourceRange::filename() const +{ + return code->filename(); +} + } diff --git a/Userland/Libraries/LibJS/AST.h b/Userland/Libraries/LibJS/AST.h index d06496c16a..eaa7ccd600 100644 --- a/Userland/Libraries/LibJS/AST.h +++ b/Userland/Libraries/LibJS/AST.h @@ -52,8 +52,7 @@ public: virtual Bytecode::CodeGenerationErrorOr generate_bytecode(Bytecode::Generator&) const; virtual void dump(int indent) const; - SourceRange const& source_range() const { return m_source_range; } - SourceRange& source_range() { return m_source_range; } + SourceRange source_range() const; String class_name() const; @@ -84,13 +83,12 @@ public: virtual bool is_class_method() const { return false; } protected: - explicit ASTNode(SourceRange source_range) - : m_source_range(source_range) - { - } + explicit ASTNode(SourceRange); private: - SourceRange m_source_range; + RefPtr m_source_code; + u32 m_start_offset { 0 }; + u32 m_end_offset { 0 }; }; class Statement : public ASTNode { diff --git a/Userland/Libraries/LibJS/CMakeLists.txt b/Userland/Libraries/LibJS/CMakeLists.txt index 0bd369652b..46b77da9a1 100644 --- a/Userland/Libraries/LibJS/CMakeLists.txt +++ b/Userland/Libraries/LibJS/CMakeLists.txt @@ -244,6 +244,7 @@ set(SOURCES Runtime/WeakSetPrototype.cpp Runtime/WrappedFunction.cpp Script.cpp + SourceCode.cpp SourceTextModule.cpp SyntaxHighlighter.cpp SyntheticModule.cpp diff --git a/Userland/Libraries/LibJS/Forward.h b/Userland/Libraries/LibJS/Forward.h index 9e2a6fda05..2181bd5afb 100644 --- a/Userland/Libraries/LibJS/Forward.h +++ b/Userland/Libraries/LibJS/Forward.h @@ -188,6 +188,8 @@ class Script; class Shape; class Statement; class StringOrSymbol; +class SourceCode; +struct SourceRange; class SourceTextModule; class Symbol; class Token; diff --git a/Userland/Libraries/LibJS/Lexer.h b/Userland/Libraries/LibJS/Lexer.h index 3cb25f68db..4b115be43b 100644 --- a/Userland/Libraries/LibJS/Lexer.h +++ b/Userland/Libraries/LibJS/Lexer.h @@ -20,8 +20,8 @@ public: Token next(); - StringView source() const { return m_source; }; - StringView filename() const { return m_filename; }; + String const& source() const { return m_source; }; + String const& filename() const { return m_filename; }; void disallow_html_comments() { m_allow_html_comments = false; }; @@ -57,13 +57,13 @@ private: TokenType consume_regex_literal(); - StringView m_source; + String m_source; size_t m_position { 0 }; Token m_current_token; char m_current_char { 0 }; bool m_eof { false }; - StringView m_filename; + String m_filename; size_t m_line_number { 1 }; size_t m_line_column { 0 }; diff --git a/Userland/Libraries/LibJS/MarkupGenerator.cpp b/Userland/Libraries/LibJS/MarkupGenerator.cpp index 33afb5fe1f..892c44c00f 100644 --- a/Userland/Libraries/LibJS/MarkupGenerator.cpp +++ b/Userland/Libraries/LibJS/MarkupGenerator.cpp @@ -150,7 +150,7 @@ void MarkupGenerator::trace_to_html(TracebackFrame const& traceback_frame, Strin auto last_slash_index = filename.find_last('/'); return last_slash_index.has_value() ? filename.substring_view(*last_slash_index + 1) : filename; }; - auto filename = escape_html_entities(get_filename_from_path(traceback_frame.source_range.filename)); + auto filename = escape_html_entities(get_filename_from_path(traceback_frame.source_range.filename())); auto trace = String::formatted("at {} ({}:{}:{})", function_name, filename, line, column); html_output.appendff("  {}
", trace); diff --git a/Userland/Libraries/LibJS/Parser.cpp b/Userland/Libraries/LibJS/Parser.cpp index 4fb31c0c2b..18edff0528 100644 --- a/Userland/Libraries/LibJS/Parser.cpp +++ b/Userland/Libraries/LibJS/Parser.cpp @@ -419,7 +419,8 @@ Parser::ParserState::ParserState(Lexer l, Program::Type program_type) } Parser::Parser(Lexer lexer, Program::Type program_type, Optional initial_state_for_eval) - : m_state(move(lexer), program_type) + : m_source_code(SourceCode::create(lexer.filename(), lexer.source())) + , m_state(move(lexer), program_type) , m_program_type(program_type) { if (initial_state_for_eval.has_value()) { @@ -503,7 +504,7 @@ bool Parser::parse_directive(ScopeNode& body) NonnullRefPtr Parser::parse_program(bool starts_in_strict_mode) { auto rule_start = push_start(); - auto program = adopt_ref(*new Program({ m_filename, rule_start.position(), position() }, m_program_type)); + auto program = adopt_ref(*new Program({ m_source_code, rule_start.position(), position() }, m_program_type)); ScopePusher program_scope = ScopePusher::program_scope(*this, *program); if (m_program_type == Program::Type::Script) @@ -614,7 +615,7 @@ NonnullRefPtr Parser::parse_declaration() default: expected("declaration"); consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }); + return create_ast_node({ m_source_code, rule_start.position(), position() }); } } @@ -658,7 +659,7 @@ NonnullRefPtr Parser::parse_statement(AllowLabelledFunction allow_lab return parse_debugger_statement(); case TokenType::Semicolon: consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }); + return create_ast_node({ m_source_code, rule_start.position(), position() }); case TokenType::Slash: case TokenType::SlashEquals: m_state.current_token = m_state.lexer.force_slash_as_regex(); @@ -685,11 +686,11 @@ NonnullRefPtr Parser::parse_statement(AllowLabelledFunction allow_lab auto expr = parse_expression(0); consume_or_insert_semicolon(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(expr)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, move(expr)); } expected("statement"); consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }); + return create_ast_node({ m_source_code, rule_start.position(), position() }); } } @@ -835,10 +836,10 @@ RefPtr Parser::try_parse_arrow_function_expression(bool expe // for arrow function bodies which are a single expression. // Esprima generates a single "ArrowFunctionExpression" // with a "body" property. - auto return_block = create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }); + auto return_block = create_ast_node({ m_source_code, rule_start.position(), position() }); ScopePusher function_scope = ScopePusher::function_scope(*this, return_block, parameters); auto return_expression = parse_expression(2); - return_block->append({ m_filename, rule_start.position(), position() }, move(return_expression)); + return_block->append({ m_source_code, rule_start.position(), position() }, move(return_expression)); if (m_state.strict_mode) return_block->set_strict_mode(); contains_direct_call_to_eval = function_scope.contains_direct_call_to_eval(); @@ -869,7 +870,7 @@ RefPtr Parser::try_parse_arrow_function_expression(bool expe auto function_end_offset = position().offset - m_state.current_token.trivia().length(); auto source_text = String { m_state.lexer.source().substring_view(function_start_offset, function_end_offset - function_start_offset) }; return create_ast_node( - { m_state.current_token.filename(), rule_start.position(), position() }, "", move(source_text), + { m_source_code, rule_start.position(), position() }, "", move(source_text), move(body), move(parameters), function_length, function_kind, body->in_strict_mode(), /* might_need_arguments_object */ false, contains_direct_call_to_eval, /* is_arrow_function */ true); } @@ -958,7 +959,7 @@ RefPtr Parser::try_parse_labelled_statement(AllowLabelledFunc m_state.labels_in_scope.remove(identifier); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, identifier, labelled_item.release_nonnull()); + return create_ast_node({ m_source_code, rule_start.position(), position() }, identifier, labelled_item.release_nonnull()); } RefPtr Parser::try_parse_new_target_expression() @@ -983,7 +984,7 @@ RefPtr Parser::try_parse_new_target_expression() state_rollback_guard.disarm(); discard_saved_state(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, MetaProperty::Type::NewTarget); + return create_ast_node({ m_source_code, rule_start.position(), position() }, MetaProperty::Type::NewTarget); } RefPtr Parser::try_parse_import_meta_expression() @@ -1008,7 +1009,7 @@ RefPtr Parser::try_parse_import_meta_expression() state_rollback_guard.disarm(); discard_saved_state(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, MetaProperty::Type::ImportMeta); + return create_ast_node({ m_source_code, rule_start.position(), position() }, MetaProperty::Type::ImportMeta); } NonnullRefPtr Parser::parse_import_call() @@ -1040,13 +1041,13 @@ NonnullRefPtr Parser::parse_import_call() consume(TokenType::ParenClose); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(argument), move(options)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, move(argument), move(options)); } NonnullRefPtr Parser::parse_class_declaration() { auto rule_start = push_start(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, parse_class_expression(true)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, parse_class_expression(true)); } NonnullRefPtr Parser::parse_class_expression(bool expect_class_name) @@ -1078,7 +1079,7 @@ NonnullRefPtr Parser::parse_class_expression(bool expect_class_ for (;;) { if (match(TokenType::TemplateLiteralStart)) { auto template_literal = parse_template_literal(true); - expression = create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(expression), move(template_literal)); + expression = create_ast_node({ m_source_code, rule_start.position(), position() }, move(expression), move(template_literal)); continue; } if (match(TokenType::BracketOpen) || match(TokenType::Period) || match(TokenType::ParenOpen)) { @@ -1167,7 +1168,7 @@ NonnullRefPtr Parser::parse_class_expression(bool expect_class_ switch (m_state.current_token.type()) { case TokenType::Identifier: name = consume().value(); - property_key = create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, name); + property_key = create_ast_node({ m_source_code, rule_start.position(), position() }, name); break; case TokenType::PrivateIdentifier: name = consume().value(); @@ -1204,7 +1205,7 @@ NonnullRefPtr Parser::parse_class_expression(bool expect_class_ syntax_error(String::formatted("Duplicate private field or method named '{}'", name)); } - property_key = create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, name); + property_key = create_ast_node({ m_source_code, rule_start.position(), position() }, name); break; case TokenType::StringLiteral: { auto string_literal = parse_string_literal(consume()); @@ -1243,12 +1244,12 @@ NonnullRefPtr Parser::parse_class_expression(bool expect_class_ method_kind = ClassMethod::Kind::Method; break; } - property_key = create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, name); + property_key = create_ast_node({ m_source_code, rule_start.position(), position() }, name); } else if (match(TokenType::CurlyOpen) && is_static) { auto static_start = push_start(); consume(TokenType::CurlyOpen); - auto static_init_block = create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }); + auto static_init_block = create_ast_node({ m_source_code, rule_start.position(), position() }); TemporaryChange break_context_rollback(m_state.in_break_context, false); TemporaryChange continue_context_rollback(m_state.in_continue_context, false); @@ -1263,7 +1264,7 @@ NonnullRefPtr Parser::parse_class_expression(bool expect_class_ parse_statement_list(static_init_block); consume(TokenType::CurlyClose); - elements.append(create_ast_node({ m_state.current_token.filename(), static_start.position(), position() }, move(static_init_block), static_init_scope.contains_direct_call_to_eval())); + elements.append(create_ast_node({ m_source_code, static_start.position(), position() }, move(static_init_block), static_init_scope.contains_direct_call_to_eval())); continue; } else { expected("property key"); @@ -1300,7 +1301,7 @@ NonnullRefPtr Parser::parse_class_expression(bool expect_class_ if (is_constructor) { constructor = move(function); } else if (!property_key.is_null()) { - elements.append(create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, property_key.release_nonnull(), move(function), method_kind, is_static)); + elements.append(create_ast_node({ m_source_code, rule_start.position(), position() }, property_key.release_nonnull(), move(function), method_kind, is_static)); } else { syntax_error("No key for class method"); } @@ -1328,7 +1329,7 @@ NonnullRefPtr Parser::parse_class_expression(bool expect_class_ contains_direct_call_to_eval = class_field_scope.contains_direct_call_to_eval(); } - elements.append(create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, property_key.release_nonnull(), move(initializer), contains_direct_call_to_eval, is_static)); + elements.append(create_ast_node({ m_source_code, rule_start.position(), position() }, property_key.release_nonnull(), move(initializer), contains_direct_call_to_eval, is_static)); consume_or_insert_semicolon(); } } @@ -1336,7 +1337,7 @@ NonnullRefPtr Parser::parse_class_expression(bool expect_class_ consume(TokenType::CurlyClose); if (constructor.is_null()) { - auto constructor_body = create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }); + auto constructor_body = create_ast_node({ m_source_code, rule_start.position(), position() }); if (!super_class.is_null()) { // Set constructor to the result of parsing the source text // constructor(... args){ super (...args);} @@ -1347,21 +1348,21 @@ NonnullRefPtr Parser::parse_class_expression(bool expect_class_ // method on %Array.prototype% visibly. FlyString argument_name = "args"; auto super_call = create_ast_node( - { m_state.current_token.filename(), rule_start.position(), position() }, + { m_source_code, rule_start.position(), position() }, SuperCall::IsPartOfSyntheticConstructor::Yes, - CallExpression::Argument { create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, "args"), true }); + CallExpression::Argument { create_ast_node({ m_source_code, rule_start.position(), position() }, "args"), true }); // NOTE: While the JS approximation above doesn't do `return super(...args)`, the // abstract closure is expected to capture and return the result, so we do need a // return statement here to create the correct completion. - constructor_body->append(create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(super_call))); + constructor_body->append(create_ast_node({ m_source_code, rule_start.position(), position() }, move(super_call))); constructor = create_ast_node( - { m_state.current_token.filename(), rule_start.position(), position() }, class_name, "", + { m_source_code, rule_start.position(), position() }, class_name, "", move(constructor_body), Vector { FunctionNode::Parameter { move(argument_name), nullptr, true } }, 0, FunctionKind::Normal, /* is_strict_mode */ true, /* might_need_arguments_object */ false, /* contains_direct_call_to_eval */ false); } else { constructor = create_ast_node( - { m_state.current_token.filename(), rule_start.position(), position() }, class_name, "", + { m_source_code, rule_start.position(), position() }, class_name, "", move(constructor_body), Vector {}, 0, FunctionKind::Normal, /* is_strict_mode */ true, /* might_need_arguments_object */ false, /* contains_direct_call_to_eval */ false); } @@ -1381,7 +1382,7 @@ NonnullRefPtr Parser::parse_class_expression(bool expect_class_ auto function_end_offset = position().offset - m_state.current_token.trivia().length(); auto source_text = String { m_state.lexer.source().substring_view(function_start_offset, function_end_offset - function_start_offset) }; - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(class_name), move(source_text), move(constructor), move(super_class), move(elements)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, move(class_name), move(source_text), move(constructor), move(super_class), move(elements)); } Parser::PrimaryExpressionParseResult Parser::parse_primary_expression() @@ -1422,14 +1423,14 @@ Parser::PrimaryExpressionParseResult Parser::parse_primary_expression() } case TokenType::This: consume(); - return { create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }) }; + return { create_ast_node({ m_source_code, rule_start.position(), position() }) }; case TokenType::Class: return { parse_class_expression(false) }; case TokenType::Super: consume(); if (!m_state.allow_super_property_lookup) syntax_error("'super' keyword unexpected here"); - return { create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }) }; + return { create_ast_node({ m_source_code, rule_start.position(), position() }) }; case TokenType::EscapedKeyword: if (match_invalid_escaped_keyword()) syntax_error("Keyword must not contain escaped characters"); @@ -1446,16 +1447,16 @@ Parser::PrimaryExpressionParseResult Parser::parse_primary_expression() return { parse_identifier() }; } case TokenType::NumericLiteral: - return { create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, consume_and_validate_numeric_literal().double_value()) }; + return { create_ast_node({ m_source_code, rule_start.position(), position() }, consume_and_validate_numeric_literal().double_value()) }; case TokenType::BigIntLiteral: - return { create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, consume().value()) }; + return { create_ast_node({ m_source_code, rule_start.position(), position() }, consume().value()) }; case TokenType::BoolLiteral: - return { create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, consume().bool_value()) }; + return { create_ast_node({ m_source_code, rule_start.position(), position() }, consume().bool_value()) }; case TokenType::StringLiteral: return { parse_string_literal(consume()) }; case TokenType::NullLiteral: consume(); - return { create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }) }; + return { create_ast_node({ m_source_code, rule_start.position(), position() }) }; case TokenType::CurlyOpen: return { parse_object_expression() }; case TokenType::Async: { @@ -1524,7 +1525,7 @@ Parser::PrimaryExpressionParseResult Parser::parse_primary_expression() syntax_error(String::formatted("Reference to undeclared private field or method '{}'", m_state.current_token.value())); if (next_token().type() != TokenType::In) syntax_error("Cannot have a private identifier in expression if not followed by 'in'"); - return { create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, consume().value()) }; + return { create_ast_node({ m_source_code, rule_start.position(), position() }, consume().value()) }; default: if (match_identifier_name()) goto read_as_identifier; @@ -1532,7 +1533,7 @@ Parser::PrimaryExpressionParseResult Parser::parse_primary_expression() } expected("primary expression"); consume(); - return { create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }) }; + return { create_ast_node({ m_source_code, rule_start.position(), position() }) }; } NonnullRefPtr Parser::parse_regexp_literal() @@ -1569,7 +1570,7 @@ NonnullRefPtr Parser::parse_regexp_literal() if (parsed_regex.error != regex::Error::NoError) syntax_error(String::formatted("RegExp compile error: {}", Regex(parsed_regex, parsed_pattern, parsed_flags).error_string()), rule_start.position()); - SourceRange range { m_state.current_token.filename(), rule_start.position(), position() }; + SourceRange range { m_source_code, rule_start.position(), position() }; return create_ast_node(move(range), move(parsed_regex), move(parsed_pattern), move(parsed_flags), pattern.to_string(), move(flags)); } @@ -1594,7 +1595,7 @@ NonnullRefPtr Parser::parse_unary_prefixed_expression() check_identifier_name_for_assignment_validity(name); } - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, UpdateOp::Increment, move(rhs), true); + return create_ast_node({ m_source_code, rule_start.position(), position() }, UpdateOp::Increment, move(rhs), true); } case TokenType::MinusMinus: { consume(); @@ -1611,29 +1612,29 @@ NonnullRefPtr Parser::parse_unary_prefixed_expression() check_identifier_name_for_assignment_validity(name); } - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, UpdateOp::Decrement, move(rhs), true); + return create_ast_node({ m_source_code, rule_start.position(), position() }, UpdateOp::Decrement, move(rhs), true); } case TokenType::ExclamationMark: consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, UnaryOp::Not, parse_expression(precedence, associativity)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, UnaryOp::Not, parse_expression(precedence, associativity)); case TokenType::Tilde: consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, UnaryOp::BitwiseNot, parse_expression(precedence, associativity)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, UnaryOp::BitwiseNot, parse_expression(precedence, associativity)); case TokenType::Plus: consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, UnaryOp::Plus, parse_expression(precedence, associativity)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, UnaryOp::Plus, parse_expression(precedence, associativity)); case TokenType::Minus: consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, UnaryOp::Minus, parse_expression(precedence, associativity)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, UnaryOp::Minus, parse_expression(precedence, associativity)); case TokenType::Typeof: consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, UnaryOp::Typeof, parse_expression(precedence, associativity)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, UnaryOp::Typeof, parse_expression(precedence, associativity)); case TokenType::Void: consume(); // FIXME: This check is really hiding the fact that we don't deal with different expressions correctly. if (match(TokenType::Yield) && m_state.in_generator_function_context) syntax_error("'yield' is not an identifier in generator function context"); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, UnaryOp::Void, parse_expression(precedence, associativity)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, UnaryOp::Void, parse_expression(precedence, associativity)); case TokenType::Delete: { consume(); auto rhs_start = position(); @@ -1646,12 +1647,12 @@ NonnullRefPtr Parser::parse_unary_prefixed_expression() if (member_expression.ends_in_private_name()) syntax_error("Private fields cannot be deleted"); } - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, UnaryOp::Delete, move(rhs)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, UnaryOp::Delete, move(rhs)); } default: expected("primary expression"); consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }); + return create_ast_node({ m_source_code, rule_start.position(), position() }); } } @@ -1661,9 +1662,9 @@ NonnullRefPtr Parser::parse_property_key() if (match(TokenType::StringLiteral)) { return parse_string_literal(consume()); } else if (match(TokenType::NumericLiteral)) { - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, consume().double_value()); + return create_ast_node({ m_source_code, rule_start.position(), position() }, consume().double_value()); } else if (match(TokenType::BigIntLiteral)) { - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, consume().value()); + return create_ast_node({ m_source_code, rule_start.position(), position() }, consume().value()); } else if (match(TokenType::BracketOpen)) { consume(TokenType::BracketOpen); auto result = parse_expression(2); @@ -1672,7 +1673,7 @@ NonnullRefPtr Parser::parse_property_key() } else { if (!match_identifier_name()) expected("IdentifierName"); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, consume().value()); + return create_ast_node({ m_source_code, rule_start.position(), position() }, consume().value()); } } @@ -1704,7 +1705,7 @@ NonnullRefPtr Parser::parse_object_expression() if (match(TokenType::TripleDot)) { consume(); property_key = parse_expression(4); - properties.append(create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, *property_key, nullptr, ObjectProperty::Type::Spread, false)); + properties.append(create_ast_node({ m_source_code, rule_start.position(), position() }, *property_key, nullptr, ObjectProperty::Type::Spread, false)); if (!match(TokenType::Comma)) break; consume(TokenType::Comma); @@ -1740,8 +1741,8 @@ NonnullRefPtr Parser::parse_object_expression() property_type = ObjectProperty::Type::Setter; property_key = parse_property_key(); } else { - property_key = create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, identifier.value()); - property_value = create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, identifier.flystring_value()); + property_key = create_ast_node({ m_source_code, rule_start.position(), position() }, identifier.value()); + property_value = create_ast_node({ m_source_code, rule_start.position(), position() }, identifier.flystring_value()); } } else { property_key = parse_property_key(); @@ -1777,7 +1778,7 @@ NonnullRefPtr Parser::parse_object_expression() if (function_kind == FunctionKind::Async || function_kind == FunctionKind::AsyncGenerator) parse_options |= FunctionNodeParseOptions::IsAsyncFunction; auto function = parse_function_node(parse_options, function_start); - properties.append(create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, *property_key, function, property_type, true)); + properties.append(create_ast_node({ m_source_code, rule_start.position(), position() }, *property_key, function, property_type, true)); } else if (match(TokenType::Colon)) { if (!property_key) { expected("a property name"); @@ -1792,7 +1793,7 @@ NonnullRefPtr Parser::parse_object_expression() } if (is_proto && property_type == ObjectProperty::Type::KeyValue) property_type = ObjectProperty::Type::ProtoSetter; - properties.append(create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, *property_key, parse_expression(2), property_type, false)); + properties.append(create_ast_node({ m_source_code, rule_start.position(), position() }, *property_key, parse_expression(2), property_type, false)); } else if (property_key && property_value) { if (m_state.strict_mode && is(*property_key)) { auto& string_literal = static_cast(*property_key); @@ -1800,7 +1801,7 @@ NonnullRefPtr Parser::parse_object_expression() syntax_error(String::formatted("'{}' is a reserved keyword", string_literal.value())); } - properties.append(create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, *property_key, *property_value, property_type, false)); + properties.append(create_ast_node({ m_source_code, rule_start.position(), position() }, *property_key, *property_value, property_type, false)); } else { expected("a property"); skip_to_next_property(); @@ -1814,7 +1815,7 @@ NonnullRefPtr Parser::parse_object_expression() consume(TokenType::CurlyClose); return create_ast_node( - { m_state.current_token.filename(), rule_start.position(), position() }, + { m_source_code, rule_start.position(), position() }, move(properties), move(invalid_object_literal_property_range)); } @@ -1830,7 +1831,7 @@ NonnullRefPtr Parser::parse_array_expression() if (match(TokenType::TripleDot)) { consume(TokenType::TripleDot); - expression = create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, parse_expression(2)); + expression = create_ast_node({ m_source_code, rule_start.position(), position() }, parse_expression(2)); } else if (match_expression()) { expression = parse_expression(2); } @@ -1842,7 +1843,7 @@ NonnullRefPtr Parser::parse_array_expression() } consume(TokenType::BracketClose); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(elements)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, move(elements)); } NonnullRefPtr Parser::parse_string_literal(Token const& token, StringLiteralType string_literal_type, bool* contains_invalid_escape) @@ -1881,7 +1882,7 @@ NonnullRefPtr Parser::parse_string_literal(Token const& token, St auto is_use_strict_directive = string_literal_type == StringLiteralType::Normal && (token.value() == "'use strict'" || token.value() == "\"use strict\""); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, string, is_use_strict_directive); + return create_ast_node({ m_source_code, rule_start.position(), position() }, string, is_use_strict_directive); } NonnullRefPtr Parser::parse_template_literal(bool is_tagged) @@ -1893,7 +1894,7 @@ NonnullRefPtr Parser::parse_template_literal(bool is_tagged) NonnullRefPtrVector raw_strings; auto append_empty_string = [this, &rule_start, &expressions, &raw_strings, is_tagged]() { - auto string_literal = create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, ""); + auto string_literal = create_ast_node({ m_source_code, rule_start.position(), position() }, ""); expressions.append(string_literal); if (is_tagged) raw_strings.append(string_literal); @@ -1911,22 +1912,22 @@ NonnullRefPtr Parser::parse_template_literal(bool is_tagged) is_tagged ? &contains_invalid_escape : nullptr); // An invalid string leads to a cooked value of `undefined` but still gives the raw string. if (contains_invalid_escape) - expressions.append(create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() })); + expressions.append(create_ast_node({ m_source_code, rule_start.position(), position() })); else expressions.append(move(parsed_string_value)); if (is_tagged) - raw_strings.append(create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, token.raw_template_value())); + raw_strings.append(create_ast_node({ m_source_code, rule_start.position(), position() }, token.raw_template_value())); } else if (match(TokenType::TemplateLiteralExprStart)) { consume(TokenType::TemplateLiteralExprStart); if (match(TokenType::TemplateLiteralExprEnd)) { syntax_error("Empty template literal expression block"); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, expressions); + return create_ast_node({ m_source_code, rule_start.position(), position() }, expressions); } expressions.append(parse_expression(0)); if (match(TokenType::UnterminatedTemplateLiteral)) { syntax_error("Unterminated template literal"); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, expressions); + return create_ast_node({ m_source_code, rule_start.position(), position() }, expressions); } consume(TokenType::TemplateLiteralExprEnd); @@ -1945,8 +1946,8 @@ NonnullRefPtr Parser::parse_template_literal(bool is_tagged) } if (is_tagged) - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, expressions, raw_strings); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, expressions); + return create_ast_node({ m_source_code, rule_start.position(), position() }, expressions, raw_strings); + return create_ast_node({ m_source_code, rule_start.position(), position() }, expressions); } NonnullRefPtr Parser::parse_expression(int min_precedence, Associativity associativity, ForbiddenTokens forbidden) @@ -1979,7 +1980,7 @@ NonnullRefPtr Parser::parse_expression(int min_precedence, Associati while (match(TokenType::TemplateLiteralStart)) { auto template_literal = parse_template_literal(true); - expression = create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(expression), move(template_literal)); + expression = create_ast_node({ m_source_code, rule_start.position(), position() }, move(expression), move(template_literal)); } if (should_continue_parsing) { while (match_secondary_expression(forbidden)) { @@ -1996,7 +1997,7 @@ NonnullRefPtr Parser::parse_expression(int min_precedence, Associati forbidden = forbidden.merge(result.forbidden); while (match(TokenType::TemplateLiteralStart) && !is(*expression)) { auto template_literal = parse_template_literal(true); - expression = create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(expression), move(template_literal)); + expression = create_ast_node({ m_source_code, rule_start.position(), position() }, move(expression), move(template_literal)); } } } @@ -2031,7 +2032,7 @@ NonnullRefPtr Parser::parse_expression(int min_precedence, Associati consume(); expressions.append(parse_expression(2)); } - expression = create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(expressions)); + expression = create_ast_node({ m_source_code, rule_start.position(), position() }, move(expressions)); } return expression; } @@ -2042,92 +2043,92 @@ Parser::ExpressionResult Parser::parse_secondary_expression(NonnullRefPtr({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::Addition, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, BinaryOp::Addition, move(lhs), parse_expression(min_precedence, associativity, forbidden)); case TokenType::PlusEquals: return parse_assignment_expression(AssignmentOp::AdditionAssignment, move(lhs), min_precedence, associativity, forbidden); case TokenType::Minus: consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::Subtraction, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, BinaryOp::Subtraction, move(lhs), parse_expression(min_precedence, associativity, forbidden)); case TokenType::MinusEquals: return parse_assignment_expression(AssignmentOp::SubtractionAssignment, move(lhs), min_precedence, associativity, forbidden); case TokenType::Asterisk: consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::Multiplication, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, BinaryOp::Multiplication, move(lhs), parse_expression(min_precedence, associativity, forbidden)); case TokenType::AsteriskEquals: return parse_assignment_expression(AssignmentOp::MultiplicationAssignment, move(lhs), min_precedence, associativity, forbidden); case TokenType::Slash: consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::Division, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, BinaryOp::Division, move(lhs), parse_expression(min_precedence, associativity, forbidden)); case TokenType::SlashEquals: return parse_assignment_expression(AssignmentOp::DivisionAssignment, move(lhs), min_precedence, associativity, forbidden); case TokenType::Percent: consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::Modulo, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, BinaryOp::Modulo, move(lhs), parse_expression(min_precedence, associativity, forbidden)); case TokenType::PercentEquals: return parse_assignment_expression(AssignmentOp::ModuloAssignment, move(lhs), min_precedence, associativity, forbidden); case TokenType::DoubleAsterisk: consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::Exponentiation, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, BinaryOp::Exponentiation, move(lhs), parse_expression(min_precedence, associativity, forbidden)); case TokenType::DoubleAsteriskEquals: return parse_assignment_expression(AssignmentOp::ExponentiationAssignment, move(lhs), min_precedence, associativity, forbidden); case TokenType::GreaterThan: consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::GreaterThan, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, BinaryOp::GreaterThan, move(lhs), parse_expression(min_precedence, associativity, forbidden)); case TokenType::GreaterThanEquals: consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::GreaterThanEquals, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, BinaryOp::GreaterThanEquals, move(lhs), parse_expression(min_precedence, associativity, forbidden)); case TokenType::LessThan: consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::LessThan, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, BinaryOp::LessThan, move(lhs), parse_expression(min_precedence, associativity, forbidden)); case TokenType::LessThanEquals: consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::LessThanEquals, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, BinaryOp::LessThanEquals, move(lhs), parse_expression(min_precedence, associativity, forbidden)); case TokenType::EqualsEqualsEquals: consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::StrictlyEquals, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, BinaryOp::StrictlyEquals, move(lhs), parse_expression(min_precedence, associativity, forbidden)); case TokenType::ExclamationMarkEqualsEquals: consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::StrictlyInequals, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, BinaryOp::StrictlyInequals, move(lhs), parse_expression(min_precedence, associativity, forbidden)); case TokenType::EqualsEquals: consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::LooselyEquals, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, BinaryOp::LooselyEquals, move(lhs), parse_expression(min_precedence, associativity, forbidden)); case TokenType::ExclamationMarkEquals: consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::LooselyInequals, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, BinaryOp::LooselyInequals, move(lhs), parse_expression(min_precedence, associativity, forbidden)); case TokenType::In: consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::In, move(lhs), parse_expression(min_precedence, associativity)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, BinaryOp::In, move(lhs), parse_expression(min_precedence, associativity)); case TokenType::Instanceof: consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::InstanceOf, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, BinaryOp::InstanceOf, move(lhs), parse_expression(min_precedence, associativity, forbidden)); case TokenType::Ampersand: consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::BitwiseAnd, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, BinaryOp::BitwiseAnd, move(lhs), parse_expression(min_precedence, associativity, forbidden)); case TokenType::AmpersandEquals: return parse_assignment_expression(AssignmentOp::BitwiseAndAssignment, move(lhs), min_precedence, associativity, forbidden); case TokenType::Pipe: consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::BitwiseOr, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, BinaryOp::BitwiseOr, move(lhs), parse_expression(min_precedence, associativity, forbidden)); case TokenType::PipeEquals: return parse_assignment_expression(AssignmentOp::BitwiseOrAssignment, move(lhs), min_precedence, associativity, forbidden); case TokenType::Caret: consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::BitwiseXor, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, BinaryOp::BitwiseXor, move(lhs), parse_expression(min_precedence, associativity, forbidden)); case TokenType::CaretEquals: return parse_assignment_expression(AssignmentOp::BitwiseXorAssignment, move(lhs), min_precedence, associativity, forbidden); case TokenType::ShiftLeft: consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::LeftShift, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, BinaryOp::LeftShift, move(lhs), parse_expression(min_precedence, associativity, forbidden)); case TokenType::ShiftLeftEquals: return parse_assignment_expression(AssignmentOp::LeftShiftAssignment, move(lhs), min_precedence, associativity, forbidden); case TokenType::ShiftRight: consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::RightShift, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, BinaryOp::RightShift, move(lhs), parse_expression(min_precedence, associativity, forbidden)); case TokenType::ShiftRightEquals: return parse_assignment_expression(AssignmentOp::RightShiftAssignment, move(lhs), min_precedence, associativity, forbidden); case TokenType::UnsignedShiftRight: consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::UnsignedRightShift, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, BinaryOp::UnsignedRightShift, move(lhs), parse_expression(min_precedence, associativity, forbidden)); case TokenType::UnsignedShiftRightEquals: return parse_assignment_expression(AssignmentOp::UnsignedRightShiftAssignment, move(lhs), min_precedence, associativity, forbidden); case TokenType::ParenOpen: @@ -2142,15 +2143,15 @@ Parser::ExpressionResult Parser::parse_secondary_expression(NonnullRefPtr(*lhs)) syntax_error(String::formatted("Cannot access private field or method '{}' on super", m_state.current_token.value())); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(lhs), create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, consume().value())); + return create_ast_node({ m_source_code, rule_start.position(), position() }, move(lhs), create_ast_node({ m_source_code, rule_start.position(), position() }, consume().value())); } else if (!match_identifier_name()) { expected("IdentifierName"); } - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(lhs), create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, consume().flystring_value())); + return create_ast_node({ m_source_code, rule_start.position(), position() }, move(lhs), create_ast_node({ m_source_code, rule_start.position(), position() }, consume().flystring_value())); case TokenType::BracketOpen: { consume(TokenType::BracketOpen); - auto expression = create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(lhs), parse_expression(0), true); + auto expression = create_ast_node({ m_source_code, rule_start.position(), position() }, move(lhs), parse_expression(0), true); consume(TokenType::BracketClose); return expression; } @@ -2167,7 +2168,7 @@ Parser::ExpressionResult Parser::parse_secondary_expression(NonnullRefPtr({ m_state.current_token.filename(), rule_start.position(), position() }, UpdateOp::Increment, move(lhs)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, UpdateOp::Increment, move(lhs)); case TokenType::MinusMinus: // FIXME: Apparently for functions this should also not be enforced on a parser level, // other engines throw ReferenceError for foo()-- @@ -2180,24 +2181,24 @@ Parser::ExpressionResult Parser::parse_secondary_expression(NonnullRefPtr({ m_state.current_token.filename(), rule_start.position(), position() }, UpdateOp::Decrement, move(lhs)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, UpdateOp::Decrement, move(lhs)); case TokenType::DoubleAmpersand: { consume(); - auto expression = create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, LogicalOp::And, move(lhs), parse_expression(min_precedence, associativity, forbidden.forbid({ TokenType::DoubleQuestionMark }))); + auto expression = create_ast_node({ m_source_code, rule_start.position(), position() }, LogicalOp::And, move(lhs), parse_expression(min_precedence, associativity, forbidden.forbid({ TokenType::DoubleQuestionMark }))); return { expression, { TokenType::DoubleQuestionMark } }; } case TokenType::DoubleAmpersandEquals: return parse_assignment_expression(AssignmentOp::AndAssignment, move(lhs), min_precedence, associativity, forbidden); case TokenType::DoublePipe: { consume(); - auto expression = create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, LogicalOp::Or, move(lhs), parse_expression(min_precedence, associativity, forbidden.forbid({ TokenType::DoubleQuestionMark }))); + auto expression = create_ast_node({ m_source_code, rule_start.position(), position() }, LogicalOp::Or, move(lhs), parse_expression(min_precedence, associativity, forbidden.forbid({ TokenType::DoubleQuestionMark }))); return { expression, { TokenType::DoubleQuestionMark } }; } case TokenType::DoublePipeEquals: return parse_assignment_expression(AssignmentOp::OrAssignment, move(lhs), min_precedence, associativity, forbidden); case TokenType::DoubleQuestionMark: { consume(); - auto expression = create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, LogicalOp::NullishCoalescing, move(lhs), parse_expression(min_precedence, associativity, forbidden.forbid({ TokenType::DoubleAmpersand, TokenType::DoublePipe }))); + auto expression = create_ast_node({ m_source_code, rule_start.position(), position() }, LogicalOp::NullishCoalescing, move(lhs), parse_expression(min_precedence, associativity, forbidden.forbid({ TokenType::DoubleAmpersand, TokenType::DoublePipe }))); return { expression, { TokenType::DoubleAmpersand, TokenType::DoublePipe } }; } case TokenType::DoubleQuestionMarkEquals: @@ -2216,7 +2217,7 @@ Parser::ExpressionResult Parser::parse_secondary_expression(NonnullRefPtr({ m_state.current_token.filename(), rule_start.position(), position() }); + return create_ast_node({ m_source_code, rule_start.position(), position() }); } } @@ -2296,7 +2297,7 @@ NonnullRefPtr Parser::parse_assignment_expression(Assignme if (binding_pattern) { auto rhs = parse_expression(min_precedence, associativity); return create_ast_node( - { m_state.current_token.filename(), rule_start.position(), position() }, + { m_source_code, rule_start.position(), position() }, assignment_op, binding_pattern.release_nonnull(), move(rhs)); @@ -2312,7 +2313,7 @@ NonnullRefPtr Parser::parse_assignment_expression(Assignme syntax_error("Cannot assign to function call"); } auto rhs = parse_expression(min_precedence, associativity, forbidden); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, assignment_op, move(lhs), move(rhs)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, assignment_op, move(lhs), move(rhs)); } NonnullRefPtr Parser::parse_identifier() @@ -2322,7 +2323,7 @@ NonnullRefPtr Parser::parse_identifier() if (m_state.in_class_field_initializer && token.value() == "arguments"sv) syntax_error("'arguments' is not allowed in class field initializer"); return create_ast_node( - { m_state.current_token.filename(), identifier_start, position() }, + { m_source_code, identifier_start, position() }, token.flystring_value()); } @@ -2356,9 +2357,9 @@ NonnullRefPtr Parser::parse_call_expression(NonnullRefPtr(*lhs)) - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(arguments)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, move(arguments)); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(lhs), move(arguments)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, move(lhs), move(arguments)); } NonnullRefPtr Parser::parse_new_expression() @@ -2388,7 +2389,7 @@ NonnullRefPtr Parser::parse_new_expression() consume(TokenType::ParenClose); } - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(callee), move(arguments)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, move(callee), move(arguments)); } NonnullRefPtr Parser::parse_yield_expression() @@ -2412,7 +2413,7 @@ NonnullRefPtr Parser::parse_yield_expression() argument = parse_expression(2); } - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(argument), yield_from); + return create_ast_node({ m_source_code, rule_start.position(), position() }, move(argument), yield_from); } NonnullRefPtr Parser::parse_await_expression() @@ -2430,7 +2431,7 @@ NonnullRefPtr Parser::parse_await_expression() m_state.current_scope_pusher->set_contains_await_expression(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(argument)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, move(argument)); } NonnullRefPtr Parser::parse_return_statement() @@ -2443,16 +2444,16 @@ NonnullRefPtr Parser::parse_return_statement() // Automatic semicolon insertion: terminate statement when return is followed by newline if (m_state.current_token.trivia_contains_line_terminator()) - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, nullptr); + return create_ast_node({ m_source_code, rule_start.position(), position() }, nullptr); if (match_expression()) { auto expression = parse_expression(0); consume_or_insert_semicolon(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(expression)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, move(expression)); } consume_or_insert_semicolon(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, nullptr); + return create_ast_node({ m_source_code, rule_start.position(), position() }, nullptr); } void Parser::parse_statement_list(ScopeNode& output_node, AllowLabelledFunction allow_labelled_functions) @@ -2475,7 +2476,7 @@ void Parser::parse_statement_list(ScopeNode& output_node, AllowLabelledFunction NonnullRefPtr Parser::parse_function_body(Vector const& parameters, FunctionKind function_kind, bool& contains_direct_call_to_eval) { auto rule_start = push_start(); - auto function_body = create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }); + auto function_body = create_ast_node({ m_source_code, rule_start.position(), position() }); ScopePusher function_scope = ScopePusher::function_scope(*this, function_body, parameters); // FIXME <- auto has_use_strict = parse_directive(function_body); bool previous_strict_mode = m_state.strict_mode; @@ -2544,7 +2545,7 @@ NonnullRefPtr Parser::parse_function_body(Vector Parser::parse_block_statement() { auto rule_start = push_start(); - auto block = create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }); + auto block = create_ast_node({ m_source_code, rule_start.position(), position() }); ScopePusher block_scope = ScopePusher::block_scope(*this, block); consume(TokenType::CurlyOpen); parse_statement_list(block); @@ -2640,7 +2641,7 @@ NonnullRefPtr Parser::parse_function_node(u16 parse_options, O auto function_end_offset = position().offset - m_state.current_token.trivia().length(); auto source_text = String { m_state.lexer.source().substring_view(function_start_offset, function_end_offset - function_start_offset) }; return create_ast_node( - { m_state.current_token.filename(), rule_start.position(), position() }, + { m_source_code, rule_start.position(), position() }, name, move(source_text), move(body), move(parameters), function_length, function_kind, has_strict_directive, m_state.function_might_need_arguments_object, contains_direct_call_to_eval); @@ -2800,17 +2801,17 @@ RefPtr Parser::parse_binding_pattern(Parser::AllowDuplicates all auto string_literal = parse_string_literal(token); name = create_ast_node( - { m_state.current_token.filename(), rule_start.position(), position() }, + { m_source_code, rule_start.position(), position() }, string_literal->value()); } else if (match(TokenType::BigIntLiteral)) { auto string_value = consume().flystring_value(); VERIFY(string_value.ends_with("n"sv)); name = create_ast_node( - { m_state.current_token.filename(), rule_start.position(), position() }, + { m_source_code, rule_start.position(), position() }, FlyString(string_value.view().substring_view(0, string_value.length() - 1))); } else { name = create_ast_node( - { m_state.current_token.filename(), rule_start.position(), position() }, + { m_source_code, rule_start.position(), position() }, consume().flystring_value()); } } else if (match(TokenType::BracketOpen)) { @@ -2848,7 +2849,7 @@ RefPtr Parser::parse_binding_pattern(Parser::AllowDuplicates all alias = binding_pattern.release_nonnull(); } else if (match_identifier_name()) { alias = create_ast_node( - { m_state.current_token.filename(), rule_start.position(), position() }, + { m_source_code, rule_start.position(), position() }, consume().flystring_value()); } else { @@ -2887,7 +2888,7 @@ RefPtr Parser::parse_binding_pattern(Parser::AllowDuplicates all // BindingElement must always have an Empty name field auto identifier_name = consume_identifier().flystring_value(); alias = create_ast_node( - { m_state.current_token.filename(), rule_start.position(), position() }, + { m_source_code, rule_start.position(), position() }, identifier_name); } else { expected("identifier or binding pattern"); @@ -2973,7 +2974,7 @@ NonnullRefPtr Parser::parse_variable_declaration(bool for_l auto identifier_start = push_start(); auto name = consume_identifier().flystring_value(); target = create_ast_node( - { m_state.current_token.filename(), rule_start.position(), position() }, + { m_source_code, rule_start.position(), position() }, name); check_identifier_name_for_assignment_validity(name); if ((declaration_kind == DeclarationKind::Let || declaration_kind == DeclarationKind::Const) && name == "let"sv) @@ -2992,14 +2993,14 @@ NonnullRefPtr Parser::parse_variable_declaration(bool for_l syntax_error("Identifier must not be a reserved word in strict mode ('yield')"); target = create_ast_node( - { m_state.current_token.filename(), rule_start.position(), position() }, + { m_source_code, rule_start.position(), position() }, consume().flystring_value()); } else if (!m_state.await_expression_is_valid && match(TokenType::Async)) { if (m_program_type == Program::Type::Module) syntax_error("Identifier must not be a reserved word in modules ('async')"); target = create_ast_node( - { m_state.current_token.filename(), rule_start.position(), position() }, + { m_source_code, rule_start.position(), position() }, consume().flystring_value()); } @@ -3028,7 +3029,7 @@ NonnullRefPtr Parser::parse_variable_declaration(bool for_l } declarations.append(create_ast_node( - { m_state.current_token.filename(), rule_start.position(), position() }, + { m_source_code, rule_start.position(), position() }, move(target).downcast, NonnullRefPtr>(), move(init))); @@ -3041,7 +3042,7 @@ NonnullRefPtr Parser::parse_variable_declaration(bool for_l if (!for_loop_variable_declaration) consume_or_insert_semicolon(); - auto declaration = create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, declaration_kind, move(declarations)); + auto declaration = create_ast_node({ m_source_code, rule_start.position(), position() }, declaration_kind, move(declarations)); return declaration; } @@ -3053,12 +3054,12 @@ NonnullRefPtr Parser::parse_throw_statement() // Automatic semicolon insertion: terminate statement when throw is followed by newline if (m_state.current_token.trivia_contains_line_terminator()) { syntax_error("No line break is allowed between 'throw' and its expression"); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() })); + return create_ast_node({ m_source_code, rule_start.position(), position() }, create_ast_node({ m_source_code, rule_start.position(), position() })); } auto expression = parse_expression(0); consume_or_insert_semicolon(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(expression)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, move(expression)); } NonnullRefPtr Parser::parse_break_statement() @@ -3082,7 +3083,7 @@ NonnullRefPtr Parser::parse_break_statement() if (target_label.is_null() && !m_state.in_break_context) syntax_error("Unlabeled 'break' not allowed outside of a loop or switch statement"); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, target_label); + return create_ast_node({ m_source_code, rule_start.position(), position() }, target_label); } NonnullRefPtr Parser::parse_continue_statement() @@ -3095,7 +3096,7 @@ NonnullRefPtr Parser::parse_continue_statement() FlyString target_label; if (match(TokenType::Semicolon)) { consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, target_label); + return create_ast_node({ m_source_code, rule_start.position(), position() }, target_label); } if (!m_state.current_token.trivia_contains_line_terminator() && match_identifier()) { auto label_position = position(); @@ -3108,7 +3109,7 @@ NonnullRefPtr Parser::parse_continue_statement() label->value = label_position; } consume_or_insert_semicolon(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, target_label); + return create_ast_node({ m_source_code, rule_start.position(), position() }, target_label); } NonnullRefPtr Parser::parse_conditional_expression(NonnullRefPtr test, ForbiddenTokens forbidden) @@ -3118,7 +3119,7 @@ NonnullRefPtr Parser::parse_conditional_expression(Nonnul auto consequent = parse_expression(2); consume(TokenType::Colon); auto alternate = parse_expression(2, Associativity::Right, forbidden); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(test), move(consequent), move(alternate)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, move(test), move(consequent), move(alternate)); } NonnullRefPtr Parser::parse_optional_chain(NonnullRefPtr base) @@ -3144,7 +3145,7 @@ NonnullRefPtr Parser::parse_optional_chain(NonnullRefPtr({ m_state.current_token.filename(), start, position() }, private_identifier.value()), + create_ast_node({ m_source_code, start, position() }, private_identifier.value()), OptionalChain::Mode::Optional }); break; } @@ -3161,7 +3162,7 @@ NonnullRefPtr Parser::parse_optional_chain(NonnullRefPtr({ m_state.current_token.filename(), start, position() }, identifier.flystring_value()), + create_ast_node({ m_source_code, start, position() }, identifier.flystring_value()), OptionalChain::Mode::Optional, }); } else { @@ -3180,14 +3181,14 @@ NonnullRefPtr Parser::parse_optional_chain(NonnullRefPtr({ m_state.current_token.filename(), start, position() }, private_identifier.value()), + create_ast_node({ m_source_code, start, position() }, private_identifier.value()), OptionalChain::Mode::NotOptional, }); } else if (match_identifier_name()) { auto start = position(); auto identifier = consume(); chain.append(OptionalChain::MemberReference { - create_ast_node({ m_state.current_token.filename(), start, position() }, identifier.flystring_value()), + create_ast_node({ m_source_code, start, position() }, identifier.flystring_value()), OptionalChain::Mode::NotOptional, }); } else { @@ -3211,7 +3212,7 @@ NonnullRefPtr Parser::parse_optional_chain(NonnullRefPtr( - { m_state.current_token.filename(), rule_start.position(), position() }, + { m_source_code, rule_start.position(), position() }, move(base), move(chain)); } @@ -3236,7 +3237,7 @@ NonnullRefPtr Parser::parse_try_statement() if (!handler && !finalizer) syntax_error("try statement must have a 'catch' or 'finally' clause"); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(block), move(handler), move(finalizer)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, move(block), move(handler), move(finalizer)); } NonnullRefPtr Parser::parse_do_while_statement() @@ -3261,7 +3262,7 @@ NonnullRefPtr Parser::parse_do_while_statement() if (match(TokenType::Semicolon)) consume(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(test), move(body)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, move(test), move(body)); } NonnullRefPtr Parser::parse_while_statement() @@ -3278,7 +3279,7 @@ NonnullRefPtr Parser::parse_while_statement() TemporaryChange continue_change(m_state.in_continue_context, true); auto body = parse_statement(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(test), move(body)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, move(test), move(body)); } NonnullRefPtr Parser::parse_switch_statement() @@ -3294,7 +3295,7 @@ NonnullRefPtr Parser::parse_switch_statement() NonnullRefPtrVector cases; - auto switch_statement = create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(determinant)); + auto switch_statement = create_ast_node({ m_source_code, rule_start.position(), position() }, move(determinant)); ScopePusher switch_scope = ScopePusher::block_scope(*this, switch_statement); @@ -3324,7 +3325,7 @@ NonnullRefPtr Parser::parse_with_statement() consume(TokenType::ParenClose); auto body = parse_statement(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(object), move(body)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, move(object), move(body)); } NonnullRefPtr Parser::parse_switch_case() @@ -3340,7 +3341,7 @@ NonnullRefPtr Parser::parse_switch_case() NonnullRefPtrVector consequent; TemporaryChange break_change(m_state.in_break_context, true); - auto switch_case = create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(test)); + auto switch_case = create_ast_node({ m_source_code, rule_start.position(), position() }, move(test)); parse_statement_list(switch_case); return switch_case; @@ -3395,13 +3396,13 @@ NonnullRefPtr Parser::parse_catch_clause() if (pattern_parameter) { return create_ast_node( - { m_state.current_token.filename(), rule_start.position(), position() }, + { m_source_code, rule_start.position(), position() }, pattern_parameter.release_nonnull(), move(body)); } return create_ast_node( - { m_state.current_token.filename(), rule_start.position(), position() }, + { m_source_code, rule_start.position(), position() }, move(parameter), move(body)); } @@ -3418,7 +3419,7 @@ NonnullRefPtr Parser::parse_if_statement() // The semantics of such a synthetic BlockStatement includes the web legacy // compatibility semantics specified in B.3.2. VERIFY(match(TokenType::Function)); - auto block = create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }); + auto block = create_ast_node({ m_source_code, rule_start.position(), position() }); ScopePusher block_scope = ScopePusher::block_scope(*this, *block); auto declaration = parse_declaration(); VERIFY(m_state.current_scope_pusher); @@ -3453,7 +3454,7 @@ NonnullRefPtr Parser::parse_if_statement() else alternate = parse_statement(); } - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(predicate), move(*consequent), move(alternate)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, move(predicate), move(*consequent), move(alternate)); } NonnullRefPtr Parser::parse_for_statement() @@ -3549,7 +3550,7 @@ NonnullRefPtr Parser::parse_for_statement() ScopePusher for_loop_scope = ScopePusher::for_loop_scope(*this, init); auto body = parse_statement(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(init), move(test), move(update), move(body)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, move(init), move(test), move(update), move(body)); } NonnullRefPtr Parser::parse_for_in_of_statement(NonnullRefPtr lhs, IsForAwaitLoop is_for_await_loop) @@ -3608,10 +3609,10 @@ NonnullRefPtr Parser::parse_for_in_of_statement(NonnullRefPtr({ m_state.current_token.filename(), rule_start.position(), position() }, move(for_declaration), move(rhs), move(body)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, move(for_declaration), move(rhs), move(body)); if (is_for_await_loop == IsForAwaitLoop::Yes) - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(for_declaration), move(rhs), move(body)); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(for_declaration), move(rhs), move(body)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, move(for_declaration), move(rhs), move(body)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, move(for_declaration), move(rhs), move(body)); } NonnullRefPtr Parser::parse_debugger_statement() @@ -3619,7 +3620,7 @@ NonnullRefPtr Parser::parse_debugger_statement() auto rule_start = push_start(); consume(TokenType::Debugger); consume_or_insert_semicolon(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }); + return create_ast_node({ m_source_code, rule_start.position(), position() }); } bool Parser::match(TokenType type) const @@ -4162,7 +4163,7 @@ NonnullRefPtr Parser::parse_import_statement(Program& program) if (match(TokenType::StringLiteral)) { // import ModuleSpecifier ; auto module_request = parse_module_request(); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(module_request)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, move(module_request)); } auto match_imported_binding = [&] { @@ -4302,7 +4303,7 @@ NonnullRefPtr Parser::parse_import_statement(Program& program) entries.append(move(entry.entry)); } - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(module_request), move(entries)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, move(module_request), move(entries)); } NonnullRefPtr Parser::parse_export_statement(Program& program) @@ -4603,7 +4604,7 @@ NonnullRefPtr Parser::parse_export_statement(Program& program) entries.append(move(entry.entry)); } - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(expression), move(entries), is_default, move(from_specifier)); + return create_ast_node({ m_source_code, rule_start.position(), position() }, move(expression), move(entries), is_default, move(from_specifier)); } Parser::ForbiddenTokens::ForbiddenTokens(std::initializer_list const& forbidden) diff --git a/Userland/Libraries/LibJS/Parser.h b/Userland/Libraries/LibJS/Parser.h index 4e9b904aaa..9d00032463 100644 --- a/Userland/Libraries/LibJS/Parser.h +++ b/Userland/Libraries/LibJS/Parser.h @@ -343,6 +343,7 @@ private: } }; + NonnullRefPtr m_source_code; Vector m_rule_starts; ParserState m_state; FlyString m_filename; diff --git a/Userland/Libraries/LibJS/Runtime/Error.cpp b/Userland/Libraries/LibJS/Runtime/Error.cpp index 25642c4564..5c2946cd27 100644 --- a/Userland/Libraries/LibJS/Runtime/Error.cpp +++ b/Userland/Libraries/LibJS/Runtime/Error.cpp @@ -54,6 +54,8 @@ ThrowCompletionOr Error::install_error_cause(Value options) void Error::populate_stack() { + static auto dummy_source_range = SourceRange { .code = SourceCode::create("", ""), .start = {}, .end = {} }; + auto& vm = this->vm(); m_traceback.ensure_capacity(vm.execution_context_stack().size()); for (ssize_t i = vm.execution_context_stack().size() - 1; i >= 0; i--) { @@ -67,7 +69,7 @@ void Error::populate_stack() // reaction jobs (which aren't called anywhere from the source code). // They're not going to generate any _unhandled_ exceptions though, so a meaningless // source range is fine. - context->current_node ? context->current_node->source_range() : SourceRange {}); + context->current_node ? context->current_node->source_range() : dummy_source_range); } } @@ -82,12 +84,12 @@ String Error::stack_string() const auto const& frame = m_traceback[i]; auto function_name = frame.function_name; // Note: Since we don't know whether we have a valid SourceRange here we just check for some default values. - if (!frame.source_range.filename.is_null() || frame.source_range.start.offset != 0 || frame.source_range.end.offset != 0) { + if (!frame.source_range.filename().is_null() || frame.source_range.start.offset != 0 || frame.source_range.end.offset != 0) { if (function_name == ""sv) - stack_string_builder.appendff(" at {}:{}:{}\n", frame.source_range.filename, frame.source_range.start.line, frame.source_range.start.column); + stack_string_builder.appendff(" at {}:{}:{}\n", frame.source_range.filename(), frame.source_range.start.line, frame.source_range.start.column); else - stack_string_builder.appendff(" at {} ({}:{}:{})\n", function_name, frame.source_range.filename, frame.source_range.start.line, frame.source_range.start.column); + stack_string_builder.appendff(" at {} ({}:{}:{})\n", function_name, frame.source_range.filename(), frame.source_range.start.line, frame.source_range.start.column); } else { stack_string_builder.appendff(" at {}\n", function_name.is_empty() ? ""sv : function_name.view()); } diff --git a/Userland/Libraries/LibJS/Runtime/VM.cpp b/Userland/Libraries/LibJS/Runtime/VM.cpp index 5a2a72cb4d..ccf701f154 100644 --- a/Userland/Libraries/LibJS/Runtime/VM.cpp +++ b/Userland/Libraries/LibJS/Runtime/VM.cpp @@ -728,8 +728,8 @@ void VM::dump_backtrace() const for (ssize_t i = m_execution_context_stack.size() - 1; i >= 0; --i) { auto& frame = m_execution_context_stack[i]; if (frame->current_node) { - auto& source_range = frame->current_node->source_range(); - dbgln("-> {} @ {}:{},{}", frame->function_name, source_range.filename, source_range.start.line, source_range.start.column); + auto source_range = frame->current_node->source_range(); + dbgln("-> {} @ {}:{},{}", frame->function_name, source_range.filename(), source_range.start.line, source_range.start.column); } else { dbgln("-> {}", frame->function_name); } diff --git a/Userland/Libraries/LibJS/SourceCode.cpp b/Userland/Libraries/LibJS/SourceCode.cpp new file mode 100644 index 0000000000..89a844f3e3 --- /dev/null +++ b/Userland/Libraries/LibJS/SourceCode.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2022, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include + +namespace JS { + +NonnullRefPtr SourceCode::create(String filename, String code) +{ + return adopt_ref(*new SourceCode(move(filename), move(code))); +} + +SourceCode::SourceCode(String filename, String code) + : m_filename(move(filename)) + , m_code(move(code)) +{ +} + +String const& SourceCode::filename() const +{ + return m_filename; +} + +String const& SourceCode::code() const +{ + return m_code; +} + +SourceRange SourceCode::range_from_offsets(u32 start_offset, u32 end_offset) const +{ + Position start; + Position end; + + size_t line = 1; + size_t column = 1; + + bool previous_code_point_was_carriage_return = false; + + Utf8View view(m_code); + for (auto it = view.begin(); it != view.end(); ++it) { + + if (start_offset == view.byte_offset_of(it)) { + start = Position { + .line = line, + .column = column, + .offset = start_offset, + }; + } + + if (end_offset == view.byte_offset_of(it)) { + end = Position { + .line = line, + .column = column, + .offset = end_offset, + }; + break; + } + + u32 code_point = *it; + + bool is_line_terminator = code_point == '\r' || (code_point == '\n' && !previous_code_point_was_carriage_return) || code_point == LINE_SEPARATOR || code_point == PARAGRAPH_SEPARATOR; + previous_code_point_was_carriage_return = code_point == '\r'; + + if (is_line_terminator) { + ++line; + column = 1; + continue; + } + ++column; + } + + return SourceRange { *this, start, end }; +} + +} diff --git a/Userland/Libraries/LibJS/SourceCode.h b/Userland/Libraries/LibJS/SourceCode.h new file mode 100644 index 0000000000..3980efed69 --- /dev/null +++ b/Userland/Libraries/LibJS/SourceCode.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2022, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace JS { + +class SourceCode : public RefCounted { +public: + static NonnullRefPtr create(String filename, String code); + + String const& filename() const; + String const& code() const; + + SourceRange range_from_offsets(u32 start_offset, u32 end_offset) const; + +private: + SourceCode(String filename, String code); + + String m_filename; + String m_code; +}; + +} diff --git a/Userland/Libraries/LibJS/SourceRange.h b/Userland/Libraries/LibJS/SourceRange.h index 485e2b2a61..b3e9377fc8 100644 --- a/Userland/Libraries/LibJS/SourceRange.h +++ b/Userland/Libraries/LibJS/SourceRange.h @@ -8,6 +8,7 @@ #include #include +#include namespace JS { @@ -20,9 +21,11 @@ struct Position { struct SourceRange { [[nodiscard]] bool contains(Position const& position) const { return position.offset <= end.offset && position.offset >= start.offset; } - StringView filename; + NonnullRefPtr code; Position start; Position end; + + String const& filename() const; }; } diff --git a/Userland/Libraries/LibWeb/HTML/Scripting/ExceptionReporter.cpp b/Userland/Libraries/LibWeb/HTML/Scripting/ExceptionReporter.cpp index cb2fa3edd9..e50078e773 100644 --- a/Userland/Libraries/LibWeb/HTML/Scripting/ExceptionReporter.cpp +++ b/Userland/Libraries/LibWeb/HTML/Scripting/ExceptionReporter.cpp @@ -34,7 +34,7 @@ void report_exception_to_console(JS::Value value, JS::Realm& realm, ErrorInPromi for (auto& traceback_frame : error_value.traceback()) { auto& function_name = traceback_frame.function_name; auto& source_range = traceback_frame.source_range; - dbgln(" {} at {}:{}:{}", function_name, source_range.filename, source_range.start.line, source_range.start.column); + dbgln(" {} at {}:{}:{}", function_name, source_range.filename(), source_range.start.line, source_range.start.column); } console.report_exception(error_value, error_in_promise == ErrorInPromise::Yes);