diff --git a/Userland/Libraries/LibJS/Runtime/Error.cpp b/Userland/Libraries/LibJS/Runtime/Error.cpp index 7c9d81c3e6..23a471c370 100644 --- a/Userland/Libraries/LibJS/Runtime/Error.cpp +++ b/Userland/Libraries/LibJS/Runtime/Error.cpp @@ -54,29 +54,46 @@ ThrowCompletionOr Error::install_error_cause(Value options) void Error::populate_stack() { - AK::StringBuilder stack_string_builder {}; + 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--) { + auto* context = vm.execution_context_stack()[i]; + auto function_name = context->function_name; + if (function_name.is_empty()) + function_name = ""sv; + m_traceback.empend( + move(function_name), + // We might not have an AST node associated with the execution context, e.g. in promise + // 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 {}); + } +} +String Error::stack_string() const +{ + StringBuilder stack_string_builder; // Note: We roughly follow V8's formatting // Note: The error's name and message get prepended by ErrorPrototype::stack // Note: We don't want to capture the global exectution context, so we omit the last frame // FIXME: We generate a stack-frame for the Errors constructor, other engines do not - for (size_t i = vm().execution_context_stack().size() - 1; i > 0; --i) { - auto const* frame = vm().execution_context_stack()[i]; + for (size_t i = 0; i < m_traceback.size() - 1; ++i) { + 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) { - auto function_name = frame->function_name; - if (auto const* current_node = frame->current_node) { - auto const& source_range = current_node->source_range(); - - if (function_name.is_empty()) - stack_string_builder.appendff(" at {}:{}:{}\n", source_range.filename, source_range.start.line, source_range.start.column); + if (function_name == ""sv) + 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, source_range.filename, source_range.start.line, source_range.start.column); + stack_string_builder.appendff(" at {} ({}:{}:{})\n", function_name, frame.source_range.filename, frame.source_range.start.line, frame.source_range.start.column); } else { stack_string_builder.appendff(" at {}\n", function_name.is_empty() ? ""sv : function_name.view()); } } - m_stack_string = stack_string_builder.build(); + return stack_string_builder.build(); } #define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \ diff --git a/Userland/Libraries/LibJS/Runtime/Error.h b/Userland/Libraries/LibJS/Runtime/Error.h index 279c3c905f..a7967320de 100644 --- a/Userland/Libraries/LibJS/Runtime/Error.h +++ b/Userland/Libraries/LibJS/Runtime/Error.h @@ -10,9 +10,15 @@ #include #include #include +#include namespace JS { +struct TracebackFrame { + FlyString function_name; + SourceRange source_range; +}; + class Error : public Object { JS_OBJECT(Error, Object); @@ -23,13 +29,15 @@ public: explicit Error(Object& prototype); virtual ~Error() override = default; - String const& stack_string() const { return m_stack_string; } + [[nodiscard]] String stack_string() const; ThrowCompletionOr install_error_cause(Value options); + Vector const& traceback() const { return m_traceback; } + private: void populate_stack(); - String m_stack_string {}; + Vector m_traceback; }; // NOTE: Making these inherit from Error is not required by the spec but diff --git a/Userland/Libraries/LibJS/Runtime/Exception.h b/Userland/Libraries/LibJS/Runtime/Exception.h index 69df020635..d7e62e7d8d 100644 --- a/Userland/Libraries/LibJS/Runtime/Exception.h +++ b/Userland/Libraries/LibJS/Runtime/Exception.h @@ -10,16 +10,12 @@ #include #include #include +#include #include #include namespace JS { -struct TracebackFrame { - FlyString function_name; - SourceRange source_range; -}; - class Exception : public Cell { public: explicit Exception(Value);