diff options
author | Andreas Kling <kling@serenityos.org> | 2022-11-21 17:37:38 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2022-11-22 21:13:35 +0100 |
commit | b0b022507b2f73be2f4cef17deb8ece91dae6822 (patch) | |
tree | 5b1ac9a508c667ee060c29402da52c07abac7cd5 /Userland/Libraries | |
parent | 3d74d72bcb9763a695a53bf27114031fe62d146e (diff) | |
download | serenity-b0b022507b2f73be2f4cef17deb8ece91dae6822.zip |
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<SourceCode> (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!) :^)
Diffstat (limited to 'Userland/Libraries')
-rw-r--r-- | Userland/Libraries/LibJS/AST.cpp | 17 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/AST.h | 12 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Forward.h | 2 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Lexer.h | 8 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/MarkupGenerator.cpp | 2 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Parser.cpp | 311 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Parser.h | 1 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/Error.cpp | 10 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/VM.cpp | 4 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/SourceCode.cpp | 81 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/SourceCode.h | 30 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/SourceRange.h | 5 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/Scripting/ExceptionReporter.cpp | 2 |
14 files changed, 311 insertions, 175 deletions
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<Assertion> 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<void> 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<SourceCode> 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(" {}<br>", 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<EvalInitialState> 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<Program> 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<Declaration> Parser::parse_declaration() default: expected("declaration"); consume(); - return create_ast_node<ErrorDeclaration>({ m_state.current_token.filename(), rule_start.position(), position() }); + return create_ast_node<ErrorDeclaration>({ m_source_code, rule_start.position(), position() }); } } @@ -658,7 +659,7 @@ NonnullRefPtr<Statement> Parser::parse_statement(AllowLabelledFunction allow_lab return parse_debugger_statement(); case TokenType::Semicolon: consume(); - return create_ast_node<EmptyStatement>({ m_state.current_token.filename(), rule_start.position(), position() }); + return create_ast_node<EmptyStatement>({ 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<Statement> Parser::parse_statement(AllowLabelledFunction allow_lab auto expr = parse_expression(0); consume_or_insert_semicolon(); - return create_ast_node<ExpressionStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, move(expr)); + return create_ast_node<ExpressionStatement>({ m_source_code, rule_start.position(), position() }, move(expr)); } expected("statement"); consume(); - return create_ast_node<ErrorStatement>({ m_state.current_token.filename(), rule_start.position(), position() }); + return create_ast_node<ErrorStatement>({ m_source_code, rule_start.position(), position() }); } } @@ -835,10 +836,10 @@ RefPtr<FunctionExpression> 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<FunctionBody>({ m_state.current_token.filename(), rule_start.position(), position() }); + auto return_block = create_ast_node<FunctionBody>({ 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<ReturnStatement>({ m_filename, rule_start.position(), position() }, move(return_expression)); + return_block->append<ReturnStatement>({ 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<FunctionExpression> 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<FunctionExpression>( - { 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<LabelledStatement> Parser::try_parse_labelled_statement(AllowLabelledFunc m_state.labels_in_scope.remove(identifier); - return create_ast_node<LabelledStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, identifier, labelled_item.release_nonnull()); + return create_ast_node<LabelledStatement>({ m_source_code, rule_start.position(), position() }, identifier, labelled_item.release_nonnull()); } RefPtr<MetaProperty> Parser::try_parse_new_target_expression() @@ -983,7 +984,7 @@ RefPtr<MetaProperty> Parser::try_parse_new_target_expression() state_rollback_guard.disarm(); discard_saved_state(); - return create_ast_node<MetaProperty>({ m_state.current_token.filename(), rule_start.position(), position() }, MetaProperty::Type::NewTarget); + return create_ast_node<MetaProperty>({ m_source_code, rule_start.position(), position() }, MetaProperty::Type::NewTarget); } RefPtr<MetaProperty> Parser::try_parse_import_meta_expression() @@ -1008,7 +1009,7 @@ RefPtr<MetaProperty> Parser::try_parse_import_meta_expression() state_rollback_guard.disarm(); discard_saved_state(); - return create_ast_node<MetaProperty>({ m_state.current_token.filename(), rule_start.position(), position() }, MetaProperty::Type::ImportMeta); + return create_ast_node<MetaProperty>({ m_source_code, rule_start.position(), position() }, MetaProperty::Type::ImportMeta); } NonnullRefPtr<ImportCall> Parser::parse_import_call() @@ -1040,13 +1041,13 @@ NonnullRefPtr<ImportCall> Parser::parse_import_call() consume(TokenType::ParenClose); - return create_ast_node<ImportCall>({ m_state.current_token.filename(), rule_start.position(), position() }, move(argument), move(options)); + return create_ast_node<ImportCall>({ m_source_code, rule_start.position(), position() }, move(argument), move(options)); } NonnullRefPtr<ClassDeclaration> Parser::parse_class_declaration() { auto rule_start = push_start(); - return create_ast_node<ClassDeclaration>({ m_state.current_token.filename(), rule_start.position(), position() }, parse_class_expression(true)); + return create_ast_node<ClassDeclaration>({ m_source_code, rule_start.position(), position() }, parse_class_expression(true)); } NonnullRefPtr<ClassExpression> Parser::parse_class_expression(bool expect_class_name) @@ -1078,7 +1079,7 @@ NonnullRefPtr<ClassExpression> Parser::parse_class_expression(bool expect_class_ for (;;) { if (match(TokenType::TemplateLiteralStart)) { auto template_literal = parse_template_literal(true); - expression = create_ast_node<TaggedTemplateLiteral>({ m_state.current_token.filename(), rule_start.position(), position() }, move(expression), move(template_literal)); + expression = create_ast_node<TaggedTemplateLiteral>({ 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<ClassExpression> Parser::parse_class_expression(bool expect_class_ switch (m_state.current_token.type()) { case TokenType::Identifier: name = consume().value(); - property_key = create_ast_node<StringLiteral>({ m_state.current_token.filename(), rule_start.position(), position() }, name); + property_key = create_ast_node<StringLiteral>({ m_source_code, rule_start.position(), position() }, name); break; case TokenType::PrivateIdentifier: name = consume().value(); @@ -1204,7 +1205,7 @@ NonnullRefPtr<ClassExpression> Parser::parse_class_expression(bool expect_class_ syntax_error(String::formatted("Duplicate private field or method named '{}'", name)); } - property_key = create_ast_node<PrivateIdentifier>({ m_state.current_token.filename(), rule_start.position(), position() }, name); + property_key = create_ast_node<PrivateIdentifier>({ m_source_code, rule_start.position(), position() }, name); break; case TokenType::StringLiteral: { auto string_literal = parse_string_literal(consume()); @@ -1243,12 +1244,12 @@ NonnullRefPtr<ClassExpression> Parser::parse_class_expression(bool expect_class_ method_kind = ClassMethod::Kind::Method; break; } - property_key = create_ast_node<StringLiteral>({ m_state.current_token.filename(), rule_start.position(), position() }, name); + property_key = create_ast_node<StringLiteral>({ 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<FunctionBody>({ m_state.current_token.filename(), rule_start.position(), position() }); + auto static_init_block = create_ast_node<FunctionBody>({ 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<ClassExpression> Parser::parse_class_expression(bool expect_class_ parse_statement_list(static_init_block); consume(TokenType::CurlyClose); - elements.append(create_ast_node<StaticInitializer>({ 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<StaticInitializer>({ 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<ClassExpression> Parser::parse_class_expression(bool expect_class_ if (is_constructor) { constructor = move(function); } else if (!property_key.is_null()) { - elements.append(create_ast_node<ClassMethod>({ m_state.current_token.filename(), rule_start.position(), position() }, property_key.release_nonnull(), move(function), method_kind, is_static)); + elements.append(create_ast_node<ClassMethod>({ 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<ClassExpression> 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<ClassField>({ 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<ClassField>({ 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<ClassExpression> Parser::parse_class_expression(bool expect_class_ consume(TokenType::CurlyClose); if (constructor.is_null()) { - auto constructor_body = create_ast_node<BlockStatement>({ m_state.current_token.filename(), rule_start.position(), position() }); + auto constructor_body = create_ast_node<BlockStatement>({ 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<ClassExpression> Parser::parse_class_expression(bool expect_class_ // method on %Array.prototype% visibly. FlyString argument_name = "args"; auto super_call = create_ast_node<SuperCall>( - { m_state.current_token.filename(), rule_start.position(), position() }, + { m_source_code, rule_start.position(), position() }, SuperCall::IsPartOfSyntheticConstructor::Yes, - CallExpression::Argument { create_ast_node<Identifier>({ m_state.current_token.filename(), rule_start.position(), position() }, "args"), true }); + CallExpression::Argument { create_ast_node<Identifier>({ 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<ReturnStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, move(super_call))); + constructor_body->append(create_ast_node<ReturnStatement>({ m_source_code, rule_start.position(), position() }, move(super_call))); constructor = create_ast_node<FunctionExpression>( - { 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<FunctionExpression>( - { 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> {}, 0, FunctionKind::Normal, /* is_strict_mode */ true, /* might_need_arguments_object */ false, /* contains_direct_call_to_eval */ false); } @@ -1381,7 +1382,7 @@ NonnullRefPtr<ClassExpression> 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<ClassExpression>({ 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<ClassExpression>({ 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<ThisExpression>({ m_state.current_token.filename(), rule_start.position(), position() }) }; + return { create_ast_node<ThisExpression>({ 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<SuperExpression>({ m_state.current_token.filename(), rule_start.position(), position() }) }; + return { create_ast_node<SuperExpression>({ 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<NumericLiteral>({ m_state.current_token.filename(), rule_start.position(), position() }, consume_and_validate_numeric_literal().double_value()) }; + return { create_ast_node<NumericLiteral>({ m_source_code, rule_start.position(), position() }, consume_and_validate_numeric_literal().double_value()) }; case TokenType::BigIntLiteral: - return { create_ast_node<BigIntLiteral>({ m_state.current_token.filename(), rule_start.position(), position() }, consume().value()) }; + return { create_ast_node<BigIntLiteral>({ m_source_code, rule_start.position(), position() }, consume().value()) }; case TokenType::BoolLiteral: - return { create_ast_node<BooleanLiteral>({ m_state.current_token.filename(), rule_start.position(), position() }, consume().bool_value()) }; + return { create_ast_node<BooleanLiteral>({ 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<NullLiteral>({ m_state.current_token.filename(), rule_start.position(), position() }) }; + return { create_ast_node<NullLiteral>({ 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<PrivateIdentifier>({ m_state.current_token.filename(), rule_start.position(), position() }, consume().value()) }; + return { create_ast_node<PrivateIdentifier>({ 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<ErrorExpression>({ m_state.current_token.filename(), rule_start.position(), position() }) }; + return { create_ast_node<ErrorExpression>({ m_source_code, rule_start.position(), position() }) }; } NonnullRefPtr<RegExpLiteral> Parser::parse_regexp_literal() @@ -1569,7 +1570,7 @@ NonnullRefPtr<RegExpLiteral> Parser::parse_regexp_literal() if (parsed_regex.error != regex::Error::NoError) syntax_error(String::formatted("RegExp compile error: {}", Regex<ECMA262>(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<RegExpLiteral>(move(range), move(parsed_regex), move(parsed_pattern), move(parsed_flags), pattern.to_string(), move(flags)); } @@ -1594,7 +1595,7 @@ NonnullRefPtr<Expression> Parser::parse_unary_prefixed_expression() check_identifier_name_for_assignment_validity(name); } - return create_ast_node<UpdateExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, UpdateOp::Increment, move(rhs), true); + return create_ast_node<UpdateExpression>({ m_source_code, rule_start.position(), position() }, UpdateOp::Increment, move(rhs), true); } case TokenType::MinusMinus: { consume(); @@ -1611,29 +1612,29 @@ NonnullRefPtr<Expression> Parser::parse_unary_prefixed_expression() check_identifier_name_for_assignment_validity(name); } - return create_ast_node<UpdateExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, UpdateOp::Decrement, move(rhs), true); + return create_ast_node<UpdateExpression>({ m_source_code, rule_start.position(), position() }, UpdateOp::Decrement, move(rhs), true); } case TokenType::ExclamationMark: consume(); - return create_ast_node<UnaryExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, UnaryOp::Not, parse_expression(precedence, associativity)); + return create_ast_node<UnaryExpression>({ m_source_code, rule_start.position(), position() }, UnaryOp::Not, parse_expression(precedence, associativity)); case TokenType::Tilde: consume(); - return create_ast_node<UnaryExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, UnaryOp::BitwiseNot, parse_expression(precedence, associativity)); + return create_ast_node<UnaryExpression>({ m_source_code, rule_start.position(), position() }, UnaryOp::BitwiseNot, parse_expression(precedence, associativity)); case TokenType::Plus: consume(); - return create_ast_node<UnaryExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, UnaryOp::Plus, parse_expression(precedence, associativity)); + return create_ast_node<UnaryExpression>({ m_source_code, rule_start.position(), position() }, UnaryOp::Plus, parse_expression(precedence, associativity)); case TokenType::Minus: consume(); - return create_ast_node<UnaryExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, UnaryOp::Minus, parse_expression(precedence, associativity)); + return create_ast_node<UnaryExpression>({ m_source_code, rule_start.position(), position() }, UnaryOp::Minus, parse_expression(precedence, associativity)); case TokenType::Typeof: consume(); - return create_ast_node<UnaryExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, UnaryOp::Typeof, parse_expression(precedence, associativity)); + return create_ast_node<UnaryExpression>({ 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<UnaryExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, UnaryOp::Void, parse_expression(precedence, associativity)); + return create_ast_node<UnaryExpression>({ 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<Expression> Parser::parse_unary_prefixed_expression() if (member_expression.ends_in_private_name()) syntax_error("Private fields cannot be deleted"); } - return create_ast_node<UnaryExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, UnaryOp::Delete, move(rhs)); + return create_ast_node<UnaryExpression>({ m_source_code, rule_start.position(), position() }, UnaryOp::Delete, move(rhs)); } default: expected("primary expression"); consume(); - return create_ast_node<ErrorExpression>({ m_state.current_token.filename(), rule_start.position(), position() }); + return create_ast_node<ErrorExpression>({ m_source_code, rule_start.position(), position() }); } } @@ -1661,9 +1662,9 @@ NonnullRefPtr<Expression> Parser::parse_property_key() if (match(TokenType::StringLiteral)) { return parse_string_literal(consume()); } else if (match(TokenType::NumericLiteral)) { - return create_ast_node<NumericLiteral>({ m_state.current_token.filename(), rule_start.position(), position() }, consume().double_value()); + return create_ast_node<NumericLiteral>({ m_source_code, rule_start.position(), position() }, consume().double_value()); } else if (match(TokenType::BigIntLiteral)) { - return create_ast_node<BigIntLiteral>({ m_state.current_token.filename(), rule_start.position(), position() }, consume().value()); + return create_ast_node<BigIntLiteral>({ 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<Expression> Parser::parse_property_key() } else { if (!match_identifier_name()) expected("IdentifierName"); - return create_ast_node<StringLiteral>({ m_state.current_token.filename(), rule_start.position(), position() }, consume().value()); + return create_ast_node<StringLiteral>({ m_source_code, rule_start.position(), position() }, consume().value()); } } @@ -1704,7 +1705,7 @@ NonnullRefPtr<ObjectExpression> Parser::parse_object_expression() if (match(TokenType::TripleDot)) { consume(); property_key = parse_expression(4); - properties.append(create_ast_node<ObjectProperty>({ m_state.current_token.filename(), rule_start.position(), position() }, *property_key, nullptr, ObjectProperty::Type::Spread, false)); + properties.append(create_ast_node<ObjectProperty>({ 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<ObjectExpression> Parser::parse_object_expression() property_type = ObjectProperty::Type::Setter; property_key = parse_property_key(); } else { - property_key = create_ast_node<StringLiteral>({ m_state.current_token.filename(), rule_start.position(), position() }, identifier.value()); - property_value = create_ast_node<Identifier>({ m_state.current_token.filename(), rule_start.position(), position() }, identifier.flystring_value()); + property_key = create_ast_node<StringLiteral>({ m_source_code, rule_start.position(), position() }, identifier.value()); + property_value = create_ast_node<Identifier>({ m_source_code, rule_start.position(), position() }, identifier.flystring_value()); } } else { property_key = parse_property_key(); @@ -1777,7 +1778,7 @@ NonnullRefPtr<ObjectExpression> Parser::parse_object_expression() if (function_kind == FunctionKind::Async || function_kind == FunctionKind::AsyncGenerator) parse_options |= FunctionNodeParseOptions::IsAsyncFunction; auto function = parse_function_node<FunctionExpression>(parse_options, function_start); - properties.append(create_ast_node<ObjectProperty>({ m_state.current_token.filename(), rule_start.position(), position() }, *property_key, function, property_type, true)); + properties.append(create_ast_node<ObjectProperty>({ 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<ObjectExpression> Parser::parse_object_expression() } if (is_proto && property_type == ObjectProperty::Type::KeyValue) property_type = ObjectProperty::Type::ProtoSetter; - properties.append(create_ast_node<ObjectProperty>({ m_state.current_token.filename(), rule_start.position(), position() }, *property_key, parse_expression(2), property_type, false)); + properties.append(create_ast_node<ObjectProperty>({ 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<StringLiteral>(*property_key)) { auto& string_literal = static_cast<StringLiteral const&>(*property_key); @@ -1800,7 +1801,7 @@ NonnullRefPtr<ObjectExpression> Parser::parse_object_expression() syntax_error(String::formatted("'{}' is a reserved keyword", string_literal.value())); } - properties.append(create_ast_node<ObjectProperty>({ m_state.current_token.filename(), rule_start.position(), position() }, *property_key, *property_value, property_type, false)); + properties.append(create_ast_node<ObjectProperty>({ 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<ObjectExpression> Parser::parse_object_expression() consume(TokenType::CurlyClose); return create_ast_node<ObjectExpression>( - { 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<ArrayExpression> Parser::parse_array_expression() if (match(TokenType::TripleDot)) { consume(TokenType::TripleDot); - expression = create_ast_node<SpreadExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, parse_expression(2)); + expression = create_ast_node<SpreadExpression>({ m_source_code, rule_start.position(), position() }, parse_expression(2)); } else if (match_expression()) { expression = parse_expression(2); } @@ -1842,7 +1843,7 @@ NonnullRefPtr<ArrayExpression> Parser::parse_array_expression() } consume(TokenType::BracketClose); - return create_ast_node<ArrayExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, move(elements)); + return create_ast_node<ArrayExpression>({ m_source_code, rule_start.position(), position() }, move(elements)); } NonnullRefPtr<StringLiteral> Parser::parse_string_literal(Token const& token, StringLiteralType string_literal_type, bool* contains_invalid_escape) @@ -1881,7 +1882,7 @@ NonnullRefPtr<StringLiteral> 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<StringLiteral>({ m_state.current_token.filename(), rule_start.position(), position() }, string, is_use_strict_directive); + return create_ast_node<StringLiteral>({ m_source_code, rule_start.position(), position() }, string, is_use_strict_directive); } NonnullRefPtr<TemplateLiteral> Parser::parse_template_literal(bool is_tagged) @@ -1893,7 +1894,7 @@ NonnullRefPtr<TemplateLiteral> Parser::parse_template_literal(bool is_tagged) NonnullRefPtrVector<Expression> raw_strings; auto append_empty_string = [this, &rule_start, &expressions, &raw_strings, is_tagged]() { - auto string_literal = create_ast_node<StringLiteral>({ m_state.current_token.filename(), rule_start.position(), position() }, ""); + auto string_literal = create_ast_node<StringLiteral>({ m_source_code, rule_start.position(), position() }, ""); expressions.append(string_literal); if (is_tagged) raw_strings.append(string_literal); @@ -1911,22 +1912,22 @@ NonnullRefPtr<TemplateLiteral> 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<NullLiteral>({ m_state.current_token.filename(), rule_start.position(), position() })); + expressions.append(create_ast_node<NullLiteral>({ m_source_code, rule_start.position(), position() })); else expressions.append(move(parsed_string_value)); if (is_tagged) - raw_strings.append(create_ast_node<StringLiteral>({ m_state.current_token.filename(), rule_start.position(), position() }, token.raw_template_value())); + raw_strings.append(create_ast_node<StringLiteral>({ 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<TemplateLiteral>({ m_state.current_token.filename(), rule_start.position(), position() }, expressions); + return create_ast_node<TemplateLiteral>({ 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<TemplateLiteral>({ m_state.current_token.filename(), rule_start.position(), position() }, expressions); + return create_ast_node<TemplateLiteral>({ m_source_code, rule_start.position(), position() }, expressions); } consume(TokenType::TemplateLiteralExprEnd); @@ -1945,8 +1946,8 @@ NonnullRefPtr<TemplateLiteral> Parser::parse_template_literal(bool is_tagged) } if (is_tagged) - return create_ast_node<TemplateLiteral>({ m_state.current_token.filename(), rule_start.position(), position() }, expressions, raw_strings); - return create_ast_node<TemplateLiteral>({ m_state.current_token.filename(), rule_start.position(), position() }, expressions); + return create_ast_node<TemplateLiteral>({ m_source_code, rule_start.position(), position() }, expressions, raw_strings); + return create_ast_node<TemplateLiteral>({ m_source_code, rule_start.position(), position() }, expressions); } NonnullRefPtr<Expression> Parser::parse_expression(int min_precedence, Associativity associativity, ForbiddenTokens forbidden) @@ -1979,7 +1980,7 @@ NonnullRefPtr<Expression> Parser::parse_expression(int min_precedence, Associati while (match(TokenType::TemplateLiteralStart)) { auto template_literal = parse_template_literal(true); - expression = create_ast_node<TaggedTemplateLiteral>({ m_state.current_token.filename(), rule_start.position(), position() }, move(expression), move(template_literal)); + expression = create_ast_node<TaggedTemplateLiteral>({ 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<Expression> Parser::parse_expression(int min_precedence, Associati forbidden = forbidden.merge(result.forbidden); while (match(TokenType::TemplateLiteralStart) && !is<UpdateExpression>(*expression)) { auto template_literal = parse_template_literal(true); - expression = create_ast_node<TaggedTemplateLiteral>({ m_state.current_token.filename(), rule_start.position(), position() }, move(expression), move(template_literal)); + expression = create_ast_node<TaggedTemplateLiteral>({ m_source_code, rule_start.position(), position() }, move(expression), move(template_literal)); } } } @@ -2031,7 +2032,7 @@ NonnullRefPtr<Expression> Parser::parse_expression(int min_precedence, Associati consume(); expressions.append(parse_expression(2)); } - expression = create_ast_node<SequenceExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, move(expressions)); + expression = create_ast_node<SequenceExpression>({ m_source_code, rule_start.position(), position() }, move(expressions)); } return expression; } @@ -2042,92 +2043,92 @@ Parser::ExpressionResult Parser::parse_secondary_expression(NonnullRefPtr<Expres switch (m_state.current_token.type()) { case TokenType::Plus: consume(); - return create_ast_node<BinaryExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::Addition, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node<BinaryExpression>({ 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<BinaryExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::Subtraction, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node<BinaryExpression>({ 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<BinaryExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::Multiplication, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node<BinaryExpression>({ 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<BinaryExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::Division, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node<BinaryExpression>({ 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<BinaryExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::Modulo, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node<BinaryExpression>({ 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<BinaryExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::Exponentiation, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node<BinaryExpression>({ 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<BinaryExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::GreaterThan, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node<BinaryExpression>({ 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<BinaryExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::GreaterThanEquals, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node<BinaryExpression>({ 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<BinaryExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::LessThan, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node<BinaryExpression>({ 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<BinaryExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::LessThanEquals, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node<BinaryExpression>({ 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<BinaryExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::StrictlyEquals, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node<BinaryExpression>({ 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<BinaryExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::StrictlyInequals, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node<BinaryExpression>({ 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<BinaryExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::LooselyEquals, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node<BinaryExpression>({ 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<BinaryExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::LooselyInequals, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node<BinaryExpression>({ 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<BinaryExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::In, move(lhs), parse_expression(min_precedence, associativity)); + return create_ast_node<BinaryExpression>({ m_source_code, rule_start.position(), position() }, BinaryOp::In, move(lhs), parse_expression(min_precedence, associativity)); case TokenType::Instanceof: consume(); - return create_ast_node<BinaryExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::InstanceOf, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node<BinaryExpression>({ 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<BinaryExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::BitwiseAnd, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node<BinaryExpression>({ 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<BinaryExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::BitwiseOr, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node<BinaryExpression>({ 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<BinaryExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::BitwiseXor, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node<BinaryExpression>({ 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<BinaryExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::LeftShift, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node<BinaryExpression>({ 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<BinaryExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::RightShift, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node<BinaryExpression>({ 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<BinaryExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, BinaryOp::UnsignedRightShift, move(lhs), parse_expression(min_precedence, associativity, forbidden)); + return create_ast_node<BinaryExpression>({ 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<Expres else if (is<SuperExpression>(*lhs)) syntax_error(String::formatted("Cannot access private field or method '{}' on super", m_state.current_token.value())); - return create_ast_node<MemberExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, move(lhs), create_ast_node<PrivateIdentifier>({ m_state.current_token.filename(), rule_start.position(), position() }, consume().value())); + return create_ast_node<MemberExpression>({ m_source_code, rule_start.position(), position() }, move(lhs), create_ast_node<PrivateIdentifier>({ m_source_code, rule_start.position(), position() }, consume().value())); } else if (!match_identifier_name()) { expected("IdentifierName"); } - return create_ast_node<MemberExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, move(lhs), create_ast_node<Identifier>({ m_state.current_token.filename(), rule_start.position(), position() }, consume().flystring_value())); + return create_ast_node<MemberExpression>({ m_source_code, rule_start.position(), position() }, move(lhs), create_ast_node<Identifier>({ m_source_code, rule_start.position(), position() }, consume().flystring_value())); case TokenType::BracketOpen: { consume(TokenType::BracketOpen); - auto expression = create_ast_node<MemberExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, move(lhs), parse_expression(0), true); + auto expression = create_ast_node<MemberExpression>({ 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<Expres } consume(); - return create_ast_node<UpdateExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, UpdateOp::Increment, move(lhs)); + return create_ast_node<UpdateExpression>({ 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<Expres check_identifier_name_for_assignment_validity(name); } consume(); - return create_ast_node<UpdateExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, UpdateOp::Decrement, move(lhs)); + return create_ast_node<UpdateExpression>({ m_source_code, rule_start.position(), position() }, UpdateOp::Decrement, move(lhs)); case TokenType::DoubleAmpersand: { consume(); - auto expression = create_ast_node<LogicalExpression>({ 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<LogicalExpression>({ 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<LogicalExpression>({ 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<LogicalExpression>({ 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<LogicalExpression>({ 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<LogicalExpression>({ 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<Expres default: expected("secondary expression"); consume(); - return create_ast_node<ErrorExpression>({ m_state.current_token.filename(), rule_start.position(), position() }); + return create_ast_node<ErrorExpression>({ m_source_code, rule_start.position(), position() }); } } @@ -2296,7 +2297,7 @@ NonnullRefPtr<AssignmentExpression> Parser::parse_assignment_expression(Assignme if (binding_pattern) { auto rhs = parse_expression(min_precedence, associativity); return create_ast_node<AssignmentExpression>( - { 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<AssignmentExpression> Parser::parse_assignment_expression(Assignme syntax_error("Cannot assign to function call"); } auto rhs = parse_expression(min_precedence, associativity, forbidden); - return create_ast_node<AssignmentExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, assignment_op, move(lhs), move(rhs)); + return create_ast_node<AssignmentExpression>({ m_source_code, rule_start.position(), position() }, assignment_op, move(lhs), move(rhs)); } NonnullRefPtr<Identifier> Parser::parse_identifier() @@ -2322,7 +2323,7 @@ NonnullRefPtr<Identifier> 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<Identifier>( - { m_state.current_token.filename(), identifier_start, position() }, + { m_source_code, identifier_start, position() }, token.flystring_value()); } @@ -2356,9 +2357,9 @@ NonnullRefPtr<Expression> Parser::parse_call_expression(NonnullRefPtr<Expression auto arguments = parse_arguments(); if (is<SuperExpression>(*lhs)) - return create_ast_node<SuperCall>({ m_state.current_token.filename(), rule_start.position(), position() }, move(arguments)); + return create_ast_node<SuperCall>({ m_source_code, rule_start.position(), position() }, move(arguments)); - return create_ast_node<CallExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, move(lhs), move(arguments)); + return create_ast_node<CallExpression>({ m_source_code, rule_start.position(), position() }, move(lhs), move(arguments)); } NonnullRefPtr<NewExpression> Parser::parse_new_expression() @@ -2388,7 +2389,7 @@ NonnullRefPtr<NewExpression> Parser::parse_new_expression() consume(TokenType::ParenClose); } - return create_ast_node<NewExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, move(callee), move(arguments)); + return create_ast_node<NewExpression>({ m_source_code, rule_start.position(), position() }, move(callee), move(arguments)); } NonnullRefPtr<YieldExpression> Parser::parse_yield_expression() @@ -2412,7 +2413,7 @@ NonnullRefPtr<YieldExpression> Parser::parse_yield_expression() argument = parse_expression(2); } - return create_ast_node<YieldExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, move(argument), yield_from); + return create_ast_node<YieldExpression>({ m_source_code, rule_start.position(), position() }, move(argument), yield_from); } NonnullRefPtr<AwaitExpression> Parser::parse_await_expression() @@ -2430,7 +2431,7 @@ NonnullRefPtr<AwaitExpression> Parser::parse_await_expression() m_state.current_scope_pusher->set_contains_await_expression(); - return create_ast_node<AwaitExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, move(argument)); + return create_ast_node<AwaitExpression>({ m_source_code, rule_start.position(), position() }, move(argument)); } NonnullRefPtr<ReturnStatement> Parser::parse_return_statement() @@ -2443,16 +2444,16 @@ NonnullRefPtr<ReturnStatement> 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<ReturnStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, nullptr); + return create_ast_node<ReturnStatement>({ m_source_code, rule_start.position(), position() }, nullptr); if (match_expression()) { auto expression = parse_expression(0); consume_or_insert_semicolon(); - return create_ast_node<ReturnStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, move(expression)); + return create_ast_node<ReturnStatement>({ m_source_code, rule_start.position(), position() }, move(expression)); } consume_or_insert_semicolon(); - return create_ast_node<ReturnStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, nullptr); + return create_ast_node<ReturnStatement>({ 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<FunctionBody> Parser::parse_function_body(Vector<FunctionDeclaration::Parameter> const& parameters, FunctionKind function_kind, bool& contains_direct_call_to_eval) { auto rule_start = push_start(); - auto function_body = create_ast_node<FunctionBody>({ m_state.current_token.filename(), rule_start.position(), position() }); + auto function_body = create_ast_node<FunctionBody>({ 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<FunctionBody> Parser::parse_function_body(Vector<FunctionDeclarati NonnullRefPtr<BlockStatement> Parser::parse_block_statement() { auto rule_start = push_start(); - auto block = create_ast_node<BlockStatement>({ m_state.current_token.filename(), rule_start.position(), position() }); + auto block = create_ast_node<BlockStatement>({ 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<FunctionNodeType> 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<FunctionNodeType>( - { 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<BindingPattern> Parser::parse_binding_pattern(Parser::AllowDuplicates all auto string_literal = parse_string_literal(token); name = create_ast_node<Identifier>( - { 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<Identifier>( - { 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<Identifier>( - { 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<BindingPattern> Parser::parse_binding_pattern(Parser::AllowDuplicates all alias = binding_pattern.release_nonnull(); } else if (match_identifier_name()) { alias = create_ast_node<Identifier>( - { 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<BindingPattern> 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<Identifier>( - { 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<VariableDeclaration> Parser::parse_variable_declaration(bool for_l auto identifier_start = push_start(); auto name = consume_identifier().flystring_value(); target = create_ast_node<Identifier>( - { 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<VariableDeclaration> Parser::parse_variable_declaration(bool for_l syntax_error("Identifier must not be a reserved word in strict mode ('yield')"); target = create_ast_node<Identifier>( - { 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<Identifier>( - { m_state.current_token.filename(), rule_start.position(), position() }, + { m_source_code, rule_start.position(), position() }, consume().flystring_value()); } @@ -3028,7 +3029,7 @@ NonnullRefPtr<VariableDeclaration> Parser::parse_variable_declaration(bool for_l } declarations.append(create_ast_node<VariableDeclarator>( - { m_state.current_token.filename(), rule_start.position(), position() }, + { m_source_code, rule_start.position(), position() }, move(target).downcast<NonnullRefPtr<Identifier>, NonnullRefPtr<BindingPattern>>(), move(init))); @@ -3041,7 +3042,7 @@ NonnullRefPtr<VariableDeclaration> Parser::parse_variable_declaration(bool for_l if (!for_loop_variable_declaration) consume_or_insert_semicolon(); - auto declaration = create_ast_node<VariableDeclaration>({ m_state.current_token.filename(), rule_start.position(), position() }, declaration_kind, move(declarations)); + auto declaration = create_ast_node<VariableDeclaration>({ m_source_code, rule_start.position(), position() }, declaration_kind, move(declarations)); return declaration; } @@ -3053,12 +3054,12 @@ NonnullRefPtr<ThrowStatement> 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<ThrowStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, create_ast_node<ErrorExpression>({ m_state.current_token.filename(), rule_start.position(), position() })); + return create_ast_node<ThrowStatement>({ m_source_code, rule_start.position(), position() }, create_ast_node<ErrorExpression>({ m_source_code, rule_start.position(), position() })); } auto expression = parse_expression(0); consume_or_insert_semicolon(); - return create_ast_node<ThrowStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, move(expression)); + return create_ast_node<ThrowStatement>({ m_source_code, rule_start.position(), position() }, move(expression)); } NonnullRefPtr<BreakStatement> Parser::parse_break_statement() @@ -3082,7 +3083,7 @@ NonnullRefPtr<BreakStatement> 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<BreakStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, target_label); + return create_ast_node<BreakStatement>({ m_source_code, rule_start.position(), position() }, target_label); } NonnullRefPtr<ContinueStatement> Parser::parse_continue_statement() @@ -3095,7 +3096,7 @@ NonnullRefPtr<ContinueStatement> Parser::parse_continue_statement() FlyString target_label; if (match(TokenType::Semicolon)) { consume(); - return create_ast_node<ContinueStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, target_label); + return create_ast_node<ContinueStatement>({ 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<ContinueStatement> Parser::parse_continue_statement() label->value = label_position; } consume_or_insert_semicolon(); - return create_ast_node<ContinueStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, target_label); + return create_ast_node<ContinueStatement>({ m_source_code, rule_start.position(), position() }, target_label); } NonnullRefPtr<ConditionalExpression> Parser::parse_conditional_expression(NonnullRefPtr<Expression> test, ForbiddenTokens forbidden) @@ -3118,7 +3119,7 @@ NonnullRefPtr<ConditionalExpression> 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<ConditionalExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, move(test), move(consequent), move(alternate)); + return create_ast_node<ConditionalExpression>({ m_source_code, rule_start.position(), position() }, move(test), move(consequent), move(alternate)); } NonnullRefPtr<OptionalChain> Parser::parse_optional_chain(NonnullRefPtr<Expression> base) @@ -3144,7 +3145,7 @@ NonnullRefPtr<OptionalChain> Parser::parse_optional_chain(NonnullRefPtr<Expressi auto start = position(); auto private_identifier = consume(); chain.append(OptionalChain::PrivateMemberReference { - create_ast_node<PrivateIdentifier>({ m_state.current_token.filename(), start, position() }, private_identifier.value()), + create_ast_node<PrivateIdentifier>({ m_source_code, start, position() }, private_identifier.value()), OptionalChain::Mode::Optional }); break; } @@ -3161,7 +3162,7 @@ NonnullRefPtr<OptionalChain> Parser::parse_optional_chain(NonnullRefPtr<Expressi auto start = position(); auto identifier = consume(); chain.append(OptionalChain::MemberReference { - create_ast_node<Identifier>({ m_state.current_token.filename(), start, position() }, identifier.flystring_value()), + create_ast_node<Identifier>({ m_source_code, start, position() }, identifier.flystring_value()), OptionalChain::Mode::Optional, }); } else { @@ -3180,14 +3181,14 @@ NonnullRefPtr<OptionalChain> Parser::parse_optional_chain(NonnullRefPtr<Expressi auto start = position(); auto private_identifier = consume(); chain.append(OptionalChain::PrivateMemberReference { - create_ast_node<PrivateIdentifier>({ m_state.current_token.filename(), start, position() }, private_identifier.value()), + create_ast_node<PrivateIdentifier>({ 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<Identifier>({ m_state.current_token.filename(), start, position() }, identifier.flystring_value()), + create_ast_node<Identifier>({ m_source_code, start, position() }, identifier.flystring_value()), OptionalChain::Mode::NotOptional, }); } else { @@ -3211,7 +3212,7 @@ NonnullRefPtr<OptionalChain> Parser::parse_optional_chain(NonnullRefPtr<Expressi } while (!done()); return create_ast_node<OptionalChain>( - { 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<TryStatement> Parser::parse_try_statement() if (!handler && !finalizer) syntax_error("try statement must have a 'catch' or 'finally' clause"); - return create_ast_node<TryStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, move(block), move(handler), move(finalizer)); + return create_ast_node<TryStatement>({ m_source_code, rule_start.position(), position() }, move(block), move(handler), move(finalizer)); } NonnullRefPtr<DoWhileStatement> Parser::parse_do_while_statement() @@ -3261,7 +3262,7 @@ NonnullRefPtr<DoWhileStatement> Parser::parse_do_while_statement() if (match(TokenType::Semicolon)) consume(); - return create_ast_node<DoWhileStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, move(test), move(body)); + return create_ast_node<DoWhileStatement>({ m_source_code, rule_start.position(), position() }, move(test), move(body)); } NonnullRefPtr<WhileStatement> Parser::parse_while_statement() @@ -3278,7 +3279,7 @@ NonnullRefPtr<WhileStatement> Parser::parse_while_statement() TemporaryChange continue_change(m_state.in_continue_context, true); auto body = parse_statement(); - return create_ast_node<WhileStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, move(test), move(body)); + return create_ast_node<WhileStatement>({ m_source_code, rule_start.position(), position() }, move(test), move(body)); } NonnullRefPtr<SwitchStatement> Parser::parse_switch_statement() @@ -3294,7 +3295,7 @@ NonnullRefPtr<SwitchStatement> Parser::parse_switch_statement() NonnullRefPtrVector<SwitchCase> cases; - auto switch_statement = create_ast_node<SwitchStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, move(determinant)); + auto switch_statement = create_ast_node<SwitchStatement>({ m_source_code, rule_start.position(), position() }, move(determinant)); ScopePusher switch_scope = ScopePusher::block_scope(*this, switch_statement); @@ -3324,7 +3325,7 @@ NonnullRefPtr<WithStatement> Parser::parse_with_statement() consume(TokenType::ParenClose); auto body = parse_statement(); - return create_ast_node<WithStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, move(object), move(body)); + return create_ast_node<WithStatement>({ m_source_code, rule_start.position(), position() }, move(object), move(body)); } NonnullRefPtr<SwitchCase> Parser::parse_switch_case() @@ -3340,7 +3341,7 @@ NonnullRefPtr<SwitchCase> Parser::parse_switch_case() NonnullRefPtrVector<Statement> consequent; TemporaryChange break_change(m_state.in_break_context, true); - auto switch_case = create_ast_node<SwitchCase>({ m_state.current_token.filename(), rule_start.position(), position() }, move(test)); + auto switch_case = create_ast_node<SwitchCase>({ m_source_code, rule_start.position(), position() }, move(test)); parse_statement_list(switch_case); return switch_case; @@ -3395,13 +3396,13 @@ NonnullRefPtr<CatchClause> Parser::parse_catch_clause() if (pattern_parameter) { return create_ast_node<CatchClause>( - { 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<CatchClause>( - { 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<IfStatement> 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<BlockStatement>({ m_state.current_token.filename(), rule_start.position(), position() }); + auto block = create_ast_node<BlockStatement>({ 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<IfStatement> Parser::parse_if_statement() else alternate = parse_statement(); } - return create_ast_node<IfStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, move(predicate), move(*consequent), move(alternate)); + return create_ast_node<IfStatement>({ m_source_code, rule_start.position(), position() }, move(predicate), move(*consequent), move(alternate)); } NonnullRefPtr<Statement> Parser::parse_for_statement() @@ -3549,7 +3550,7 @@ NonnullRefPtr<Statement> Parser::parse_for_statement() ScopePusher for_loop_scope = ScopePusher::for_loop_scope(*this, init); auto body = parse_statement(); - return create_ast_node<ForStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, move(init), move(test), move(update), move(body)); + return create_ast_node<ForStatement>({ m_source_code, rule_start.position(), position() }, move(init), move(test), move(update), move(body)); } NonnullRefPtr<Statement> Parser::parse_for_in_of_statement(NonnullRefPtr<ASTNode> lhs, IsForAwaitLoop is_for_await_loop) @@ -3608,10 +3609,10 @@ NonnullRefPtr<Statement> Parser::parse_for_in_of_statement(NonnullRefPtr<ASTNode ScopePusher for_loop_scope = ScopePusher::for_loop_scope(*this, lhs); auto body = parse_statement(); if (is_in) - return create_ast_node<ForInStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, move(for_declaration), move(rhs), move(body)); + return create_ast_node<ForInStatement>({ 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<ForAwaitOfStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, move(for_declaration), move(rhs), move(body)); - return create_ast_node<ForOfStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, move(for_declaration), move(rhs), move(body)); + return create_ast_node<ForAwaitOfStatement>({ m_source_code, rule_start.position(), position() }, move(for_declaration), move(rhs), move(body)); + return create_ast_node<ForOfStatement>({ m_source_code, rule_start.position(), position() }, move(for_declaration), move(rhs), move(body)); } NonnullRefPtr<DebuggerStatement> Parser::parse_debugger_statement() @@ -3619,7 +3620,7 @@ NonnullRefPtr<DebuggerStatement> Parser::parse_debugger_statement() auto rule_start = push_start(); consume(TokenType::Debugger); consume_or_insert_semicolon(); - return create_ast_node<DebuggerStatement>({ m_state.current_token.filename(), rule_start.position(), position() }); + return create_ast_node<DebuggerStatement>({ m_source_code, rule_start.position(), position() }); } bool Parser::match(TokenType type) const @@ -4162,7 +4163,7 @@ NonnullRefPtr<ImportStatement> Parser::parse_import_statement(Program& program) if (match(TokenType::StringLiteral)) { // import ModuleSpecifier ; auto module_request = parse_module_request(); - return create_ast_node<ImportStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, move(module_request)); + return create_ast_node<ImportStatement>({ m_source_code, rule_start.position(), position() }, move(module_request)); } auto match_imported_binding = [&] { @@ -4302,7 +4303,7 @@ NonnullRefPtr<ImportStatement> Parser::parse_import_statement(Program& program) entries.append(move(entry.entry)); } - return create_ast_node<ImportStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, move(module_request), move(entries)); + return create_ast_node<ImportStatement>({ m_source_code, rule_start.position(), position() }, move(module_request), move(entries)); } NonnullRefPtr<ExportStatement> Parser::parse_export_statement(Program& program) @@ -4603,7 +4604,7 @@ NonnullRefPtr<ExportStatement> Parser::parse_export_statement(Program& program) entries.append(move(entry.entry)); } - return create_ast_node<ExportStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, move(expression), move(entries), is_default, move(from_specifier)); + return create_ast_node<ExportStatement>({ m_source_code, rule_start.position(), position() }, move(expression), move(entries), is_default, move(from_specifier)); } Parser::ForbiddenTokens::ForbiddenTokens(std::initializer_list<TokenType> 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<SourceCode> m_source_code; Vector<Position> 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<void> 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 == "<unknown>"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() ? "<unknown>"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 <kling@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <AK/Utf8View.h> +#include <LibJS/SourceCode.h> +#include <LibJS/SourceRange.h> +#include <LibJS/Token.h> + +namespace JS { + +NonnullRefPtr<SourceCode> 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 <kling@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <AK/String.h> +#include <LibJS/Forward.h> + +namespace JS { + +class SourceCode : public RefCounted<SourceCode> { +public: + static NonnullRefPtr<SourceCode> 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 <AK/StringView.h> #include <AK/Types.h> +#include <LibJS/SourceCode.h> 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<SourceCode> 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); |