LibJS: Capture UnrealizedSourceRanges in ExecutionContext, not ASTNodes

This loosens the connection to the AST interpreter and will allow us to
generate SourceRanges for the Bytecode interpreter in the future as well

Moves UnrealizedSourceRanges from TracebackFrame to the JS namespace for
this
This commit is contained in:
Hediadyoin1 2023-07-27 14:40:01 +02:00 committed by Andreas Kling
parent cd9bb985d4
commit 50bf303edd
Notes: sideshowbarker 2024-07-17 01:27:18 +09:00
11 changed files with 33 additions and 32 deletions

View File

@ -29,7 +29,6 @@ Workbook::Workbook(Vector<NonnullRefPtr<Sheet>>&& sheets, GUI::Window& parent_wi
m_workbook_object = m_vm->heap().allocate<WorkbookObject>(m_interpreter->realm(), m_interpreter->realm(), *this).release_allocated_value_but_fixme_should_propagate_errors();
m_interpreter->realm().global_object().define_direct_property("workbook", workbook_object(), JS::default_attributes);
m_main_execution_context.current_node = nullptr;
m_main_execution_context.this_value = &m_interpreter->realm().global_object();
m_main_execution_context.function_name = "(global execution context)"sv;
m_main_execution_context.lexical_environment = &m_interpreter->realm().global_environment();

View File

@ -47,7 +47,7 @@ public:
: m_interpreter(interpreter)
, m_chain_node { nullptr, node }
{
m_interpreter.vm().running_execution_context().current_node = &node;
m_interpreter.vm().running_execution_context().source_range = node.unrealized_source_range();
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdangling-pointer"
// The node pointer is popped from the interpreter in the destructor.

View File

@ -58,6 +58,10 @@ public:
virtual void dump(int indent) const;
[[nodiscard]] SourceRange source_range() const;
UnrealizedSourceRange unrealized_source_range() const
{
return { m_source_code, m_start_offset, m_end_offset };
}
u32 start_offset() const { return m_start_offset; }
u32 end_offset() const { return m_end_offset; }

View File

@ -154,8 +154,8 @@ ThrowCompletionOr<Value> ECMAScriptFunctionObject::internal_call(Value this_argu
// Non-standard
callee_context.arguments.extend(move(arguments_list));
if (auto* interpreter = vm.interpreter_if_exists())
callee_context.current_node = interpreter->current_node();
if (auto* interpreter = vm.interpreter_if_exists(); interpreter && interpreter->current_node())
callee_context.source_range = interpreter->current_node()->unrealized_source_range();
// 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.
@ -225,8 +225,8 @@ ThrowCompletionOr<NonnullGCPtr<Object>> ECMAScriptFunctionObject::internal_const
// Non-standard
callee_context.arguments.extend(move(arguments_list));
if (auto* interpreter = vm.interpreter_if_exists())
callee_context.current_node = interpreter->current_node();
if (auto* interpreter = vm.interpreter_if_exists(); interpreter && interpreter->current_node())
callee_context.source_range = interpreter->current_node()->unrealized_source_range();
// 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.

View File

@ -23,7 +23,7 @@ SourceRange const& TracebackFrame::source_range() const
static auto dummy_source_code = SourceCode::create(String {}, String {});
return SourceRange { dummy_source_code, {}, {} };
}
return unrealized->source_code->range_from_offsets(unrealized->start_offset, unrealized->end_offset);
return unrealized->realize();
}();
source_range_storage = move(source_range);
}
@ -85,20 +85,9 @@ void Error::populate_stack()
TracebackFrame frame {
.function_name = move(function_name),
.source_range_storage = TracebackFrame::UnrealizedSourceRange {},
.source_range_storage = context->source_range,
};
// 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.
if (context->current_node) {
auto* unrealized = frame.source_range_storage.get_pointer<TracebackFrame::UnrealizedSourceRange>();
unrealized->source_code = context->current_node->source_code();
unrealized->start_offset = context->current_node->start_offset();
unrealized->end_offset = context->current_node->end_offset();
}
m_traceback.append(move(frame));
}
}

View File

@ -19,11 +19,6 @@ struct TracebackFrame {
DeprecatedFlyString function_name;
[[nodiscard]] SourceRange const& source_range() const;
struct UnrealizedSourceRange {
u32 start_offset { 0 };
u32 end_offset { 0 };
RefPtr<JS::SourceCode const> source_code;
};
mutable Variant<SourceRange, UnrealizedSourceRange> source_range_storage;
};

View File

@ -33,7 +33,7 @@ ExecutionContext ExecutionContext::copy() const
copy.lexical_environment = lexical_environment;
copy.variable_environment = variable_environment;
copy.private_environment = private_environment;
copy.current_node = current_node;
copy.source_range = source_range;
copy.function_name = function_name;
copy.this_value = this_value;
copy.is_strict_mode = is_strict_mode;

View File

@ -15,6 +15,7 @@
#include <LibJS/Module.h>
#include <LibJS/Runtime/PrivateEnvironment.h>
#include <LibJS/Runtime/Value.h>
#include <LibJS/SourceRange.h>
namespace JS {
@ -42,7 +43,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;
ASTNode const* current_node { nullptr };
UnrealizedSourceRange source_range;
DeprecatedFlyString function_name;
Value this_value;
MarkedVector<Value> arguments;

View File

@ -5,6 +5,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/AST.h>
#include <LibJS/Interpreter.h>
#include <LibJS/Runtime/FunctionEnvironment.h>
#include <LibJS/Runtime/GlobalObject.h>
@ -141,8 +142,8 @@ ThrowCompletionOr<Value> NativeFunction::internal_call(Value this_argument, Mark
// NOTE: This is a LibJS specific hack for NativeFunction to inherit the strictness of its caller.
callee_context.is_strict_mode = vm.in_strict_mode();
if (auto* interpreter = vm.interpreter_if_exists())
callee_context.current_node = interpreter->current_node();
if (auto* interpreter = vm.interpreter_if_exists(); interpreter && interpreter->current_node())
callee_context.source_range = interpreter->current_node()->unrealized_source_range();
// </8.> --------------------------------------------------------------------------
@ -204,8 +205,8 @@ ThrowCompletionOr<NonnullGCPtr<Object>> NativeFunction::internal_construct(Marke
// NOTE: This is a LibJS specific hack for NativeFunction to inherit the strictness of its caller.
callee_context.is_strict_mode = vm.in_strict_mode();
if (auto* interpreter = vm.interpreter_if_exists())
callee_context.current_node = interpreter->current_node();
if (auto* interpreter = vm.interpreter_if_exists(); interpreter && interpreter->current_node())
callee_context.source_range = interpreter->current_node()->unrealized_source_range();
// </8.> --------------------------------------------------------------------------

View File

@ -805,8 +805,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->current_node) {
auto source_range = frame->current_node->source_range();
if (frame->source_range.source_code) {
auto source_range = frame->source_range.realize();
dbgln("-> {} @ {}:{},{}", frame->function_name, source_range.filename(), source_range.start.line, source_range.start.column);
} else {
dbgln("-> {}", frame->function_name);

View File

@ -30,4 +30,16 @@ struct SourceRange {
DeprecatedString filename() const;
};
struct UnrealizedSourceRange {
[[nodiscard]] SourceRange realize() const
{
VERIFY(source_code);
return source_code->range_from_offsets(start_offset, end_offset);
}
RefPtr<SourceCode const> source_code;
u32 start_offset { 0 };
u32 end_offset { 0 };
};
}