LibJS: Remove ExecutionContext::function_name field

Instead of having ExecutionContext track function names separately,
we give FunctionObject a virtual function that returns an appropriate
name string for use in call stacks.
This commit is contained in:
Andreas Kling 2025-10-29 08:28:41 +01:00 committed by Andreas Kling
parent e967631763
commit 4c7ffc0552
20 changed files with 57 additions and 17 deletions

View File

@ -350,10 +350,10 @@ ThrowCompletionOr<Value> Console::trace()
auto& execution_context_stack = vm.execution_context_stack();
// NOTE: -2 to skip the console.trace() execution context
for (ssize_t i = execution_context_stack.size() - 2; i >= 0; --i) {
auto const& function_name = execution_context_stack[i]->function_name;
trace.stack.append((!function_name || function_name->is_empty())
auto function_name = execution_context_stack[i]->function ? execution_context_stack[i]->function->name_for_call_stack() : ""_utf16;
trace.stack.append(function_name.is_empty()
? "<anonymous>"_string
: function_name->utf8_string());
: function_name.to_utf8());
}
// 2. Optionally, let formattedData be the result of Formatter(data), and incorporate formattedData as a label for trace.

View File

@ -115,4 +115,9 @@ ThrowCompletionOr<void> BoundFunction::get_stack_frame_size(size_t& registers_an
return {};
}
Utf16String BoundFunction::name_for_call_stack() const
{
return m_bound_target_function->name_for_call_stack();
}
}

View File

@ -30,6 +30,8 @@ public:
Value bound_this() const { return m_bound_this; }
Vector<Value> const& bound_arguments() const { return m_bound_arguments; }
virtual Utf16String name_for_call_stack() const override;
private:
BoundFunction(Realm&, FunctionObject& target_function, Value bound_this, Vector<Value> bound_arguments, Object* prototype);

View File

@ -694,7 +694,6 @@ void ECMAScriptFunctionObject::prepare_for_ordinary_call(VM& vm, ExecutionContex
// 3. Set the Function of calleeContext to F.
callee_context.function = this;
callee_context.function_name = m_name_string;
// 4. Let calleeRealm be F.[[Realm]].
// 5. Set the Realm of calleeContext to calleeRealm.
@ -933,4 +932,9 @@ ECMAScriptFunctionObject::ClassData& ECMAScriptFunctionObject::ensure_class_data
return *m_class_data;
}
Utf16String ECMAScriptFunctionObject::name_for_call_stack() const
{
return m_name_string->utf16_string();
}
}

View File

@ -139,6 +139,8 @@ public:
Statement const& ecmascript_code() const { return *shared_data().m_ecmascript_code; }
[[nodiscard]] virtual FunctionParameters const& formal_parameters() const override { return *shared_data().m_formal_parameters; }
virtual Utf16String name_for_call_stack() const override;
Utf16FlyString const& name() const { return shared_data().m_name; }
void set_name(Utf16FlyString const& name);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2020-2025, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
@ -10,6 +10,7 @@
#include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/ExecutionContext.h>
#include <LibJS/Runtime/FunctionObject.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/SourceRange.h>
@ -90,7 +91,7 @@ void Error::populate_stack()
for (auto& element : stack_trace) {
auto* context = element.execution_context;
TracebackFrame frame {
.function_name = context->function_name ? context->function_name->utf8_string() : ""_string,
.function_name = context->function ? context->function->name_for_call_stack() : ""_utf16,
.cached_source_range = element.source_range,
};
@ -117,7 +118,7 @@ String Error::stack_string(CompactTraceback compact) const
else
stack_string_builder.appendff(" at {} ({}:{}:{})\n", function_name, source_range.filename(), source_range.start.line, source_range.start.column);
} else {
stack_string_builder.appendff(" at {}\n", function_name.is_empty() ? "<unknown>"sv : function_name);
stack_string_builder.appendff(" at {}\n", function_name.is_empty() ? "<unknown>"_utf16 : function_name);
}
};

View File

@ -18,7 +18,7 @@
namespace JS {
struct JS_API TracebackFrame {
FlyString function_name;
Utf16String function_name;
[[nodiscard]] SourceRange const& source_range() const;
RefPtr<CachedSourceRange> cached_source_range;

View File

@ -122,7 +122,6 @@ NonnullOwnPtr<ExecutionContext> ExecutionContext::copy() const
copy->variable_environment = variable_environment;
copy->private_environment = private_environment;
copy->program_counter = program_counter;
copy->function_name = function_name;
copy->this_value = this_value;
copy->executable = executable;
copy->passed_argument_count = passed_argument_count;
@ -147,7 +146,6 @@ void ExecutionContext::visit_edges(Cell::Visitor& visitor)
if (this_value.has_value())
visitor.visit(*this_value);
visitor.visit(executable);
visitor.visit(function_name);
visitor.visit(registers_and_constants_and_locals_and_arguments_span());
for (auto& context : unwind_contexts) {
visitor.visit(context.lexical_environment);

View File

@ -62,7 +62,6 @@ public:
mutable RefPtr<CachedSourceRange> cached_source_range;
GC::Ptr<PrimitiveString> function_name;
Optional<Value> this_value;
GC::Ptr<Bytecode::Executable> executable;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020-2021, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2020-2025, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
@ -42,6 +42,8 @@ public:
virtual FunctionParameters const& formal_parameters() const { VERIFY_NOT_REACHED(); }
virtual Utf16String name_for_call_stack() const = 0;
template<typename T>
bool fast_is() const = delete;

View File

@ -192,4 +192,9 @@ JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::symbol_has_instance)
return TRY(ordinary_has_instance(vm, vm.argument(0), vm.this_value()));
}
Utf16String FunctionPrototype::name_for_call_stack() const
{
return "(Function.prototype)"_utf16;
}
}

View File

@ -20,6 +20,8 @@ public:
virtual ThrowCompletionOr<Value> internal_call(ExecutionContext&, Value this_argument) override;
virtual Utf16String name_for_call_stack() const override;
private:
explicit FunctionPrototype(Realm&);

View File

@ -123,7 +123,6 @@ ThrowCompletionOr<Value> NativeFunction::internal_call(ExecutionContext& callee_
// 4. Set the Function of calleeContext to F.
callee_context.function = this;
callee_context.function_name = m_name_string;
// 5. Let calleeRealm be F.[[Realm]].
auto callee_realm = m_realm;
@ -179,7 +178,6 @@ ThrowCompletionOr<GC::Ref<Object>> NativeFunction::internal_construct(ExecutionC
// 4. Set the Function of calleeContext to F.
callee_context.function = this;
callee_context.function_name = m_name_string;
// 5. Let calleeRealm be F.[[Realm]].
auto callee_realm = m_realm;
@ -233,4 +231,9 @@ bool NativeFunction::is_strict_mode() const
return true;
}
Utf16String NativeFunction::name_for_call_stack() const
{
return m_name.to_utf16_string();
}
}

View File

@ -36,6 +36,8 @@ public:
virtual ThrowCompletionOr<Value> call();
virtual ThrowCompletionOr<GC::Ref<Object>> construct(FunctionObject& new_target);
virtual Utf16String name_for_call_stack() const override;
Utf16FlyString const& name() const { return m_name; }
virtual bool is_strict_mode() const override;
virtual bool has_constructor() const override { return false; }

View File

@ -895,4 +895,9 @@ ThrowCompletionOr<void> ProxyObject::get_stack_frame_size(size_t& registers_and_
return as<FunctionObject>(*m_target).get_stack_frame_size(registers_and_constants_and_locals_count, argument_count);
}
Utf16String ProxyObject::name_for_call_stack() const
{
return "(Proxy)"_utf16;
}
}

View File

@ -46,6 +46,8 @@ public:
virtual ThrowCompletionOr<GC::Ref<Object>> internal_construct(ExecutionContext&, FunctionObject& new_target) override;
ThrowCompletionOr<void> validate_non_revoked_proxy() const;
virtual Utf16String name_for_call_stack() const override;
private:
ProxyObject(Object& target, Object& handler, Object& prototype);

View File

@ -483,11 +483,12 @@ 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->executable) {
auto source_range = frame->executable->source_range_at(frame->program_counter).realize();
dbgln("-> {} @ {}:{},{}", frame->function_name ? frame->function_name->utf8_string() : ""_string, source_range.filename(), source_range.start.line, source_range.start.column);
dbgln("-> {} @ {}:{},{}", frame->function ? frame->function->name_for_call_stack() : ""_utf16, source_range.filename(), source_range.start.line, source_range.start.column);
} else {
dbgln("-> {}", frame->function_name ? frame->function_name->utf8_string() : ""_string);
dbgln("-> {}", frame->function ? frame->function->name_for_call_stack() : ""_utf16);
}
}
}

View File

@ -174,4 +174,9 @@ void prepare_for_wrapped_function_call(WrappedFunction& function, ExecutionConte
// NOTE: No-op, see NOTE after step 2.
}
Utf16String WrappedFunction::name_for_call_stack() const
{
return "(Wrapped)"_utf16;
}
}

View File

@ -29,6 +29,8 @@ public:
virtual ThrowCompletionOr<void> get_stack_frame_size(size_t& registers_and_constants_and_locals_count, size_t& argument_count) override;
virtual Utf16String name_for_call_stack() const override;
private:
WrappedFunction(Realm&, FunctionObject&, Object& prototype);

View File

@ -114,7 +114,7 @@ void DevToolsConsoleClient::report_exception(JS::Error const& exception, bool in
WebView::StackFrame stack_frame;
if (!frame.function_name.is_empty())
stack_frame.function = frame.function_name.to_string();
stack_frame.function = frame.function_name.to_utf8();
if (!source_range.filename().is_empty() || source_range.start.offset != 0 || source_range.end.offset != 0) {
stack_frame.file = String::from_utf8_with_replacement_character(source_range.filename());