diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp index da5ff7b1b6..7addde23ab 100644 --- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -29,6 +29,7 @@ Bytecode::CodeGenerationErrorOr ASTNode::generate_bytecode(Bytecode::Gener Bytecode::CodeGenerationErrorOr ScopeNode::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); bool did_create_lexical_environment = false; if (is(*this)) { @@ -61,11 +62,13 @@ Bytecode::CodeGenerationErrorOr EmptyStatement::generate_bytecode(Bytecode Bytecode::CodeGenerationErrorOr ExpressionStatement::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); return m_expression->generate_bytecode(generator); } Bytecode::CodeGenerationErrorOr BinaryExpression::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); if (m_op == BinaryOp::In && is(*m_lhs)) { auto const& private_identifier = static_cast(*m_lhs).string(); TRY(m_rhs->generate_bytecode(generator)); @@ -154,6 +157,7 @@ Bytecode::CodeGenerationErrorOr BinaryExpression::generate_bytecode(Byteco Bytecode::CodeGenerationErrorOr LogicalExpression::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); TRY(m_lhs->generate_bytecode(generator)); // lhs @@ -198,6 +202,7 @@ Bytecode::CodeGenerationErrorOr LogicalExpression::generate_bytecode(Bytec Bytecode::CodeGenerationErrorOr UnaryExpression::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); if (m_op == UnaryOp::Delete) return generator.emit_delete_reference(m_lhs); @@ -245,24 +250,28 @@ Bytecode::CodeGenerationErrorOr UnaryExpression::generate_bytecode(Bytecod Bytecode::CodeGenerationErrorOr NumericLiteral::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); generator.emit(m_value); return {}; } Bytecode::CodeGenerationErrorOr BooleanLiteral::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); generator.emit(Value(m_value)); return {}; } Bytecode::CodeGenerationErrorOr NullLiteral::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); generator.emit(js_null()); return {}; } Bytecode::CodeGenerationErrorOr BigIntLiteral::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); // 1. Return the NumericValue of NumericLiteral as defined in 12.8.3. auto integer = [&] { if (m_value[0] == '0' && m_value.length() >= 3) @@ -281,12 +290,14 @@ Bytecode::CodeGenerationErrorOr BigIntLiteral::generate_bytecode(Bytecode: Bytecode::CodeGenerationErrorOr StringLiteral::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); generator.emit(generator.intern_string(m_value)); return {}; } Bytecode::CodeGenerationErrorOr RegExpLiteral::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); auto source_index = generator.intern_string(m_pattern); auto flags_index = generator.intern_string(m_flags); auto regex_index = generator.intern_regex(Bytecode::ParsedRegex { @@ -300,6 +311,7 @@ Bytecode::CodeGenerationErrorOr RegExpLiteral::generate_bytecode(Bytecode: Bytecode::CodeGenerationErrorOr Identifier::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); if (is_global()) { generator.emit(generator.intern_identifier(m_string), generator.next_global_variable_cache()); } else if (is_local()) { @@ -354,6 +366,7 @@ static Bytecode::CodeGenerationErrorOr arguments_to_array_for_call(Bytecod Bytecode::CodeGenerationErrorOr SuperCall::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); if (m_is_synthetic == IsPartOfSyntheticConstructor::Yes) { // NOTE: This is the case where we have a fake constructor(...args) { super(...args); } which // shouldn't call @@iterator of %Array.prototype%. @@ -375,6 +388,7 @@ static Bytecode::CodeGenerationErrorOr generate_binding_pattern_bytecode(B Bytecode::CodeGenerationErrorOr AssignmentExpression::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); if (m_op == AssignmentOp::Assignment) { // AssignmentExpression : LeftHandSideExpression = AssignmentExpression return m_lhs.visit( @@ -621,6 +635,7 @@ Bytecode::CodeGenerationErrorOr AssignmentExpression::generate_bytecode(By // LabelledStatement : LabelIdentifier : LabelledItem Bytecode::CodeGenerationErrorOr LabelledStatement::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); // Return ? LabelledEvaluation of this LabelledStatement with argument « ». return generate_labelled_evaluation(generator, {}); } @@ -629,6 +644,7 @@ Bytecode::CodeGenerationErrorOr LabelledStatement::generate_bytecode(Bytec // LabelledStatement : LabelIdentifier : LabelledItem Bytecode::CodeGenerationErrorOr LabelledStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector const& label_set) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); // Convert the m_labelled_item NNRP to a reference early so we don't have to do it every single time we want to use it. auto const& labelled_item = *m_labelled_item; @@ -687,11 +703,13 @@ Bytecode::CodeGenerationErrorOr IterationStatement::generate_labelled_eval Bytecode::CodeGenerationErrorOr WhileStatement::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); return generate_labelled_evaluation(generator, {}); } Bytecode::CodeGenerationErrorOr WhileStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector const& label_set) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); // test // jump if_false (true) end (false) body // body @@ -742,11 +760,13 @@ Bytecode::CodeGenerationErrorOr WhileStatement::generate_labelled_evaluati Bytecode::CodeGenerationErrorOr DoWhileStatement::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); return generate_labelled_evaluation(generator, {}); } Bytecode::CodeGenerationErrorOr DoWhileStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector const& label_set) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); // jump always (true) body // test // jump if_false (true) end (false) body @@ -798,11 +818,13 @@ Bytecode::CodeGenerationErrorOr DoWhileStatement::generate_labelled_evalua Bytecode::CodeGenerationErrorOr ForStatement::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); return generate_labelled_evaluation(generator, {}); } Bytecode::CodeGenerationErrorOr ForStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector const& label_set) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); // init // jump always (true) test // test @@ -938,6 +960,7 @@ Bytecode::CodeGenerationErrorOr ForStatement::generate_labelled_evaluation Bytecode::CodeGenerationErrorOr ObjectExpression::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); generator.emit(); if (m_properties.is_empty()) return {}; @@ -999,6 +1022,7 @@ Bytecode::CodeGenerationErrorOr ObjectExpression::generate_bytecode(Byteco Bytecode::CodeGenerationErrorOr ArrayExpression::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); if (m_elements.is_empty()) { generator.emit(); return {}; @@ -1048,12 +1072,14 @@ Bytecode::CodeGenerationErrorOr ArrayExpression::generate_bytecode(Bytecod Bytecode::CodeGenerationErrorOr MemberExpression::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); return generator.emit_load_from_reference(*this); } Bytecode::CodeGenerationErrorOr FunctionDeclaration::generate_bytecode(Bytecode::Generator& generator) const { if (m_is_hoisted) { + Bytecode::Generator::SourceLocationScope scope(generator, *this); auto index = generator.intern_identifier(name()); generator.emit(index); generator.emit(index, Bytecode::Op::SetVariable::InitializationMode::Set, Bytecode::Op::EnvironmentMode::Var); @@ -1063,6 +1089,7 @@ Bytecode::CodeGenerationErrorOr FunctionDeclaration::generate_bytecode(Byt Bytecode::CodeGenerationErrorOr FunctionExpression::generate_bytecode_with_lhs_name(Bytecode::Generator& generator, Optional lhs_name) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); bool has_name = !name().is_empty(); Optional name_identifier; @@ -1085,6 +1112,7 @@ Bytecode::CodeGenerationErrorOr FunctionExpression::generate_bytecode_with Bytecode::CodeGenerationErrorOr FunctionExpression::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); return generate_bytecode_with_lhs_name(generator, {}); } @@ -1427,6 +1455,7 @@ static Bytecode::CodeGenerationErrorOr assign_accumulator_to_variable_decl Bytecode::CodeGenerationErrorOr VariableDeclaration::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); // The completion value of a VariableDeclaration is empty, but there might already be a non-empty // completion value in the accumulator. We need to save it and restore it after the declaration executed. auto saved_accumulator = generator.allocate_register(); @@ -1511,6 +1540,7 @@ static Bytecode::CodeGenerationErrorOr generate_optional_chain(Bytecode::G Bytecode::CodeGenerationErrorOr CallExpression::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); auto callee_reg = generator.allocate_register(); auto this_reg = generator.allocate_register(); generator.emit(js_undefined()); @@ -1587,6 +1617,7 @@ static void generate_await(Bytecode::Generator& generator, Bytecode::Register re // https://tc39.es/ecma262/#sec-return-statement-runtime-semantics-evaluation Bytecode::CodeGenerationErrorOr ReturnStatement::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); if (m_argument) { // ReturnStatement : return Expression ; // 1. Let exprRef be ? Evaluation of Expression. @@ -1699,6 +1730,7 @@ static void generate_yield(Bytecode::Generator& generator, Bytecode::Label conti Bytecode::CodeGenerationErrorOr YieldExpression::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); VERIFY(generator.is_in_generator_function()); auto received_completion_register = generator.allocate_register(); @@ -2039,6 +2071,7 @@ Bytecode::CodeGenerationErrorOr YieldExpression::generate_bytecode(Bytecod Bytecode::CodeGenerationErrorOr IfStatement::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); // test // jump if_true (true) true (false) false // true @@ -2081,6 +2114,7 @@ Bytecode::CodeGenerationErrorOr IfStatement::generate_bytecode(Bytecode::G Bytecode::CodeGenerationErrorOr ContinueStatement::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); // FIXME: Handle finally blocks in a graceful manner // We need to execute the finally block, but tell it to resume // execution at the designated block @@ -2100,6 +2134,7 @@ Bytecode::CodeGenerationErrorOr DebuggerStatement::generate_bytecode(Bytec Bytecode::CodeGenerationErrorOr ConditionalExpression::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); // test // jump if_true (true) true (false) false // true @@ -2135,6 +2170,7 @@ Bytecode::CodeGenerationErrorOr ConditionalExpression::generate_bytecode(B Bytecode::CodeGenerationErrorOr SequenceExpression::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); for (auto& expression : m_expressions) TRY(expression->generate_bytecode(generator)); @@ -2143,6 +2179,7 @@ Bytecode::CodeGenerationErrorOr SequenceExpression::generate_bytecode(Byte Bytecode::CodeGenerationErrorOr TemplateLiteral::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); auto string_reg = generator.allocate_register(); for (size_t i = 0; i < m_expressions.size(); i++) { @@ -2160,6 +2197,7 @@ Bytecode::CodeGenerationErrorOr TemplateLiteral::generate_bytecode(Bytecod Bytecode::CodeGenerationErrorOr TaggedTemplateLiteral::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); TRY(m_tag->generate_bytecode(generator)); auto tag_reg = generator.allocate_register(); generator.emit(tag_reg); @@ -2252,6 +2290,7 @@ Bytecode::CodeGenerationErrorOr TaggedTemplateLiteral::generate_bytecode(B Bytecode::CodeGenerationErrorOr UpdateExpression::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); TRY(generator.emit_load_from_reference(*m_argument)); Optional previous_value_for_postfix_reg; @@ -2275,6 +2314,7 @@ Bytecode::CodeGenerationErrorOr UpdateExpression::generate_bytecode(Byteco Bytecode::CodeGenerationErrorOr ThrowStatement::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); TRY(m_argument->generate_bytecode(generator)); generator.perform_needed_unwinds(); generator.emit(); @@ -2283,6 +2323,7 @@ Bytecode::CodeGenerationErrorOr ThrowStatement::generate_bytecode(Bytecode Bytecode::CodeGenerationErrorOr BreakStatement::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); // FIXME: Handle finally blocks in a graceful manner // We need to execute the finally block, but tell it to resume // execution at the designated block @@ -2297,6 +2338,7 @@ Bytecode::CodeGenerationErrorOr BreakStatement::generate_bytecode(Bytecode Bytecode::CodeGenerationErrorOr TryStatement::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); auto& saved_block = generator.current_block(); Optional handler_target; @@ -2397,11 +2439,13 @@ Bytecode::CodeGenerationErrorOr TryStatement::generate_bytecode(Bytecode:: Bytecode::CodeGenerationErrorOr SwitchStatement::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); return generate_labelled_evaluation(generator, {}); } Bytecode::CodeGenerationErrorOr SwitchStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector const& label_set) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); auto discriminant_reg = generator.allocate_register(); TRY(m_discriminant->generate_bytecode(generator)); generator.emit(discriminant_reg); @@ -2484,6 +2528,7 @@ Bytecode::CodeGenerationErrorOr SuperExpression::generate_bytecode(Bytecod Bytecode::CodeGenerationErrorOr ClassDeclaration::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); auto accumulator_backup_reg = generator.allocate_register(); generator.emit(accumulator_backup_reg); @@ -2516,6 +2561,7 @@ Bytecode::CodeGenerationErrorOr ClassExpression::generate_bytecode_with_lh Bytecode::CodeGenerationErrorOr ClassExpression::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); return generate_bytecode_with_lhs_name(generator, {}); } @@ -2528,6 +2574,7 @@ Bytecode::CodeGenerationErrorOr SpreadExpression::generate_bytecode(Byteco Bytecode::CodeGenerationErrorOr ThisExpression::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); generator.emit(); return {}; } @@ -2564,6 +2611,7 @@ static void generate_await(Bytecode::Generator& generator, Bytecode::Register re Bytecode::CodeGenerationErrorOr AwaitExpression::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); TRY(m_argument->generate_bytecode(generator)); auto received_completion_register = generator.allocate_register(); @@ -2579,6 +2627,7 @@ Bytecode::CodeGenerationErrorOr AwaitExpression::generate_bytecode(Bytecod Bytecode::CodeGenerationErrorOr WithStatement::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); TRY(m_object->generate_bytecode(generator)); generator.emit(); @@ -2934,6 +2983,7 @@ static Bytecode::CodeGenerationErrorOr for_in_of_body_evaluation(Bytecode: Bytecode::CodeGenerationErrorOr ForInStatement::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); return generate_labelled_evaluation(generator, {}); } @@ -2952,6 +3002,7 @@ Bytecode::CodeGenerationErrorOr ForInStatement::generate_labelled_evaluati Bytecode::CodeGenerationErrorOr ForOfStatement::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); return generate_labelled_evaluation(generator, {}); } @@ -2969,6 +3020,7 @@ Bytecode::CodeGenerationErrorOr ForOfStatement::generate_labelled_evaluati Bytecode::CodeGenerationErrorOr ForAwaitOfStatement::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); return generate_labelled_evaluation(generator, {}); } @@ -2987,6 +3039,7 @@ Bytecode::CodeGenerationErrorOr ForAwaitOfStatement::generate_labelled_eva // 13.3.12.1 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-meta-properties-runtime-semantics-evaluation Bytecode::CodeGenerationErrorOr MetaProperty::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); // NewTarget : new . target if (m_type == MetaProperty::Type::NewTarget) { // 1. Return GetNewTarget(). @@ -3005,6 +3058,7 @@ Bytecode::CodeGenerationErrorOr MetaProperty::generate_bytecode(Bytecode:: Bytecode::CodeGenerationErrorOr ClassFieldInitializerStatement::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); TRY(generator.emit_named_evaluation_if_anonymous_function(*m_expression, generator.intern_identifier(m_class_field_identifier_name))); generator.perform_needed_unwinds(); generator.emit(); @@ -3084,6 +3138,7 @@ static Bytecode::CodeGenerationErrorOr generate_optional_chain(Bytecode::G Bytecode::CodeGenerationErrorOr OptionalChain::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); auto current_base_register = generator.allocate_register(); auto current_value_register = generator.allocate_register(); generator.emit(js_undefined()); @@ -3093,6 +3148,7 @@ Bytecode::CodeGenerationErrorOr OptionalChain::generate_bytecode(Bytecode: Bytecode::CodeGenerationErrorOr ImportCall::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); TRY(m_specifier->generate_bytecode(generator)); auto specifier_reg = generator.allocate_register(); generator.emit(specifier_reg); @@ -3111,6 +3167,7 @@ Bytecode::CodeGenerationErrorOr ImportCall::generate_bytecode(Bytecode::Ge Bytecode::CodeGenerationErrorOr ExportStatement::generate_bytecode(Bytecode::Generator& generator) const { + Bytecode::Generator::SourceLocationScope scope(generator, *this); if (!is_default_export()) { if (m_statement) { return m_statement->generate_bytecode(generator); diff --git a/Userland/Libraries/LibJS/Bytecode/Executable.h b/Userland/Libraries/LibJS/Bytecode/Executable.h index 103636d55e..aeda600d57 100644 --- a/Userland/Libraries/LibJS/Bytecode/Executable.h +++ b/Userland/Libraries/LibJS/Bytecode/Executable.h @@ -26,6 +26,11 @@ struct GlobalVariableCache : public PropertyLookupCache { u64 environment_serial_number { 0 }; }; +struct SourceRecord { + u32 source_start_offset {}; + u32 source_end_offset {}; +}; + struct Executable { DeprecatedFlyString name; Vector property_lookup_caches; @@ -34,6 +39,7 @@ struct Executable { NonnullOwnPtr string_table; NonnullOwnPtr identifier_table; NonnullOwnPtr regex_table; + NonnullRefPtr source_code; size_t number_of_registers { 0 }; bool is_strict_mode { false }; diff --git a/Userland/Libraries/LibJS/Bytecode/Generator.cpp b/Userland/Libraries/LibJS/Bytecode/Generator.cpp index bd3aca468e..f7cf70de16 100644 --- a/Userland/Libraries/LibJS/Bytecode/Generator.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Generator.cpp @@ -24,6 +24,7 @@ CodeGenerationErrorOr> Generator::generate(ASTNode con { Generator generator; generator.switch_to_basic_block(generator.make_block()); + SourceLocationScope scope(generator, node); generator.m_enclosing_function_kind = enclosing_function_kind; if (generator.is_in_generator_or_async_function()) { // Immediately yield with no value. @@ -69,6 +70,7 @@ CodeGenerationErrorOr> Generator::generate(ASTNode con .string_table = move(generator.m_string_table), .identifier_table = move(generator.m_identifier_table), .regex_table = move(generator.m_regex_table), + .source_code = node.source_code(), .number_of_registers = generator.m_next_register, .is_strict_mode = is_strict_mode, }); @@ -92,6 +94,18 @@ Register Generator::allocate_register() return Register { m_next_register++ }; } +Generator::SourceLocationScope::SourceLocationScope(Generator& generator, ASTNode const& node) + : m_generator(generator) + , m_previous_node(m_generator.m_current_ast_node) +{ + m_generator.m_current_ast_node = &node; +} + +Generator::SourceLocationScope::~SourceLocationScope() +{ + m_generator.m_current_ast_node = m_previous_node; +} + Label Generator::nearest_continuable_scope() const { return m_continuable_scopes.last().bytecode_target; diff --git a/Userland/Libraries/LibJS/Bytecode/Generator.h b/Userland/Libraries/LibJS/Bytecode/Generator.h index b759a8a2bb..af02f3cae1 100644 --- a/Userland/Libraries/LibJS/Bytecode/Generator.h +++ b/Userland/Libraries/LibJS/Bytecode/Generator.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -45,6 +46,16 @@ public: } } + class SourceLocationScope { + public: + SourceLocationScope(Generator&, ASTNode const& node); + ~SourceLocationScope(); + + private: + Generator& m_generator; + ASTNode const* m_previous_node { nullptr }; + }; + template OpType& emit(Args&&... args) { @@ -58,7 +69,9 @@ public: new (slot) OpType(forward(args)...); if constexpr (OpType::IsTerminator) m_current_basic_block->terminate({}, static_cast(slot)); - return *static_cast(slot); + auto* op = static_cast(slot); + op->set_source_record({ m_current_ast_node->start_offset(), m_current_ast_node->end_offset() }); + return *op; } template @@ -77,7 +90,9 @@ public: new (slot) OpType(forward(args)...); if constexpr (OpType::IsTerminator) m_current_basic_block->terminate({}, static_cast(slot)); - return *static_cast(slot); + auto* op = static_cast(slot); + op->set_source_record({ m_current_ast_node->start_offset(), m_current_ast_node->end_offset() }); + return *op; } CodeGenerationErrorOr emit_load_from_reference(JS::ASTNode const&); @@ -231,6 +246,7 @@ private: }; BasicBlock* m_current_basic_block { nullptr }; + ASTNode const* m_current_ast_node { nullptr }; Vector> m_root_basic_blocks; NonnullOwnPtr m_string_table; NonnullOwnPtr m_identifier_table; diff --git a/Userland/Libraries/LibJS/Bytecode/Instruction.cpp b/Userland/Libraries/LibJS/Bytecode/Instruction.cpp index c67fe61d68..37ad9f8939 100644 --- a/Userland/Libraries/LibJS/Bytecode/Instruction.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Instruction.cpp @@ -4,6 +4,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include @@ -25,4 +26,20 @@ void Instruction::destroy(Instruction& instruction) #undef __BYTECODE_OP } +UnrealizedSourceRange InstructionStreamIterator::source_range() const +{ + VERIFY(m_executable); + auto record = dereference().source_record(); + return { + .source_code = m_executable->source_code, + .start_offset = record.source_start_offset, + .end_offset = record.source_end_offset, + }; +} + +RefPtr InstructionStreamIterator::source_code() const +{ + return m_executable ? m_executable->source_code.ptr() : nullptr; +} + } diff --git a/Userland/Libraries/LibJS/Bytecode/Instruction.h b/Userland/Libraries/LibJS/Bytecode/Instruction.h index 7855e723b5..6f3fddfaa3 100644 --- a/Userland/Libraries/LibJS/Bytecode/Instruction.h +++ b/Userland/Libraries/LibJS/Bytecode/Instruction.h @@ -8,7 +8,9 @@ #include #include +#include #include +#include #define ENUMERATE_BYTECODE_OPS(O) \ O(Add) \ @@ -137,6 +139,10 @@ public: ThrowCompletionOr execute(Bytecode::Interpreter&) const; static void destroy(Instruction&); + // FIXME: Find a better way to organize this information + void set_source_record(SourceRecord rec) { m_source_record = rec; } + SourceRecord source_record() const { return m_source_record; } + protected: explicit Instruction(Type type) : m_type(type) @@ -144,13 +150,15 @@ protected: } private: + SourceRecord m_source_record {}; Type m_type {}; }; class InstructionStreamIterator { public: - explicit InstructionStreamIterator(ReadonlyBytes bytes) + InstructionStreamIterator(ReadonlyBytes bytes, Executable const* executable = nullptr) : m_bytes(bytes) + , m_executable(executable) { } @@ -170,11 +178,15 @@ public: m_offset += dereference().length(); } + UnrealizedSourceRange source_range() const; + RefPtr source_code() const; + private: Instruction const& dereference() const { return *reinterpret_cast(m_bytes.data() + offset()); } ReadonlyBytes m_bytes; size_t m_offset { 0 }; + Executable const* m_executable { nullptr }; }; } diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp index b8ce6fa6a6..33d3d378bc 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -173,6 +173,7 @@ Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Realm& realm, Execu TemporaryChange restore_saved_jump { m_scheduled_jump, static_cast(nullptr) }; TemporaryChange restore_saved_exception { m_saved_exception, {} }; + VERIFY(!vm().execution_context_stack().is_empty()); bool pushed_execution_context = false; ExecutionContext execution_context(vm().heap()); if (vm().execution_context_stack().is_empty() || !vm().running_execution_context().lexical_environment) { @@ -198,8 +199,9 @@ Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Realm& realm, Execu TemporaryChange restore_this_value { m_this_value, {} }; for (;;) { - Bytecode::InstructionStreamIterator pc(m_current_block->instruction_stream()); - TemporaryChange temp_change { m_pc, &pc }; + auto pc = InstructionStreamIterator { m_current_block->instruction_stream(), m_current_executable }; + TemporaryChange temp_change { m_pc, Optional(pc) }; + TemporaryChange context_change { vm().running_execution_context().instruction_stream_iterator, Optional(pc) }; // FIXME: This is getting kinda spaghetti-y bool will_jump = false; @@ -369,14 +371,10 @@ ThrowCompletionOr Interpreter::continue_pending_unwind(Label const& resume return {}; } -size_t Interpreter::pc() const -{ - return m_pc ? m_pc->offset() : 0; -} - DeprecatedString Interpreter::debug_position() const { - return DeprecatedString::formatted("{}:{:2}:{:4x}", m_current_executable->name, m_current_block->name(), pc()); + auto offset = m_pc.has_value() ? m_pc->offset() : 0; + return DeprecatedString::formatted("{}:{:2}:{:4x}", m_current_executable->name, m_current_block->name(), offset); } ThrowCompletionOr> compile(VM& vm, ASTNode const& node, FunctionKind kind, DeprecatedFlyString const& name) diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.h b/Userland/Libraries/LibJS/Bytecode/Interpreter.h index f1995716b4..8e863da4a9 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.h +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.h @@ -87,7 +87,7 @@ public: Executable& current_executable() { return *m_current_executable; } Executable const& current_executable() const { return *m_current_executable; } BasicBlock const& current_block() const { return *m_current_block; } - size_t pc() const; + auto& instruction_stream_iterator() const { return m_pc; } DeprecatedString debug_position() const; Optional& this_value() { return m_this_value; } @@ -121,7 +121,7 @@ private: Optional m_saved_exception; Executable* m_current_executable { nullptr }; BasicBlock const* m_current_block { nullptr }; - InstructionStreamIterator* m_pc { nullptr }; + Optional m_pc {}; }; extern bool g_dump_bytecode; diff --git a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp index e3867e11db..d500772c41 100644 --- a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp @@ -152,6 +152,7 @@ ThrowCompletionOr ECMAScriptFunctionObject::internal_call(Value this_argu // Non-standard callee_context.arguments.extend(move(arguments_list)); + callee_context.instruction_stream_iterator = vm.bytecode_interpreter().instruction_stream_iterator(); // 2. Let calleeContext be PrepareForOrdinaryCall(F, undefined). // NOTE: We throw if the end of the native stack is reached, so unlike in the spec this _does_ need an exception check. @@ -221,6 +222,7 @@ ThrowCompletionOr> ECMAScriptFunctionObject::internal_const // Non-standard callee_context.arguments.extend(move(arguments_list)); + callee_context.instruction_stream_iterator = vm.bytecode_interpreter().instruction_stream_iterator(); // 4. Let calleeContext be PrepareForOrdinaryCall(F, newTarget). // NOTE: We throw if the end of the native stack is reached, so unlike in the spec this _does_ need an exception check. diff --git a/Userland/Libraries/LibJS/Runtime/Error.cpp b/Userland/Libraries/LibJS/Runtime/Error.cpp index 92795ccc27..3755ffde90 100644 --- a/Userland/Libraries/LibJS/Runtime/Error.cpp +++ b/Userland/Libraries/LibJS/Runtime/Error.cpp @@ -83,9 +83,12 @@ void Error::populate_stack() if (function_name.is_empty()) function_name = ""sv; + UnrealizedSourceRange range = {}; + if (context->instruction_stream_iterator.has_value()) + range = context->instruction_stream_iterator->source_range(); TracebackFrame frame { .function_name = move(function_name), - .source_range_storage = context->source_range, + .source_range_storage = range, }; m_traceback.append(move(frame)); diff --git a/Userland/Libraries/LibJS/Runtime/ExecutionContext.cpp b/Userland/Libraries/LibJS/Runtime/ExecutionContext.cpp index 10f53e03f2..a9d6d6aa52 100644 --- a/Userland/Libraries/LibJS/Runtime/ExecutionContext.cpp +++ b/Userland/Libraries/LibJS/Runtime/ExecutionContext.cpp @@ -33,7 +33,7 @@ ExecutionContext ExecutionContext::copy() const copy.lexical_environment = lexical_environment; copy.variable_environment = variable_environment; copy.private_environment = private_environment; - copy.source_range = source_range; + copy.instruction_stream_iterator = instruction_stream_iterator; copy.function_name = function_name; copy.this_value = this_value; copy.is_strict_mode = is_strict_mode; diff --git a/Userland/Libraries/LibJS/Runtime/ExecutionContext.h b/Userland/Libraries/LibJS/Runtime/ExecutionContext.h index df8124816e..180e0d3e8a 100644 --- a/Userland/Libraries/LibJS/Runtime/ExecutionContext.h +++ b/Userland/Libraries/LibJS/Runtime/ExecutionContext.h @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -43,7 +44,7 @@ public: // Non-standard: This points at something that owns this ExecutionContext, in case it needs to be protected from GC. GCPtr context_owner; - UnrealizedSourceRange source_range; + Optional instruction_stream_iterator; DeprecatedFlyString function_name; Value this_value; MarkedVector arguments; diff --git a/Userland/Libraries/LibJS/Runtime/NativeFunction.cpp b/Userland/Libraries/LibJS/Runtime/NativeFunction.cpp index 54cd62ff8b..b3cc81d928 100644 --- a/Userland/Libraries/LibJS/Runtime/NativeFunction.cpp +++ b/Userland/Libraries/LibJS/Runtime/NativeFunction.cpp @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -128,9 +129,9 @@ ThrowCompletionOr NativeFunction::internal_call(Value this_argument, Mark // Note: This is already the default value. // 8. Perform any necessary implementation-defined initialization of calleeContext. - callee_context.this_value = this_argument; callee_context.arguments.extend(move(arguments_list)); + callee_context.instruction_stream_iterator = vm.bytecode_interpreter().instruction_stream_iterator(); callee_context.lexical_environment = caller_context.lexical_environment; callee_context.variable_environment = caller_context.variable_environment; @@ -192,8 +193,8 @@ ThrowCompletionOr> NativeFunction::internal_construct(Marke // Note: This is already the default value. // 8. Perform any necessary implementation-defined initialization of calleeContext. - callee_context.arguments.extend(move(arguments_list)); + callee_context.instruction_stream_iterator = vm.bytecode_interpreter().instruction_stream_iterator(); callee_context.lexical_environment = caller_context.lexical_environment; callee_context.variable_environment = caller_context.variable_environment; diff --git a/Userland/Libraries/LibJS/Runtime/VM.cpp b/Userland/Libraries/LibJS/Runtime/VM.cpp index d60089b886..cdfd5346c8 100644 --- a/Userland/Libraries/LibJS/Runtime/VM.cpp +++ b/Userland/Libraries/LibJS/Runtime/VM.cpp @@ -746,8 +746,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->source_range.source_code) { - auto source_range = frame->source_range.realize(); + if (frame->instruction_stream_iterator->source_code()) { + auto source_range = frame->instruction_stream_iterator->source_range().realize(); 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/Tests/builtins/Error/Error.prototype.stack.js b/Userland/Libraries/LibJS/Tests/builtins/Error/Error.prototype.stack.js index 30a2e95ae0..0929819d27 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/Error/Error.prototype.stack.js +++ b/Userland/Libraries/LibJS/Tests/builtins/Error/Error.prototype.stack.js @@ -3,15 +3,14 @@ const stackGetter = stackDescriptor.get; const stackSetter = stackDescriptor.set; describe("getter - normal behavior", () => { - test.xfail("basic functionality", () => { + test("basic functionality", () => { const stackFrames = [ /^ at .*Error \(.*\/Error\.prototype\.stack\.js:\d+:\d+\)$/, /^ at .+\/Error\/Error\.prototype\.stack\.js:\d+:\d+$/, /^ at test \(.+\/test-common.js:\d+:\d+\)$/, - /^ at (.+\/test-common.js:\d+:\d+)/, - /^ at .+\/Error\/Error\.prototype\.stack\.js:6:73$/, + /^ at .+\/Error\/Error\.prototype\.stack\.js:6:9$/, /^ at describe \(.+\/test-common\.js:\d+:\d+\)$/, - /^ at .+\/Error\/Error\.prototype\.stack\.js:5:38$/, + /^ at .+\/Error\/Error\.prototype\.stack\.js:5:9$/, ]; const values = [ { diff --git a/Userland/Utilities/js.cpp b/Userland/Utilities/js.cpp index aa190e439a..6b7ab7ef10 100644 --- a/Userland/Utilities/js.cpp +++ b/Userland/Utilities/js.cpp @@ -268,8 +268,13 @@ static ErrorOr parse_and_run(JS::Realm& realm, StringView source, StringVi warnln(" -> {}", traceback_frame.function_name); warnln(" {} more calls", repetitions); } else { - for (size_t j = 0; j < repetitions + 1; ++j) - warnln(" -> {}", traceback_frame.function_name); + for (size_t j = 0; j < repetitions + 1; ++j) { + warnln(" -> {} ({}:{},{})", + traceback_frame.function_name, + traceback_frame.source_range().code->filename(), + traceback_frame.source_range().start.line, + traceback_frame.source_range().start.column); + } } repetitions = 0; }