LibJS: Don't use GC::Root unnecessarily in Error stack traces

This was causing a fair bit of root registration churn on pages that
throw lots of errors.

Since there's no need for these pointers to float around freely, we can
just visit them during the mark phase as usual.
This commit is contained in:
Andreas Kling 2025-11-30 10:21:50 +01:00 committed by Andreas Kling
parent 6ca69d9e26
commit 2d76e21cfd
Notes: github-actions[bot] 2025-11-30 10:55:07 +00:00
5 changed files with 18 additions and 10 deletions

View file

@ -58,6 +58,13 @@ Error::Error(Object& prototype)
populate_stack();
}
void Error::visit_edges(Visitor& visitor)
{
Base::visit_edges(visitor);
for (auto& frame : m_traceback)
visitor.visit(frame.cached_source_range);
}
// 20.5.8.1 InstallErrorCause ( O, options ), https://tc39.es/ecma262/#sec-installerrorcause
ThrowCompletionOr<void> Error::install_error_cause(Value options)
{

View file

@ -21,7 +21,7 @@ struct JS_API TracebackFrame {
Utf16String function_name;
[[nodiscard]] SourceRange const& source_range() const;
GC::Root<CachedSourceRange> cached_source_range;
GC::Ptr<CachedSourceRange> cached_source_range;
};
enum CompactTraceback {
@ -51,6 +51,8 @@ public:
protected:
explicit Error(Object& prototype);
virtual void visit_edges(Visitor&) override;
private:
virtual bool is_error_object() const final { return true; }

View file

@ -164,8 +164,8 @@ static_assert(IsTriviallyDestructible<ExecutionContext>);
} while (0)
struct StackTraceElement {
ExecutionContext* execution_context;
GC::Root<CachedSourceRange> source_range;
ExecutionContext* execution_context { nullptr };
GC::Ptr<CachedSourceRange> source_range;
};
}

View file

@ -774,11 +774,11 @@ static GC::Ptr<CachedSourceRange> get_source_range(ExecutionContext* context)
return context->rare_data()->cached_source_range;
}
Vector<StackTraceElement> VM::stack_trace() const
GC::ConservativeVector<StackTraceElement> VM::stack_trace() const
{
Vector<StackTraceElement> stack_trace;
for (ssize_t i = m_execution_context_stack.size() - 1; i >= 0; i--) {
auto* context = m_execution_context_stack[i];
GC::ConservativeVector<StackTraceElement> stack_trace(heap());
stack_trace.ensure_capacity(m_execution_context_stack.size());
for (auto* context : m_execution_context_stack.in_reverse()) {
stack_trace.append({
.execution_context = context,
.source_range = get_source_range(context),

View file

@ -58,8 +58,7 @@ public:
static NonnullRefPtr<VM> create();
~VM();
GC::Heap& heap() { return m_heap; }
GC::Heap const& heap() const { return m_heap; }
GC::Heap& heap() const { return const_cast<GC::Heap&>(m_heap); }
Bytecode::Interpreter& bytecode_interpreter() { return *m_bytecode_interpreter; }
@ -295,7 +294,7 @@ public:
Function<ThrowCompletionOr<void>(Realm&, NonnullOwnPtr<ExecutionContext>, ShadowRealm&)> host_initialize_shadow_realm;
Function<Crypto::SignedBigInteger(Object const& global)> host_system_utc_epoch_nanoseconds;
Vector<StackTraceElement> stack_trace() const;
[[nodiscard]] GC::ConservativeVector<StackTraceElement> stack_trace() const;
private:
using ErrorMessages = AK::Array<Utf16String, to_underlying(ErrorMessage::__Count)>;