LibJS: Make async functions & generators faster with helper types

Instead of returning internal generator results as ordinary JS::Objects
with properties, we now use GeneratorResult and CompletionCell which
both inherit from Cell directly and allow efficient access to state.

1.59x speedup on JetStream3/lazy-collections.js :^)
This commit is contained in:
Andreas Kling 2025-03-31 09:32:39 +01:00 committed by Alexander Kalenik
parent 1022566bff
commit a0bb31f7a0
Notes: github-actions[bot] 2025-04-01 00:31:35 +00:00
12 changed files with 259 additions and 93 deletions

View file

@ -7,8 +7,10 @@
#include <AK/TemporaryChange.h>
#include <LibJS/Bytecode/Generator.h>
#include <LibJS/Bytecode/Interpreter.h>
#include <LibJS/Runtime/CompletionCell.h>
#include <LibJS/Runtime/GeneratorObject.h>
#include <LibJS/Runtime/GeneratorPrototype.h>
#include <LibJS/Runtime/GeneratorResult.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Iterator.h>
@ -82,15 +84,15 @@ ThrowCompletionOr<Value> GeneratorObject::execute(VM& vm, Completion const& comp
VERIFY(completion.value().has_value());
auto generated_value = [&vm](Value value) -> Value {
if (value.is_object())
return value.as_object().get_without_side_effects(vm.names.result);
auto generated_value = [](Value value) -> Value {
if (value.is_cell())
return static_cast<GeneratorResult const&>(value.as_cell()).result();
return value.is_empty() ? js_undefined() : value;
};
auto generated_continuation = [&](Value value) -> Optional<size_t> {
if (value.is_object()) {
auto number_value = value.as_object().get_without_side_effects(vm.names.continuation);
if (value.is_cell()) {
auto number_value = static_cast<GeneratorResult const&>(value.as_cell()).continuation();
if (number_value.is_null())
return {};
return static_cast<u64>(number_value.as_double());
@ -98,10 +100,7 @@ ThrowCompletionOr<Value> GeneratorObject::execute(VM& vm, Completion const& comp
return {};
};
auto& realm = *vm.current_realm();
auto completion_object = Object::create(realm, nullptr);
completion_object->define_direct_property(vm.names.type, Value(to_underlying(completion.type())), default_attributes);
completion_object->define_direct_property(vm.names.value, completion.value().value(), default_attributes);
auto compleion_cell = heap().allocate<CompletionCell>(completion);
auto& bytecode_interpreter = vm.bytecode_interpreter();
@ -110,7 +109,7 @@ ThrowCompletionOr<Value> GeneratorObject::execute(VM& vm, Completion const& comp
// We should never enter `execute` again after the generator is complete.
VERIFY(next_block.has_value());
auto next_result = bytecode_interpreter.run_executable(*m_generating_function->bytecode_executable(), next_block, completion_object);
auto next_result = bytecode_interpreter.run_executable(*m_generating_function->bytecode_executable(), next_block, compleion_cell);
vm.pop_execution_context();