Jelajahi Sumber

LibJS: Add file & line number to bytecode VM stack traces :^)

This works by adding source start/end offset to every bytecode
instruction. In the future we can make this more efficient by keeping
a map of bytecode ranges to source ranges in the Executable instead,
but let's just get traces working first.

Co-Authored-By: Andrew Kaster <akaster@serenityos.org>
Andreas Kling 1 tahun lalu
induk
melakukan
1c06111cbd

+ 57 - 0
Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp

@@ -29,6 +29,7 @@ Bytecode::CodeGenerationErrorOr<void> ASTNode::generate_bytecode(Bytecode::Gener
 
 Bytecode::CodeGenerationErrorOr<void> ScopeNode::generate_bytecode(Bytecode::Generator& generator) const
 {
+    Bytecode::Generator::SourceLocationScope scope(generator, *this);
     bool did_create_lexical_environment = false;
 
     if (is<BlockStatement>(*this)) {
@@ -61,11 +62,13 @@ Bytecode::CodeGenerationErrorOr<void> EmptyStatement::generate_bytecode(Bytecode
 
 Bytecode::CodeGenerationErrorOr<void> ExpressionStatement::generate_bytecode(Bytecode::Generator& generator) const
 {
+    Bytecode::Generator::SourceLocationScope scope(generator, *this);
     return m_expression->generate_bytecode(generator);
 }
 
 Bytecode::CodeGenerationErrorOr<void> BinaryExpression::generate_bytecode(Bytecode::Generator& generator) const
 {
+    Bytecode::Generator::SourceLocationScope scope(generator, *this);
     if (m_op == BinaryOp::In && is<PrivateIdentifier>(*m_lhs)) {
         auto const& private_identifier = static_cast<PrivateIdentifier const&>(*m_lhs).string();
         TRY(m_rhs->generate_bytecode(generator));
@@ -154,6 +157,7 @@ Bytecode::CodeGenerationErrorOr<void> BinaryExpression::generate_bytecode(Byteco
 
 Bytecode::CodeGenerationErrorOr<void> 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<void> LogicalExpression::generate_bytecode(Bytec
 
 Bytecode::CodeGenerationErrorOr<void> 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<void> UnaryExpression::generate_bytecode(Bytecod
 
 Bytecode::CodeGenerationErrorOr<void> NumericLiteral::generate_bytecode(Bytecode::Generator& generator) const
 {
+    Bytecode::Generator::SourceLocationScope scope(generator, *this);
     generator.emit<Bytecode::Op::LoadImmediate>(m_value);
     return {};
 }
 
 Bytecode::CodeGenerationErrorOr<void> BooleanLiteral::generate_bytecode(Bytecode::Generator& generator) const
 {
+    Bytecode::Generator::SourceLocationScope scope(generator, *this);
     generator.emit<Bytecode::Op::LoadImmediate>(Value(m_value));
     return {};
 }
 
 Bytecode::CodeGenerationErrorOr<void> NullLiteral::generate_bytecode(Bytecode::Generator& generator) const
 {
+    Bytecode::Generator::SourceLocationScope scope(generator, *this);
     generator.emit<Bytecode::Op::LoadImmediate>(js_null());
     return {};
 }
 
 Bytecode::CodeGenerationErrorOr<void> 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<void> BigIntLiteral::generate_bytecode(Bytecode:
 
 Bytecode::CodeGenerationErrorOr<void> StringLiteral::generate_bytecode(Bytecode::Generator& generator) const
 {
+    Bytecode::Generator::SourceLocationScope scope(generator, *this);
     generator.emit<Bytecode::Op::NewString>(generator.intern_string(m_value));
     return {};
 }
 
 Bytecode::CodeGenerationErrorOr<void> 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<void> RegExpLiteral::generate_bytecode(Bytecode:
 
 Bytecode::CodeGenerationErrorOr<void> Identifier::generate_bytecode(Bytecode::Generator& generator) const
 {
+    Bytecode::Generator::SourceLocationScope scope(generator, *this);
     if (is_global()) {
         generator.emit<Bytecode::Op::GetGlobal>(generator.intern_identifier(m_string), generator.next_global_variable_cache());
     } else if (is_local()) {
@@ -354,6 +366,7 @@ static Bytecode::CodeGenerationErrorOr<void> arguments_to_array_for_call(Bytecod
 
 Bytecode::CodeGenerationErrorOr<void> 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<void> generate_binding_pattern_bytecode(B
 
 Bytecode::CodeGenerationErrorOr<void> 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<void> AssignmentExpression::generate_bytecode(By
 //  LabelledStatement : LabelIdentifier : LabelledItem
 Bytecode::CodeGenerationErrorOr<void> 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<void> LabelledStatement::generate_bytecode(Bytec
 // LabelledStatement : LabelIdentifier : LabelledItem
 Bytecode::CodeGenerationErrorOr<void> LabelledStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector<DeprecatedFlyString> 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<void> IterationStatement::generate_labelled_eval
 
 Bytecode::CodeGenerationErrorOr<void> WhileStatement::generate_bytecode(Bytecode::Generator& generator) const
 {
+    Bytecode::Generator::SourceLocationScope scope(generator, *this);
     return generate_labelled_evaluation(generator, {});
 }
 
 Bytecode::CodeGenerationErrorOr<void> WhileStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector<DeprecatedFlyString> 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<void> WhileStatement::generate_labelled_evaluati
 
 Bytecode::CodeGenerationErrorOr<void> DoWhileStatement::generate_bytecode(Bytecode::Generator& generator) const
 {
+    Bytecode::Generator::SourceLocationScope scope(generator, *this);
     return generate_labelled_evaluation(generator, {});
 }
 
 Bytecode::CodeGenerationErrorOr<void> DoWhileStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector<DeprecatedFlyString> 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<void> DoWhileStatement::generate_labelled_evalua
 
 Bytecode::CodeGenerationErrorOr<void> ForStatement::generate_bytecode(Bytecode::Generator& generator) const
 {
+    Bytecode::Generator::SourceLocationScope scope(generator, *this);
     return generate_labelled_evaluation(generator, {});
 }
 
 Bytecode::CodeGenerationErrorOr<void> ForStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector<DeprecatedFlyString> const& label_set) const
 {
+    Bytecode::Generator::SourceLocationScope scope(generator, *this);
     // init
     // jump always (true) test
     // test
@@ -938,6 +960,7 @@ Bytecode::CodeGenerationErrorOr<void> ForStatement::generate_labelled_evaluation
 
 Bytecode::CodeGenerationErrorOr<void> ObjectExpression::generate_bytecode(Bytecode::Generator& generator) const
 {
+    Bytecode::Generator::SourceLocationScope scope(generator, *this);
     generator.emit<Bytecode::Op::NewObject>();
     if (m_properties.is_empty())
         return {};
@@ -999,6 +1022,7 @@ Bytecode::CodeGenerationErrorOr<void> ObjectExpression::generate_bytecode(Byteco
 
 Bytecode::CodeGenerationErrorOr<void> ArrayExpression::generate_bytecode(Bytecode::Generator& generator) const
 {
+    Bytecode::Generator::SourceLocationScope scope(generator, *this);
     if (m_elements.is_empty()) {
         generator.emit<Bytecode::Op::NewArray>();
         return {};
@@ -1048,12 +1072,14 @@ Bytecode::CodeGenerationErrorOr<void> ArrayExpression::generate_bytecode(Bytecod
 
 Bytecode::CodeGenerationErrorOr<void> MemberExpression::generate_bytecode(Bytecode::Generator& generator) const
 {
+    Bytecode::Generator::SourceLocationScope scope(generator, *this);
     return generator.emit_load_from_reference(*this);
 }
 
 Bytecode::CodeGenerationErrorOr<void> 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<Bytecode::Op::GetVariable>(index);
         generator.emit<Bytecode::Op::SetVariable>(index, Bytecode::Op::SetVariable::InitializationMode::Set, Bytecode::Op::EnvironmentMode::Var);
@@ -1063,6 +1089,7 @@ Bytecode::CodeGenerationErrorOr<void> FunctionDeclaration::generate_bytecode(Byt
 
 Bytecode::CodeGenerationErrorOr<void> FunctionExpression::generate_bytecode_with_lhs_name(Bytecode::Generator& generator, Optional<Bytecode::IdentifierTableIndex> lhs_name) const
 {
+    Bytecode::Generator::SourceLocationScope scope(generator, *this);
     bool has_name = !name().is_empty();
     Optional<Bytecode::IdentifierTableIndex> name_identifier;
 
@@ -1085,6 +1112,7 @@ Bytecode::CodeGenerationErrorOr<void> FunctionExpression::generate_bytecode_with
 
 Bytecode::CodeGenerationErrorOr<void> 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<void> assign_accumulator_to_variable_decl
 
 Bytecode::CodeGenerationErrorOr<void> 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<void> generate_optional_chain(Bytecode::G
 
 Bytecode::CodeGenerationErrorOr<void> 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<Bytecode::Op::LoadImmediate>(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<void> 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<void> 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<void> YieldExpression::generate_bytecode(Bytecod
 
 Bytecode::CodeGenerationErrorOr<void> 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<void> IfStatement::generate_bytecode(Bytecode::G
 
 Bytecode::CodeGenerationErrorOr<void> 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<void> DebuggerStatement::generate_bytecode(Bytec
 
 Bytecode::CodeGenerationErrorOr<void> 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<void> ConditionalExpression::generate_bytecode(B
 
 Bytecode::CodeGenerationErrorOr<void> 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<void> SequenceExpression::generate_bytecode(Byte
 
 Bytecode::CodeGenerationErrorOr<void> 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<void> TemplateLiteral::generate_bytecode(Bytecod
 
 Bytecode::CodeGenerationErrorOr<void> 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<Bytecode::Op::Store>(tag_reg);
@@ -2252,6 +2290,7 @@ Bytecode::CodeGenerationErrorOr<void> TaggedTemplateLiteral::generate_bytecode(B
 
 Bytecode::CodeGenerationErrorOr<void> UpdateExpression::generate_bytecode(Bytecode::Generator& generator) const
 {
+    Bytecode::Generator::SourceLocationScope scope(generator, *this);
     TRY(generator.emit_load_from_reference(*m_argument));
 
     Optional<Bytecode::Register> previous_value_for_postfix_reg;
@@ -2275,6 +2314,7 @@ Bytecode::CodeGenerationErrorOr<void> UpdateExpression::generate_bytecode(Byteco
 
 Bytecode::CodeGenerationErrorOr<void> ThrowStatement::generate_bytecode(Bytecode::Generator& generator) const
 {
+    Bytecode::Generator::SourceLocationScope scope(generator, *this);
     TRY(m_argument->generate_bytecode(generator));
     generator.perform_needed_unwinds<Bytecode::Op::Throw>();
     generator.emit<Bytecode::Op::Throw>();
@@ -2283,6 +2323,7 @@ Bytecode::CodeGenerationErrorOr<void> ThrowStatement::generate_bytecode(Bytecode
 
 Bytecode::CodeGenerationErrorOr<void> 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<void> BreakStatement::generate_bytecode(Bytecode
 
 Bytecode::CodeGenerationErrorOr<void> TryStatement::generate_bytecode(Bytecode::Generator& generator) const
 {
+    Bytecode::Generator::SourceLocationScope scope(generator, *this);
     auto& saved_block = generator.current_block();
 
     Optional<Bytecode::Label> handler_target;
@@ -2397,11 +2439,13 @@ Bytecode::CodeGenerationErrorOr<void> TryStatement::generate_bytecode(Bytecode::
 
 Bytecode::CodeGenerationErrorOr<void> SwitchStatement::generate_bytecode(Bytecode::Generator& generator) const
 {
+    Bytecode::Generator::SourceLocationScope scope(generator, *this);
     return generate_labelled_evaluation(generator, {});
 }
 
 Bytecode::CodeGenerationErrorOr<void> SwitchStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector<DeprecatedFlyString> const& label_set) const
 {
+    Bytecode::Generator::SourceLocationScope scope(generator, *this);
     auto discriminant_reg = generator.allocate_register();
     TRY(m_discriminant->generate_bytecode(generator));
     generator.emit<Bytecode::Op::Store>(discriminant_reg);
@@ -2484,6 +2528,7 @@ Bytecode::CodeGenerationErrorOr<void> SuperExpression::generate_bytecode(Bytecod
 
 Bytecode::CodeGenerationErrorOr<void> ClassDeclaration::generate_bytecode(Bytecode::Generator& generator) const
 {
+    Bytecode::Generator::SourceLocationScope scope(generator, *this);
     auto accumulator_backup_reg = generator.allocate_register();
     generator.emit<Bytecode::Op::Store>(accumulator_backup_reg);
 
@@ -2516,6 +2561,7 @@ Bytecode::CodeGenerationErrorOr<void> ClassExpression::generate_bytecode_with_lh
 
 Bytecode::CodeGenerationErrorOr<void> 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<void> SpreadExpression::generate_bytecode(Byteco
 
 Bytecode::CodeGenerationErrorOr<void> ThisExpression::generate_bytecode(Bytecode::Generator& generator) const
 {
+    Bytecode::Generator::SourceLocationScope scope(generator, *this);
     generator.emit<Bytecode::Op::ResolveThisBinding>();
     return {};
 }
@@ -2564,6 +2611,7 @@ static void generate_await(Bytecode::Generator& generator, Bytecode::Register re
 
 Bytecode::CodeGenerationErrorOr<void> 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<void> AwaitExpression::generate_bytecode(Bytecod
 
 Bytecode::CodeGenerationErrorOr<void> WithStatement::generate_bytecode(Bytecode::Generator& generator) const
 {
+    Bytecode::Generator::SourceLocationScope scope(generator, *this);
     TRY(m_object->generate_bytecode(generator));
     generator.emit<Bytecode::Op::EnterObjectEnvironment>();
 
@@ -2934,6 +2983,7 @@ static Bytecode::CodeGenerationErrorOr<void> for_in_of_body_evaluation(Bytecode:
 
 Bytecode::CodeGenerationErrorOr<void> ForInStatement::generate_bytecode(Bytecode::Generator& generator) const
 {
+    Bytecode::Generator::SourceLocationScope scope(generator, *this);
     return generate_labelled_evaluation(generator, {});
 }
 
@@ -2952,6 +3002,7 @@ Bytecode::CodeGenerationErrorOr<void> ForInStatement::generate_labelled_evaluati
 
 Bytecode::CodeGenerationErrorOr<void> ForOfStatement::generate_bytecode(Bytecode::Generator& generator) const
 {
+    Bytecode::Generator::SourceLocationScope scope(generator, *this);
     return generate_labelled_evaluation(generator, {});
 }
 
@@ -2969,6 +3020,7 @@ Bytecode::CodeGenerationErrorOr<void> ForOfStatement::generate_labelled_evaluati
 
 Bytecode::CodeGenerationErrorOr<void> ForAwaitOfStatement::generate_bytecode(Bytecode::Generator& generator) const
 {
+    Bytecode::Generator::SourceLocationScope scope(generator, *this);
     return generate_labelled_evaluation(generator, {});
 }
 
@@ -2987,6 +3039,7 @@ Bytecode::CodeGenerationErrorOr<void> ForAwaitOfStatement::generate_labelled_eva
 // 13.3.12.1 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-meta-properties-runtime-semantics-evaluation
 Bytecode::CodeGenerationErrorOr<void> 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<void> MetaProperty::generate_bytecode(Bytecode::
 
 Bytecode::CodeGenerationErrorOr<void> 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<Bytecode::Op::Return>();
     generator.emit<Bytecode::Op::Return>();
@@ -3084,6 +3138,7 @@ static Bytecode::CodeGenerationErrorOr<void> generate_optional_chain(Bytecode::G
 
 Bytecode::CodeGenerationErrorOr<void> 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<Bytecode::Op::LoadImmediate>(js_undefined());
@@ -3093,6 +3148,7 @@ Bytecode::CodeGenerationErrorOr<void> OptionalChain::generate_bytecode(Bytecode:
 
 Bytecode::CodeGenerationErrorOr<void> 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<Bytecode::Op::Store>(specifier_reg);
@@ -3111,6 +3167,7 @@ Bytecode::CodeGenerationErrorOr<void> ImportCall::generate_bytecode(Bytecode::Ge
 
 Bytecode::CodeGenerationErrorOr<void> 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);

+ 6 - 0
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<PropertyLookupCache> property_lookup_caches;
@@ -34,6 +39,7 @@ struct Executable {
     NonnullOwnPtr<StringTable> string_table;
     NonnullOwnPtr<IdentifierTable> identifier_table;
     NonnullOwnPtr<RegexTable> regex_table;
+    NonnullRefPtr<SourceCode const> source_code;
     size_t number_of_registers { 0 };
     bool is_strict_mode { false };
 

+ 14 - 0
Userland/Libraries/LibJS/Bytecode/Generator.cpp

@@ -24,6 +24,7 @@ CodeGenerationErrorOr<NonnullOwnPtr<Executable>> 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<NonnullOwnPtr<Executable>> 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;

+ 18 - 2
Userland/Libraries/LibJS/Bytecode/Generator.h

@@ -8,6 +8,7 @@
 
 #include <AK/OwnPtr.h>
 #include <AK/SinglyLinkedList.h>
+#include <LibJS/AST.h>
 #include <LibJS/Bytecode/BasicBlock.h>
 #include <LibJS/Bytecode/CodeGenerationError.h>
 #include <LibJS/Bytecode/Executable.h>
@@ -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<typename OpType, typename... Args>
     OpType& emit(Args&&... args)
     {
@@ -58,7 +69,9 @@ public:
         new (slot) OpType(forward<Args>(args)...);
         if constexpr (OpType::IsTerminator)
             m_current_basic_block->terminate({}, static_cast<Instruction const*>(slot));
-        return *static_cast<OpType*>(slot);
+        auto* op = static_cast<OpType*>(slot);
+        op->set_source_record({ m_current_ast_node->start_offset(), m_current_ast_node->end_offset() });
+        return *op;
     }
 
     template<typename OpType, typename... Args>
@@ -77,7 +90,9 @@ public:
         new (slot) OpType(forward<Args>(args)...);
         if constexpr (OpType::IsTerminator)
             m_current_basic_block->terminate({}, static_cast<Instruction const*>(slot));
-        return *static_cast<OpType*>(slot);
+        auto* op = static_cast<OpType*>(slot);
+        op->set_source_record({ m_current_ast_node->start_offset(), m_current_ast_node->end_offset() });
+        return *op;
     }
 
     CodeGenerationErrorOr<void> 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<NonnullOwnPtr<BasicBlock>> m_root_basic_blocks;
     NonnullOwnPtr<StringTable> m_string_table;
     NonnullOwnPtr<IdentifierTable> m_identifier_table;

+ 17 - 0
Userland/Libraries/LibJS/Bytecode/Instruction.cpp

@@ -4,6 +4,7 @@
  * SPDX-License-Identifier: BSD-2-Clause
  */
 
+#include <LibJS/Bytecode/Executable.h>
 #include <LibJS/Bytecode/Instruction.h>
 #include <LibJS/Bytecode/Op.h>
 
@@ -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<SourceCode> InstructionStreamIterator::source_code() const
+{
+    return m_executable ? m_executable->source_code.ptr() : nullptr;
+}
+
 }

+ 13 - 1
Userland/Libraries/LibJS/Bytecode/Instruction.h

@@ -8,7 +8,9 @@
 
 #include <AK/Forward.h>
 #include <AK/Span.h>
+#include <LibJS/Bytecode/Executable.h>
 #include <LibJS/Forward.h>
+#include <LibJS/SourceRange.h>
 
 #define ENUMERATE_BYTECODE_OPS(O)      \
     O(Add)                             \
@@ -137,6 +139,10 @@ public:
     ThrowCompletionOr<void> 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<SourceCode> source_code() const;
+
 private:
     Instruction const& dereference() const { return *reinterpret_cast<Instruction const*>(m_bytes.data() + offset()); }
 
     ReadonlyBytes m_bytes;
     size_t m_offset { 0 };
+    Executable const* m_executable { nullptr };
 };
 
 }

+ 6 - 8
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<BasicBlock const*>(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<InstructionStreamIterator&>(pc) };
+        TemporaryChange context_change { vm().running_execution_context().instruction_stream_iterator, Optional<InstructionStreamIterator&>(pc) };
 
         // FIXME: This is getting kinda spaghetti-y
         bool will_jump = false;
@@ -369,14 +371,10 @@ ThrowCompletionOr<void> 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<NonnullOwnPtr<Bytecode::Executable>> compile(VM& vm, ASTNode const& node, FunctionKind kind, DeprecatedFlyString const& name)

+ 2 - 2
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<Value>& this_value() { return m_this_value; }
@@ -121,7 +121,7 @@ private:
     Optional<Value> m_saved_exception;
     Executable* m_current_executable { nullptr };
     BasicBlock const* m_current_block { nullptr };
-    InstructionStreamIterator* m_pc { nullptr };
+    Optional<InstructionStreamIterator&> m_pc {};
 };
 
 extern bool g_dump_bytecode;

+ 2 - 0
Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp

@@ -152,6 +152,7 @@ ThrowCompletionOr<Value> 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<NonnullGCPtr<Object>> 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.

+ 4 - 1
Userland/Libraries/LibJS/Runtime/Error.cpp

@@ -83,9 +83,12 @@ void Error::populate_stack()
         if (function_name.is_empty())
             function_name = "<unknown>"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));

+ 1 - 1
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;

+ 2 - 1
Userland/Libraries/LibJS/Runtime/ExecutionContext.h

@@ -10,6 +10,7 @@
 
 #include <AK/DeprecatedFlyString.h>
 #include <AK/WeakPtr.h>
+#include <LibJS/Bytecode/Instruction.h>
 #include <LibJS/Forward.h>
 #include <LibJS/Heap/MarkedVector.h>
 #include <LibJS/Module.h>
@@ -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<Cell> context_owner;
 
-    UnrealizedSourceRange source_range;
+    Optional<Bytecode::InstructionStreamIterator&> instruction_stream_iterator;
     DeprecatedFlyString function_name;
     Value this_value;
     MarkedVector<Value> arguments;

+ 3 - 2
Userland/Libraries/LibJS/Runtime/NativeFunction.cpp

@@ -6,6 +6,7 @@
  */
 
 #include <LibJS/AST.h>
+#include <LibJS/Bytecode/Interpreter.h>
 #include <LibJS/Runtime/FunctionEnvironment.h>
 #include <LibJS/Runtime/GlobalObject.h>
 #include <LibJS/Runtime/NativeFunction.h>
@@ -128,9 +129,9 @@ ThrowCompletionOr<Value> 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<NonnullGCPtr<Object>> 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;

+ 2 - 2
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);

+ 3 - 4
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 = [
             {

+ 7 - 2
Userland/Utilities/js.cpp

@@ -268,8 +268,13 @@ static ErrorOr<bool> 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;
             }