mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-09-10 19:46:03 +00:00
LibJS: Merge CallFrame into ExecutionContext
Before this change both ExecutionContext and CallFrame were created before executing function/module/script with a couple exceptions: - executable created for default function argument evaluation has to run in function's execution context. - `execute_ast_node()` where executable compiled for ASTNode has to be executed in running execution context. This change moves all members previously owned by CallFrame into ExecutionContext, and makes two exceptions where an executable that does not have a corresponding execution context saves and restores registers before running. Now, all execution state lives in a single entity, which makes it a bit easier to reason about and opens opportunities for optimizations, such as moving registers and local variables into a single array.
This commit is contained in:
parent
46b8a3afb7
commit
865e651a7d
Notes:
sideshowbarker
2024-07-17 10:39:39 +09:00
Author: https://github.com/kalenikaliaksandr
Commit: 865e651a7d
Pull-request: https://github.com/SerenityOS/serenity/pull/24180
15 changed files with 121 additions and 187 deletions
|
@ -647,7 +647,7 @@ inline ThrowCompletionOr<ECMAScriptFunctionObject*> new_class(VM& vm, Value supe
|
|||
|
||||
// NOTE: NewClass expects classEnv to be active lexical environment
|
||||
auto* class_environment = vm.lexical_environment();
|
||||
vm.running_execution_context().lexical_environment = interpreter.saved_lexical_environment_stack().take_last();
|
||||
vm.running_execution_context().lexical_environment = vm.running_execution_context().saved_lexical_environments.take_last();
|
||||
|
||||
Optional<DeprecatedFlyString> binding_name;
|
||||
DeprecatedFlyString class_name;
|
||||
|
|
|
@ -106,18 +106,6 @@ static ByteString format_value_list(StringView name, ReadonlySpan<Value> values)
|
|||
return builder.to_byte_string();
|
||||
}
|
||||
|
||||
NonnullOwnPtr<CallFrame> CallFrame::create(size_t register_count)
|
||||
{
|
||||
size_t allocation_size = sizeof(CallFrame) + sizeof(Value) * register_count;
|
||||
auto* memory = malloc(allocation_size);
|
||||
VERIFY(memory);
|
||||
auto call_frame = adopt_own(*new (memory) CallFrame);
|
||||
call_frame->register_count = register_count;
|
||||
for (auto i = 0u; i < register_count; ++i)
|
||||
new (&call_frame->register_values[i]) Value();
|
||||
return call_frame;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE static ThrowCompletionOr<Value> loosely_inequals(VM& vm, Value src1, Value src2)
|
||||
{
|
||||
if (src1.tag() == src2.tag()) {
|
||||
|
@ -163,13 +151,6 @@ Interpreter::~Interpreter()
|
|||
{
|
||||
}
|
||||
|
||||
void Interpreter::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
for (auto& frame : m_call_frames) {
|
||||
frame.visit([&](auto& value) { value->visit_edges(visitor); });
|
||||
}
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Value Interpreter::get(Operand op) const
|
||||
{
|
||||
switch (op.type()) {
|
||||
|
@ -263,11 +244,11 @@ ThrowCompletionOr<Value> Interpreter::run(Script& script_record, JS::GCPtr<Envir
|
|||
executable->dump();
|
||||
|
||||
// a. Set result to the result of evaluating script.
|
||||
auto result_or_error = run_and_return_frame(*executable, nullptr);
|
||||
auto result_or_error = run_executable(*executable, nullptr);
|
||||
if (result_or_error.value.is_error())
|
||||
result = result_or_error.value.release_error();
|
||||
else
|
||||
result = result_or_error.frame->registers()[0];
|
||||
result = result_or_error.return_register_value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -384,7 +365,8 @@ void Interpreter::run_bytecode()
|
|||
do_return(saved_return_value());
|
||||
break;
|
||||
}
|
||||
auto const* old_scheduled_jump = call_frame().previously_scheduled_jumps.take_last();
|
||||
auto& running_execution_context = vm().running_execution_context();
|
||||
auto const* old_scheduled_jump = running_execution_context.previously_scheduled_jumps.take_last();
|
||||
if (m_scheduled_jump) {
|
||||
// FIXME: If we `break` or `continue` in the finally, we need to clear
|
||||
// this field
|
||||
|
@ -418,7 +400,8 @@ void Interpreter::run_bytecode()
|
|||
if (!handler && !finalizer)
|
||||
return;
|
||||
|
||||
auto& unwind_context = unwind_contexts().last();
|
||||
auto& running_execution_context = vm().running_execution_context();
|
||||
auto& unwind_context = running_execution_context.unwind_contexts.last();
|
||||
VERIFY(unwind_context.executable == m_current_executable);
|
||||
|
||||
if (handler) {
|
||||
|
@ -453,7 +436,8 @@ void Interpreter::run_bytecode()
|
|||
}
|
||||
|
||||
if (auto const* finalizer = m_current_block->finalizer(); finalizer && !will_yield) {
|
||||
auto& unwind_context = unwind_contexts().last();
|
||||
auto& running_execution_context = vm().running_execution_context();
|
||||
auto& unwind_context = running_execution_context.unwind_contexts.last();
|
||||
VERIFY(unwind_context.executable == m_current_executable);
|
||||
reg(Register::saved_return_value()) = reg(Register::return_value());
|
||||
reg(Register::return_value()) = {};
|
||||
|
@ -470,7 +454,7 @@ void Interpreter::run_bytecode()
|
|||
}
|
||||
}
|
||||
|
||||
Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Executable& executable, BasicBlock const* entry_point, CallFrame* in_frame)
|
||||
Interpreter::ResultAndReturnRegister Interpreter::run_executable(Executable& executable, BasicBlock const* entry_point)
|
||||
{
|
||||
dbgln_if(JS_BYTECODE_DEBUG, "Bytecode::Interpreter will run unit {:p}", &executable);
|
||||
|
||||
|
@ -484,12 +468,13 @@ Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Executable& executa
|
|||
|
||||
TemporaryChange restore_current_block { m_current_block, entry_point ?: executable.basic_blocks.first() };
|
||||
|
||||
if (in_frame)
|
||||
push_call_frame(in_frame);
|
||||
else
|
||||
push_call_frame(CallFrame::create(executable.number_of_registers));
|
||||
auto& running_execution_context = vm().running_execution_context();
|
||||
if (running_execution_context.registers.size() < executable.number_of_registers)
|
||||
running_execution_context.registers.resize(executable.number_of_registers);
|
||||
|
||||
vm().execution_context_stack().last()->executable = &executable;
|
||||
reg(Register::return_value()) = {};
|
||||
|
||||
running_execution_context.executable = &executable;
|
||||
|
||||
run_bytecode();
|
||||
|
||||
|
@ -513,48 +498,39 @@ Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Executable& executa
|
|||
return_value = reg(Register::saved_return_value());
|
||||
auto exception = reg(Register::exception());
|
||||
|
||||
auto frame = pop_call_frame();
|
||||
|
||||
// NOTE: The return value from a called function is put into $0 in the caller context.
|
||||
if (!m_call_frames.is_empty())
|
||||
call_frame().registers()[0] = return_value;
|
||||
|
||||
// At this point we may have already run any queued promise jobs via on_call_stack_emptied,
|
||||
// in which case this is a no-op.
|
||||
vm().run_queued_promise_jobs();
|
||||
|
||||
vm().finish_execution_generation();
|
||||
|
||||
if (!exception.is_empty()) {
|
||||
if (auto* call_frame = frame.get_pointer<NonnullOwnPtr<CallFrame>>())
|
||||
return { throw_completion(exception), move(*call_frame) };
|
||||
return { throw_completion(exception), nullptr };
|
||||
}
|
||||
|
||||
if (auto* call_frame = frame.get_pointer<NonnullOwnPtr<CallFrame>>())
|
||||
return { return_value, move(*call_frame) };
|
||||
return { return_value, nullptr };
|
||||
if (!exception.is_empty())
|
||||
return { throw_completion(exception), vm().running_execution_context().registers[0] };
|
||||
return { return_value, vm().running_execution_context().registers[0] };
|
||||
}
|
||||
|
||||
void Interpreter::enter_unwind_context()
|
||||
{
|
||||
unwind_contexts().empend(
|
||||
auto& running_execution_context = vm().running_execution_context();
|
||||
running_execution_context.unwind_contexts.empend(
|
||||
m_current_executable,
|
||||
vm().running_execution_context().lexical_environment);
|
||||
call_frame().previously_scheduled_jumps.append(m_scheduled_jump);
|
||||
running_execution_context.previously_scheduled_jumps.append(m_scheduled_jump);
|
||||
m_scheduled_jump = nullptr;
|
||||
}
|
||||
|
||||
void Interpreter::leave_unwind_context()
|
||||
{
|
||||
unwind_contexts().take_last();
|
||||
auto& running_execution_context = vm().running_execution_context();
|
||||
running_execution_context.unwind_contexts.take_last();
|
||||
}
|
||||
|
||||
void Interpreter::catch_exception(Operand dst)
|
||||
{
|
||||
set(dst, reg(Register::exception()));
|
||||
reg(Register::exception()) = {};
|
||||
auto& context = unwind_contexts().last();
|
||||
auto& running_execution_context = vm().running_execution_context();
|
||||
auto& context = running_execution_context.unwind_contexts.last();
|
||||
VERIFY(!context.handler_called);
|
||||
VERIFY(context.executable == ¤t_executable());
|
||||
context.handler_called = true;
|
||||
|
@ -563,9 +539,10 @@ void Interpreter::catch_exception(Operand dst)
|
|||
|
||||
void Interpreter::enter_object_environment(Object& object)
|
||||
{
|
||||
auto& old_environment = vm().running_execution_context().lexical_environment;
|
||||
saved_lexical_environment_stack().append(old_environment);
|
||||
vm().running_execution_context().lexical_environment = new_object_environment(object, true, old_environment);
|
||||
auto& running_execution_context = vm().running_execution_context();
|
||||
auto& old_environment = running_execution_context.lexical_environment;
|
||||
running_execution_context.saved_lexical_environments.append(old_environment);
|
||||
running_execution_context.lexical_environment = new_object_environment(object, true, old_environment);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<NonnullGCPtr<Bytecode::Executable>> compile(VM& vm, ASTNode const& node, ReadonlySpan<FunctionParameter> parameters, FunctionKind kind, DeprecatedFlyString const& name)
|
||||
|
@ -583,20 +560,6 @@ ThrowCompletionOr<NonnullGCPtr<Bytecode::Executable>> compile(VM& vm, ASTNode co
|
|||
return bytecode_executable;
|
||||
}
|
||||
|
||||
void Interpreter::push_call_frame(Variant<NonnullOwnPtr<CallFrame>, CallFrame*> frame)
|
||||
{
|
||||
m_call_frames.append(move(frame));
|
||||
m_current_call_frame = this->call_frame().registers();
|
||||
reg(Register::return_value()) = {};
|
||||
}
|
||||
|
||||
Variant<NonnullOwnPtr<CallFrame>, CallFrame*> Interpreter::pop_call_frame()
|
||||
{
|
||||
auto frame = m_call_frames.take_last();
|
||||
m_current_call_frame = m_call_frames.is_empty() ? Span<Value> {} : this->call_frame().registers();
|
||||
return frame;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace JS::Bytecode {
|
||||
|
@ -1039,7 +1002,8 @@ ThrowCompletionOr<void> CreateLexicalEnvironment::execute_impl(Bytecode::Interpr
|
|||
swap(old_environment, environment);
|
||||
return environment;
|
||||
};
|
||||
interpreter.saved_lexical_environment_stack().append(make_and_swap_envs(interpreter.vm().running_execution_context().lexical_environment));
|
||||
auto& running_execution_context = interpreter.vm().running_execution_context();
|
||||
running_execution_context.saved_lexical_environments.append(make_and_swap_envs(running_execution_context.lexical_environment));
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -1453,7 +1417,8 @@ ThrowCompletionOr<void> ScheduleJump::execute_impl(Bytecode::Interpreter&) const
|
|||
|
||||
ThrowCompletionOr<void> LeaveLexicalEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||
{
|
||||
interpreter.vm().running_execution_context().lexical_environment = interpreter.saved_lexical_environment_stack().take_last();
|
||||
auto& running_execution_context = interpreter.vm().running_execution_context();
|
||||
running_execution_context.lexical_environment = running_execution_context.saved_lexical_environments.take_last();
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -1643,9 +1608,10 @@ ThrowCompletionOr<void> BlockDeclarationInstantiation::execute_impl(Bytecode::In
|
|||
{
|
||||
auto& vm = interpreter.vm();
|
||||
auto old_environment = vm.running_execution_context().lexical_environment;
|
||||
interpreter.saved_lexical_environment_stack().append(old_environment);
|
||||
vm.running_execution_context().lexical_environment = new_declarative_environment(*old_environment);
|
||||
m_scope_node.block_declaration_instantiation(vm, vm.running_execution_context().lexical_environment);
|
||||
auto& running_execution_context = vm.running_execution_context();
|
||||
running_execution_context.saved_lexical_environments.append(old_environment);
|
||||
running_execution_context.lexical_environment = new_declarative_environment(*old_environment);
|
||||
m_scope_node.block_declaration_instantiation(vm, running_execution_context.lexical_environment);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
|
@ -19,31 +19,6 @@ namespace JS::Bytecode {
|
|||
|
||||
class InstructionStreamIterator;
|
||||
|
||||
struct CallFrame {
|
||||
static NonnullOwnPtr<CallFrame> create(size_t register_count);
|
||||
|
||||
void operator delete(void* ptr) { free(ptr); }
|
||||
|
||||
void visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
visitor.visit(registers());
|
||||
visitor.visit(saved_lexical_environments);
|
||||
for (auto& context : unwind_contexts) {
|
||||
visitor.visit(context.lexical_environment);
|
||||
}
|
||||
}
|
||||
|
||||
Vector<GCPtr<Environment>> saved_lexical_environments;
|
||||
Vector<UnwindInfo> unwind_contexts;
|
||||
Vector<BasicBlock const*> previously_scheduled_jumps;
|
||||
|
||||
Span<Value> registers() { return { register_values, register_count }; }
|
||||
ReadonlySpan<Value> registers() const { return { register_values, register_count }; }
|
||||
|
||||
size_t register_count { 0 };
|
||||
Value register_values[];
|
||||
};
|
||||
|
||||
class Interpreter {
|
||||
public:
|
||||
explicit Interpreter(VM&);
|
||||
|
@ -60,27 +35,30 @@ public:
|
|||
|
||||
ThrowCompletionOr<Value> run(Bytecode::Executable& executable, Bytecode::BasicBlock const* entry_point = nullptr)
|
||||
{
|
||||
auto value_and_frame = run_and_return_frame(executable, entry_point);
|
||||
return move(value_and_frame.value);
|
||||
auto result_and_return_register = run_executable(executable, entry_point);
|
||||
return move(result_and_return_register.value);
|
||||
}
|
||||
|
||||
struct ValueAndFrame {
|
||||
struct ResultAndReturnRegister {
|
||||
ThrowCompletionOr<Value> value;
|
||||
OwnPtr<CallFrame> frame;
|
||||
Value return_register_value;
|
||||
};
|
||||
ValueAndFrame run_and_return_frame(Bytecode::Executable&, Bytecode::BasicBlock const* entry_point, CallFrame* = nullptr);
|
||||
ResultAndReturnRegister run_executable(Bytecode::Executable&, Bytecode::BasicBlock const* entry_point);
|
||||
|
||||
ALWAYS_INLINE Value& accumulator() { return reg(Register::accumulator()); }
|
||||
ALWAYS_INLINE Value& saved_return_value() { return reg(Register::saved_return_value()); }
|
||||
Value& reg(Register const& r) { return registers()[r.index()]; }
|
||||
Value reg(Register const& r) const { return registers()[r.index()]; }
|
||||
Value& reg(Register const& r)
|
||||
{
|
||||
return vm().running_execution_context().registers[r.index()];
|
||||
}
|
||||
Value reg(Register const& r) const
|
||||
{
|
||||
return vm().running_execution_context().registers[r.index()];
|
||||
}
|
||||
|
||||
[[nodiscard]] Value get(Operand) const;
|
||||
void set(Operand, Value);
|
||||
|
||||
auto& saved_lexical_environment_stack() { return call_frame().saved_lexical_environments; }
|
||||
auto& unwind_contexts() { return call_frame().unwind_contexts; }
|
||||
|
||||
void do_return(Value value)
|
||||
{
|
||||
reg(Register::return_value()) = value;
|
||||
|
@ -98,30 +76,13 @@ public:
|
|||
BasicBlock const& current_block() const { return *m_current_block; }
|
||||
Optional<InstructionStreamIterator const&> instruction_stream_iterator() const { return m_pc; }
|
||||
|
||||
void visit_edges(Cell::Visitor&);
|
||||
|
||||
Span<Value> registers() { return m_current_call_frame; }
|
||||
ReadonlySpan<Value> registers() const { return m_current_call_frame; }
|
||||
Vector<Value>& registers() { return vm().running_execution_context().registers; }
|
||||
Vector<Value> const& registers() const { return vm().running_execution_context().registers; }
|
||||
|
||||
private:
|
||||
void run_bytecode();
|
||||
|
||||
CallFrame& call_frame()
|
||||
{
|
||||
return m_call_frames.last().visit([](auto& x) -> CallFrame& { return *x; });
|
||||
}
|
||||
|
||||
CallFrame const& call_frame() const
|
||||
{
|
||||
return const_cast<Interpreter*>(this)->call_frame();
|
||||
}
|
||||
|
||||
void push_call_frame(Variant<NonnullOwnPtr<CallFrame>, CallFrame*>);
|
||||
[[nodiscard]] Variant<NonnullOwnPtr<CallFrame>, CallFrame*> pop_call_frame();
|
||||
|
||||
VM& m_vm;
|
||||
Vector<Variant<NonnullOwnPtr<CallFrame>, CallFrame*>> m_call_frames;
|
||||
Span<Value> m_current_call_frame;
|
||||
BasicBlock const* m_scheduled_jump { nullptr };
|
||||
GCPtr<Executable> m_current_executable { nullptr };
|
||||
BasicBlock const* m_current_block { nullptr };
|
||||
|
|
|
@ -259,7 +259,6 @@ AK::JsonObject Heap::dump_graph()
|
|||
HashMap<Cell*, HeapRoot> roots;
|
||||
gather_roots(roots);
|
||||
GraphConstructorVisitor visitor(*this, roots);
|
||||
vm().bytecode_interpreter().visit_edges(visitor);
|
||||
visitor.visit_all_cells();
|
||||
return visitor.dump();
|
||||
}
|
||||
|
@ -457,8 +456,6 @@ void Heap::mark_live_cells(HashMap<Cell*, HeapRoot> const& roots)
|
|||
|
||||
MarkingVisitor visitor(*this, roots);
|
||||
|
||||
vm().bytecode_interpreter().visit_edges(visitor);
|
||||
|
||||
visitor.mark_all_live_cells();
|
||||
|
||||
for (auto& inverse_root : m_uprooted_cells)
|
||||
|
|
|
@ -693,11 +693,11 @@ ThrowCompletionOr<Value> perform_eval(VM& vm, Value x, CallerMode strict_caller,
|
|||
executable->name = "eval"sv;
|
||||
if (Bytecode::g_dump_bytecode)
|
||||
executable->dump();
|
||||
auto result_or_error = vm.bytecode_interpreter().run_and_return_frame(*executable, nullptr);
|
||||
auto result_or_error = vm.bytecode_interpreter().run_executable(*executable, nullptr);
|
||||
if (result_or_error.value.is_error())
|
||||
return result_or_error.value.release_error();
|
||||
|
||||
auto& result = result_or_error.frame->registers()[0];
|
||||
auto& result = result_or_error.return_register_value;
|
||||
if (!result.is_empty())
|
||||
eval_result = result;
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace JS {
|
|||
|
||||
JS_DEFINE_ALLOCATOR(AsyncGenerator);
|
||||
|
||||
ThrowCompletionOr<NonnullGCPtr<AsyncGenerator>> AsyncGenerator::create(Realm& realm, Value initial_value, ECMAScriptFunctionObject* generating_function, NonnullOwnPtr<ExecutionContext> execution_context, NonnullOwnPtr<Bytecode::CallFrame> frame)
|
||||
ThrowCompletionOr<NonnullGCPtr<AsyncGenerator>> AsyncGenerator::create(Realm& realm, Value initial_value, ECMAScriptFunctionObject* generating_function, NonnullOwnPtr<ExecutionContext> execution_context)
|
||||
{
|
||||
auto& vm = realm.vm();
|
||||
// This is "g1.prototype" in figure-2 (https://tc39.es/ecma262/img/figure-2.png)
|
||||
|
@ -24,7 +24,6 @@ ThrowCompletionOr<NonnullGCPtr<AsyncGenerator>> AsyncGenerator::create(Realm& re
|
|||
auto generating_function_prototype_object = TRY(generating_function_prototype.to_object(vm));
|
||||
auto object = realm.heap().allocate<AsyncGenerator>(realm, realm, generating_function_prototype_object, move(execution_context));
|
||||
object->m_generating_function = generating_function;
|
||||
object->m_frame = move(frame);
|
||||
object->m_previous_value = initial_value;
|
||||
return object;
|
||||
}
|
||||
|
@ -45,8 +44,6 @@ void AsyncGenerator::visit_edges(Cell::Visitor& visitor)
|
|||
}
|
||||
visitor.visit(m_generating_function);
|
||||
visitor.visit(m_previous_value);
|
||||
if (m_frame)
|
||||
m_frame->visit_edges(visitor);
|
||||
visitor.visit(m_current_promise);
|
||||
m_async_generator_context->visit_edges(visitor);
|
||||
}
|
||||
|
@ -192,19 +189,9 @@ void AsyncGenerator::execute(VM& vm, Completion completion)
|
|||
|
||||
VERIFY(!m_generating_function->bytecode_executable()->basic_blocks.find_if([next_block](auto& block) { return block == next_block; }).is_end());
|
||||
|
||||
Bytecode::CallFrame* frame = nullptr;
|
||||
if (m_frame)
|
||||
frame = m_frame.ptr();
|
||||
bytecode_interpreter.accumulator() = completion_object;
|
||||
|
||||
if (frame)
|
||||
frame->registers()[0] = completion_object;
|
||||
else
|
||||
bytecode_interpreter.accumulator() = completion_object;
|
||||
|
||||
auto next_result = bytecode_interpreter.run_and_return_frame(*m_generating_function->bytecode_executable(), next_block, frame);
|
||||
|
||||
if (!m_frame)
|
||||
m_frame = move(next_result.frame);
|
||||
auto next_result = bytecode_interpreter.run_executable(*m_generating_function->bytecode_executable(), next_block);
|
||||
|
||||
auto result_value = move(next_result.value);
|
||||
if (!result_value.is_throw_completion()) {
|
||||
|
|
|
@ -28,7 +28,7 @@ public:
|
|||
Completed,
|
||||
};
|
||||
|
||||
static ThrowCompletionOr<NonnullGCPtr<AsyncGenerator>> create(Realm&, Value, ECMAScriptFunctionObject*, NonnullOwnPtr<ExecutionContext>, NonnullOwnPtr<Bytecode::CallFrame>);
|
||||
static ThrowCompletionOr<NonnullGCPtr<AsyncGenerator>> create(Realm&, Value, ECMAScriptFunctionObject*, NonnullOwnPtr<ExecutionContext>);
|
||||
|
||||
virtual ~AsyncGenerator() override = default;
|
||||
|
||||
|
@ -60,7 +60,6 @@ private:
|
|||
|
||||
GCPtr<ECMAScriptFunctionObject> m_generating_function;
|
||||
Value m_previous_value;
|
||||
OwnPtr<Bytecode::CallFrame> m_frame;
|
||||
GCPtr<Promise> m_current_promise;
|
||||
};
|
||||
|
||||
|
|
|
@ -713,11 +713,23 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
|
|||
} else if (i < execution_context_arguments.size() && !execution_context_arguments[i].is_undefined()) {
|
||||
argument_value = execution_context_arguments[i];
|
||||
} else if (parameter.default_value) {
|
||||
auto value_and_frame = vm.bytecode_interpreter().run_and_return_frame(*m_default_parameter_bytecode_executables[default_parameter_index - 1], nullptr);
|
||||
if (value_and_frame.value.is_error())
|
||||
return value_and_frame.value.release_error();
|
||||
// Resulting value is in the accumulator.
|
||||
argument_value = value_and_frame.frame->registers()[0];
|
||||
auto& running_execution_context = vm.running_execution_context();
|
||||
|
||||
// NOTE: Registers have to be saved and restored because executable created for default parameter uses
|
||||
// running execution context.
|
||||
// FIXME: This is a hack and instead instructions for default parameters should be a part of the function's bytecode.
|
||||
auto saved_registers = running_execution_context.registers;
|
||||
for (size_t register_index = 0; register_index < saved_registers.size(); ++register_index)
|
||||
saved_registers[register_index] = {};
|
||||
|
||||
auto result_and_return_register = vm.bytecode_interpreter().run_executable(*m_default_parameter_bytecode_executables[default_parameter_index - 1], nullptr);
|
||||
|
||||
for (size_t register_index = 0; register_index < saved_registers.size(); ++register_index)
|
||||
running_execution_context.registers[register_index] = saved_registers[register_index];
|
||||
|
||||
if (result_and_return_register.value.is_error())
|
||||
return result_and_return_register.value.release_error();
|
||||
argument_value = result_and_return_register.return_register_value;
|
||||
} else {
|
||||
argument_value = js_undefined();
|
||||
}
|
||||
|
@ -1103,7 +1115,7 @@ void async_block_start(VM& vm, T const& async_body, PromiseCapability const& pro
|
|||
if (maybe_executable.is_error())
|
||||
result = maybe_executable.release_error();
|
||||
else
|
||||
result = vm.bytecode_interpreter().run_and_return_frame(*maybe_executable.value(), nullptr).value;
|
||||
result = vm.bytecode_interpreter().run_executable(*maybe_executable.value(), nullptr).value;
|
||||
}
|
||||
// b. Else,
|
||||
else {
|
||||
|
@ -1230,9 +1242,8 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body()
|
|||
}
|
||||
}
|
||||
|
||||
auto result_and_frame = vm.bytecode_interpreter().run_and_return_frame(*m_bytecode_executable, nullptr);
|
||||
auto result_and_frame = vm.bytecode_interpreter().run_executable(*m_bytecode_executable, nullptr);
|
||||
|
||||
VERIFY(result_and_frame.frame != nullptr);
|
||||
if (result_and_frame.value.is_error())
|
||||
return result_and_frame.value.release_error();
|
||||
|
||||
|
@ -1244,11 +1255,11 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body()
|
|||
return { Completion::Type::Return, result.value_or(js_undefined()), {} };
|
||||
|
||||
if (m_kind == FunctionKind::AsyncGenerator) {
|
||||
auto async_generator_object = TRY(AsyncGenerator::create(realm, result, this, vm.running_execution_context().copy(), result_and_frame.frame.release_nonnull()));
|
||||
auto async_generator_object = TRY(AsyncGenerator::create(realm, result, this, vm.running_execution_context().copy()));
|
||||
return { Completion::Type::Return, async_generator_object, {} };
|
||||
}
|
||||
|
||||
auto generator_object = TRY(GeneratorObject::create(realm, result, this, vm.running_execution_context().copy(), result_and_frame.frame.release_nonnull()));
|
||||
auto generator_object = TRY(GeneratorObject::create(realm, result, this, vm.running_execution_context().copy()));
|
||||
|
||||
// NOTE: Async functions are entirely transformed to generator functions, and wrapped in a custom driver that returns a promise
|
||||
// See AwaitExpression::generate_bytecode() for the transformation.
|
||||
|
|
|
@ -43,6 +43,10 @@ NonnullOwnPtr<ExecutionContext> ExecutionContext::copy() const
|
|||
copy->executable = executable;
|
||||
copy->arguments = arguments;
|
||||
copy->locals = locals;
|
||||
copy->registers = registers;
|
||||
copy->unwind_contexts = unwind_contexts;
|
||||
copy->saved_lexical_environments = saved_lexical_environments;
|
||||
copy->previously_scheduled_jumps = previously_scheduled_jumps;
|
||||
return copy;
|
||||
}
|
||||
|
||||
|
@ -61,6 +65,11 @@ void ExecutionContext::visit_edges(Cell::Visitor& visitor)
|
|||
visitor.visit(function_name);
|
||||
visitor.visit(arguments);
|
||||
visitor.visit(locals);
|
||||
visitor.visit(registers);
|
||||
for (auto& context : unwind_contexts) {
|
||||
visitor.visit(context.lexical_environment);
|
||||
}
|
||||
visitor.visit(saved_lexical_environments);
|
||||
script_or_module.visit(
|
||||
[](Empty) {},
|
||||
[&](auto& script_or_module) {
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include <AK/DeprecatedFlyString.h>
|
||||
#include <AK/WeakPtr.h>
|
||||
#include <LibJS/Bytecode/BasicBlock.h>
|
||||
#include <LibJS/Bytecode/Instruction.h>
|
||||
#include <LibJS/Forward.h>
|
||||
#include <LibJS/Module.h>
|
||||
|
@ -75,6 +76,10 @@ public:
|
|||
|
||||
Vector<Value> arguments;
|
||||
Vector<Value> locals;
|
||||
Vector<Value> registers;
|
||||
Vector<Bytecode::UnwindInfo> unwind_contexts;
|
||||
Vector<Bytecode::BasicBlock const*> previously_scheduled_jumps;
|
||||
Vector<GCPtr<Environment>> saved_lexical_environments;
|
||||
};
|
||||
|
||||
struct StackTraceElement {
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace JS {
|
|||
|
||||
JS_DEFINE_ALLOCATOR(GeneratorObject);
|
||||
|
||||
ThrowCompletionOr<NonnullGCPtr<GeneratorObject>> GeneratorObject::create(Realm& realm, Value initial_value, ECMAScriptFunctionObject* generating_function, NonnullOwnPtr<ExecutionContext> execution_context, NonnullOwnPtr<Bytecode::CallFrame> frame)
|
||||
ThrowCompletionOr<NonnullGCPtr<GeneratorObject>> GeneratorObject::create(Realm& realm, Value initial_value, ECMAScriptFunctionObject* generating_function, NonnullOwnPtr<ExecutionContext> execution_context)
|
||||
{
|
||||
auto& vm = realm.vm();
|
||||
// This is "g1.prototype" in figure-2 (https://tc39.es/ecma262/img/figure-2.png)
|
||||
|
@ -32,7 +32,6 @@ ThrowCompletionOr<NonnullGCPtr<GeneratorObject>> GeneratorObject::create(Realm&
|
|||
auto generating_function_prototype_object = TRY(generating_function_prototype.to_object(vm));
|
||||
auto object = realm.heap().allocate<GeneratorObject>(realm, realm, generating_function_prototype_object, move(execution_context));
|
||||
object->m_generating_function = generating_function;
|
||||
object->m_frame = move(frame);
|
||||
object->m_previous_value = initial_value;
|
||||
return object;
|
||||
}
|
||||
|
@ -49,8 +48,6 @@ void GeneratorObject::visit_edges(Cell::Visitor& visitor)
|
|||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_generating_function);
|
||||
visitor.visit(m_previous_value);
|
||||
if (m_frame)
|
||||
m_frame->visit_edges(visitor);
|
||||
m_execution_context->visit_edges(visitor);
|
||||
}
|
||||
|
||||
|
@ -113,22 +110,12 @@ ThrowCompletionOr<Value> GeneratorObject::execute(VM& vm, Completion const& comp
|
|||
|
||||
VERIFY(!m_generating_function->bytecode_executable()->basic_blocks.find_if([next_block](auto& block) { return block == next_block; }).is_end());
|
||||
|
||||
Bytecode::CallFrame* frame = nullptr;
|
||||
if (m_frame)
|
||||
frame = m_frame;
|
||||
bytecode_interpreter.registers()[0] = completion_object;
|
||||
|
||||
if (frame)
|
||||
frame->registers()[0] = completion_object;
|
||||
else
|
||||
bytecode_interpreter.accumulator() = completion_object;
|
||||
|
||||
auto next_result = bytecode_interpreter.run_and_return_frame(*m_generating_function->bytecode_executable(), next_block, frame);
|
||||
auto next_result = bytecode_interpreter.run_executable(*m_generating_function->bytecode_executable(), next_block);
|
||||
|
||||
vm.pop_execution_context();
|
||||
|
||||
if (!m_frame)
|
||||
m_frame = move(next_result.frame);
|
||||
|
||||
auto result_value = move(next_result.value);
|
||||
if (result_value.is_throw_completion()) {
|
||||
// Uncaught exceptions disable the generator.
|
||||
|
|
|
@ -17,7 +17,7 @@ class GeneratorObject : public Object {
|
|||
JS_DECLARE_ALLOCATOR(GeneratorObject);
|
||||
|
||||
public:
|
||||
static ThrowCompletionOr<NonnullGCPtr<GeneratorObject>> create(Realm&, Value, ECMAScriptFunctionObject*, NonnullOwnPtr<ExecutionContext>, NonnullOwnPtr<Bytecode::CallFrame>);
|
||||
static ThrowCompletionOr<NonnullGCPtr<GeneratorObject>> create(Realm&, Value, ECMAScriptFunctionObject*, NonnullOwnPtr<ExecutionContext>);
|
||||
virtual ~GeneratorObject() override = default;
|
||||
void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
|
@ -43,7 +43,6 @@ private:
|
|||
NonnullOwnPtr<ExecutionContext> m_execution_context;
|
||||
GCPtr<ECMAScriptFunctionObject> m_generating_function;
|
||||
Value m_previous_value;
|
||||
OwnPtr<Bytecode::CallFrame> m_frame;
|
||||
GeneratorState m_generator_state { GeneratorState::SuspendedStart };
|
||||
Optional<StringView> m_generator_brand;
|
||||
};
|
||||
|
|
|
@ -180,12 +180,12 @@ ThrowCompletionOr<Value> perform_shadow_realm_eval(VM& vm, StringView source_tex
|
|||
else {
|
||||
auto executable = maybe_executable.release_value();
|
||||
|
||||
auto value_and_frame = vm.bytecode_interpreter().run_and_return_frame(*executable, nullptr);
|
||||
if (value_and_frame.value.is_error()) {
|
||||
result = value_and_frame.value.release_error();
|
||||
auto result_and_return_register = vm.bytecode_interpreter().run_executable(*executable, nullptr);
|
||||
if (result_and_return_register.value.is_error()) {
|
||||
result = result_and_return_register.value.release_error();
|
||||
} else {
|
||||
// Resulting value is in the accumulator.
|
||||
result = value_and_frame.frame->registers()[0].value_or(js_undefined());
|
||||
result = result_and_return_register.return_register_value.value_or(js_undefined());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -289,11 +289,24 @@ ThrowCompletionOr<void> VM::binding_initialization(NonnullRefPtr<BindingPattern
|
|||
|
||||
ThrowCompletionOr<Value> VM::execute_ast_node(ASTNode const& node)
|
||||
{
|
||||
// FIXME: This function should be gone once we will emit bytecode for everything before executing instructions.
|
||||
|
||||
auto executable = TRY(Bytecode::compile(*this, node, {}, FunctionKind::Normal, ""sv));
|
||||
auto result_or_error = bytecode_interpreter().run_and_return_frame(*executable, nullptr);
|
||||
auto& running_execution_context = this->running_execution_context();
|
||||
|
||||
// Registers have to be saved and restored because executable for compiled ASTNode does not have its own execution context
|
||||
auto saved_registers = running_execution_context.registers;
|
||||
for (size_t i = 0; i < saved_registers.size(); ++i)
|
||||
running_execution_context.registers[i] = {};
|
||||
|
||||
auto result_or_error = bytecode_interpreter().run_executable(*executable, nullptr);
|
||||
|
||||
for (size_t i = 0; i < saved_registers.size(); ++i)
|
||||
running_execution_context.registers[i] = saved_registers[i];
|
||||
|
||||
if (result_or_error.value.is_error())
|
||||
return result_or_error.value.release_error();
|
||||
return result_or_error.frame->registers()[0];
|
||||
return result_or_error.return_register_value;
|
||||
}
|
||||
|
||||
// 13.15.5.3 Runtime Semantics: PropertyDestructuringAssignmentEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-propertydestructuringassignmentevaluation
|
||||
|
|
|
@ -717,12 +717,12 @@ ThrowCompletionOr<void> SourceTextModule::execute_module(VM& vm, GCPtr<PromiseCa
|
|||
else {
|
||||
auto executable = maybe_executable.release_value();
|
||||
|
||||
auto value_and_frame = vm.bytecode_interpreter().run_and_return_frame(*executable, nullptr);
|
||||
if (value_and_frame.value.is_error()) {
|
||||
result = value_and_frame.value.release_error();
|
||||
auto result_and_return_register = vm.bytecode_interpreter().run_executable(*executable, nullptr);
|
||||
if (result_and_return_register.value.is_error()) {
|
||||
result = result_and_return_register.value.release_error();
|
||||
} else {
|
||||
// Resulting value is in the accumulator.
|
||||
result = value_and_frame.frame->registers()[0].value_or(js_undefined());
|
||||
result = result_and_return_register.return_register_value.value_or(js_undefined());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue