mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-06-09 09:34:57 +09:00
LibJS: Skip iteration result allocation in AsyncFunctionDriverWrapper
- Create less GC pressure by making each `await` in async function skip iteration result object allocation. - Skip uncached `Object::get()` calls to extract `value` and `done` from the iteration result object. With this change, following function goes 30% faster on my computer: ```js (async () => { const resolved = Promise.resolve(); for (let i = 0; i < 5_000_000; i++) { await resolved; } })(); ```
This commit is contained in:
parent
286a9d8101
commit
4c789ac689
Notes:
github-actions[bot]
2025-05-09 10:31:18 +00:00
Author: https://github.com/kalenikaliaksandr
Commit: 4c789ac689
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4663
7 changed files with 47 additions and 28 deletions
|
@ -131,11 +131,8 @@ void AsyncFunctionDriverWrapper::continue_async_execution(VM& vm, Value value, b
|
|||
return generator_result.throw_completion();
|
||||
|
||||
auto result = generator_result.release_value();
|
||||
VERIFY(result.is_object());
|
||||
|
||||
auto promise_value = TRY(result.get(vm, vm.names.value));
|
||||
|
||||
if (TRY(result.get(vm, vm.names.done)).to_boolean()) {
|
||||
auto promise_value = result.value;
|
||||
if (result.done) {
|
||||
// When returning a promise, we need to unwrap it.
|
||||
if (promise_value.is_object() && is<Promise>(promise_value.as_object())) {
|
||||
auto& returned_promise = static_cast<Promise&>(promise_value.as_object());
|
||||
|
|
|
@ -78,7 +78,7 @@ ThrowCompletionOr<GeneratorObject::GeneratorState> GeneratorObject::validate(VM&
|
|||
return state;
|
||||
}
|
||||
|
||||
ThrowCompletionOr<Value> GeneratorObject::execute(VM& vm, Completion const& completion)
|
||||
ThrowCompletionOr<GeneratorObject::IterationResult> GeneratorObject::execute(VM& vm, Completion const& completion)
|
||||
{
|
||||
// Loosely based on step 4 of https://tc39.es/ecma262/#sec-generatorstart mixed with https://tc39.es/ecma262/#sec-generatoryield at the end.
|
||||
|
||||
|
@ -115,24 +115,26 @@ ThrowCompletionOr<Value> GeneratorObject::execute(VM& vm, Completion const& comp
|
|||
if (result_value.is_throw_completion()) {
|
||||
// Uncaught exceptions disable the generator.
|
||||
m_generator_state = GeneratorState::Completed;
|
||||
return result_value;
|
||||
return result_value.throw_completion();
|
||||
}
|
||||
m_previous_value = result_value.release_value();
|
||||
bool done = !generated_continuation(m_previous_value).has_value();
|
||||
|
||||
m_generator_state = done ? GeneratorState::Completed : GeneratorState::SuspendedYield;
|
||||
return create_iterator_result_object(vm, generated_value(m_previous_value), done);
|
||||
|
||||
return IterationResult(generated_value(m_previous_value), done);
|
||||
}
|
||||
|
||||
// 27.5.3.3 GeneratorResume ( generator, value, generatorBrand ), https://tc39.es/ecma262/#sec-generatorresume
|
||||
ThrowCompletionOr<Value> GeneratorObject::resume(VM& vm, Value value, Optional<StringView> const& generator_brand)
|
||||
ThrowCompletionOr<GeneratorObject::IterationResult> GeneratorObject::resume(VM& vm, Value value, Optional<StringView> const& generator_brand)
|
||||
{
|
||||
// 1. Let state be ? GeneratorValidate(generator, generatorBrand).
|
||||
auto state = TRY(validate(vm, generator_brand));
|
||||
|
||||
// 2. If state is completed, return CreateIterResultObject(undefined, true).
|
||||
if (state == GeneratorState::Completed)
|
||||
return create_iterator_result_object(vm, js_undefined(), true);
|
||||
if (state == GeneratorState::Completed) {
|
||||
return IterationResult(js_undefined(), true);
|
||||
}
|
||||
|
||||
// 3. Assert: state is either suspendedStart or suspendedYield.
|
||||
VERIFY(state == GeneratorState::SuspendedStart || state == GeneratorState::SuspendedYield);
|
||||
|
@ -163,7 +165,7 @@ ThrowCompletionOr<Value> GeneratorObject::resume(VM& vm, Value value, Optional<S
|
|||
}
|
||||
|
||||
// 27.5.3.4 GeneratorResumeAbrupt ( generator, abruptCompletion, generatorBrand ), https://tc39.es/ecma262/#sec-generatorresumeabrupt
|
||||
ThrowCompletionOr<Value> GeneratorObject::resume_abrupt(JS::VM& vm, JS::Completion abrupt_completion, Optional<StringView> const& generator_brand)
|
||||
ThrowCompletionOr<GeneratorObject::IterationResult> GeneratorObject::resume_abrupt(JS::VM& vm, JS::Completion abrupt_completion, Optional<StringView> const& generator_brand)
|
||||
{
|
||||
// 1. Let state be ? GeneratorValidate(generator, generatorBrand).
|
||||
auto state = TRY(validate(vm, generator_brand));
|
||||
|
@ -185,7 +187,7 @@ ThrowCompletionOr<Value> GeneratorObject::resume_abrupt(JS::VM& vm, JS::Completi
|
|||
// a. If abruptCompletion.[[Type]] is return, then
|
||||
if (abrupt_completion.type() == Completion::Type::Return) {
|
||||
// i. Return CreateIterResultObject(abruptCompletion.[[Value]], true).
|
||||
return create_iterator_result_object(vm, abrupt_completion.value(), true);
|
||||
return IterationResult(abrupt_completion.value(), true);
|
||||
}
|
||||
|
||||
// b. Return ? abruptCompletion.
|
||||
|
|
|
@ -21,8 +21,20 @@ public:
|
|||
virtual ~GeneratorObject() override = default;
|
||||
void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
ThrowCompletionOr<Value> resume(VM&, Value value, Optional<StringView> const& generator_brand);
|
||||
ThrowCompletionOr<Value> resume_abrupt(VM&, JS::Completion abrupt_completion, Optional<StringView> const& generator_brand);
|
||||
struct IterationResult {
|
||||
IterationResult() = delete;
|
||||
explicit IterationResult(Value value, bool done)
|
||||
: done(done)
|
||||
, value(value)
|
||||
{
|
||||
}
|
||||
|
||||
bool done { false };
|
||||
Value value;
|
||||
};
|
||||
|
||||
ThrowCompletionOr<IterationResult> resume(VM&, Value value, Optional<StringView> const& generator_brand);
|
||||
ThrowCompletionOr<IterationResult> resume_abrupt(VM&, JS::Completion abrupt_completion, Optional<StringView> const& generator_brand);
|
||||
|
||||
enum class GeneratorState {
|
||||
SuspendedStart,
|
||||
|
@ -37,7 +49,7 @@ protected:
|
|||
GeneratorObject(Realm&, Object& prototype, NonnullOwnPtr<ExecutionContext>, Optional<StringView> generator_brand = {});
|
||||
|
||||
ThrowCompletionOr<GeneratorState> validate(VM&, Optional<StringView> const& generator_brand);
|
||||
virtual ThrowCompletionOr<Value> execute(VM&, JS::Completion const& completion);
|
||||
virtual ThrowCompletionOr<IterationResult> execute(VM&, JS::Completion const& completion);
|
||||
|
||||
private:
|
||||
NonnullOwnPtr<ExecutionContext> m_execution_context;
|
||||
|
|
|
@ -34,7 +34,8 @@ JS_DEFINE_NATIVE_FUNCTION(GeneratorPrototype::next)
|
|||
{
|
||||
// 1. Return ? GeneratorResume(this value, value, empty).
|
||||
auto generator_object = TRY(typed_this_object(vm));
|
||||
return generator_object->resume(vm, vm.argument(0), {});
|
||||
auto iteration_result = TRY(generator_object->resume(vm, vm.argument(0), {}));
|
||||
return create_iterator_result_object(vm, iteration_result.value, iteration_result.done);
|
||||
}
|
||||
|
||||
// 27.5.1.3 Generator.prototype.return ( value ), https://tc39.es/ecma262/#sec-generator.prototype.return
|
||||
|
@ -47,7 +48,8 @@ JS_DEFINE_NATIVE_FUNCTION(GeneratorPrototype::return_)
|
|||
auto completion = Completion(Completion::Type::Return, vm.argument(0));
|
||||
|
||||
// 3. Return ? GeneratorResumeAbrupt(g, C, empty).
|
||||
return generator_object->resume_abrupt(vm, completion, {});
|
||||
auto iteration_result = TRY(generator_object->resume_abrupt(vm, completion, {}));
|
||||
return create_iterator_result_object(vm, iteration_result.value, iteration_result.done);
|
||||
}
|
||||
|
||||
// 27.5.1.4 Generator.prototype.throw ( exception ), https://tc39.es/ecma262/#sec-generator.prototype.throw
|
||||
|
@ -60,7 +62,8 @@ JS_DEFINE_NATIVE_FUNCTION(GeneratorPrototype::throw_)
|
|||
auto completion = throw_completion(vm.argument(0));
|
||||
|
||||
// 3. Return ? GeneratorResumeAbrupt(g, C, empty).
|
||||
return generator_object->resume_abrupt(vm, completion, {});
|
||||
auto iteration_result = TRY(generator_object->resume_abrupt(vm, completion, {}));
|
||||
return create_iterator_result_object(vm, iteration_result.value, iteration_result.done);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -46,24 +46,27 @@ ThrowCompletionOr<Value> IteratorHelper::close_result(VM& vm, Completion complet
|
|||
return TRY(iterator_close(vm, underlying_iterator(), move(completion)));
|
||||
}
|
||||
|
||||
ThrowCompletionOr<Value> IteratorHelper::execute(VM& vm, JS::Completion const& completion)
|
||||
ThrowCompletionOr<GeneratorObject::IterationResult> IteratorHelper::execute(VM& vm, JS::Completion const& completion)
|
||||
{
|
||||
ScopeGuard guard { [&] { vm.pop_execution_context(); } };
|
||||
|
||||
if (completion.is_abrupt()) {
|
||||
if (m_abrupt_closure)
|
||||
return m_abrupt_closure->function()(vm, *this, completion);
|
||||
return close_result(vm, completion);
|
||||
if (m_abrupt_closure) {
|
||||
auto abrupt_result = TRY(m_abrupt_closure->function()(vm, *this, completion));
|
||||
return IterationResult(abrupt_result, true);
|
||||
}
|
||||
auto close_result = TRY(this->close_result(vm, completion));
|
||||
return IterationResult(close_result, true);
|
||||
}
|
||||
|
||||
auto result_value = m_closure->function()(vm, *this);
|
||||
|
||||
if (result_value.is_throw_completion()) {
|
||||
set_generator_state(GeneratorState::Completed);
|
||||
return result_value;
|
||||
return result_value.throw_completion();
|
||||
}
|
||||
|
||||
return create_iterator_result_object(vm, result(result_value.release_value()), generator_state() == GeneratorState::Completed);
|
||||
return IterationResult(result(result_value.release_value()), generator_state() == GeneratorState::Completed);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ private:
|
|||
IteratorHelper(Realm&, Object& prototype, GC::Ref<IteratorRecord>, GC::Ref<Closure>, GC::Ptr<AbruptClosure>);
|
||||
|
||||
virtual void visit_edges(Visitor&) override;
|
||||
virtual ThrowCompletionOr<Value> execute(VM&, JS::Completion const& completion) override;
|
||||
virtual ThrowCompletionOr<IterationResult> execute(VM&, JS::Completion const& completion) override;
|
||||
|
||||
GC::Ref<IteratorRecord> m_underlying_iterator; // [[UnderlyingIterator]]
|
||||
GC::Ref<Closure> m_closure;
|
||||
|
|
|
@ -37,7 +37,8 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorHelperPrototype::next)
|
|||
auto iterator = TRY(typed_this_object(vm));
|
||||
|
||||
// 1. Return ? GeneratorResume(this value, undefined, "Iterator Helper").
|
||||
return iterator->resume(vm, js_undefined(), "Iterator Helper"sv);
|
||||
auto iteration_result = TRY(iterator->resume(vm, js_undefined(), "Iterator Helper"sv));
|
||||
return create_iterator_result_object(vm, iteration_result.value, iteration_result.done);
|
||||
}
|
||||
|
||||
// 27.1.2.1.2 %IteratorHelperPrototype%.return ( ), https://tc39.es/ecma262/#sec-%iteratorhelperprototype%.return
|
||||
|
@ -66,7 +67,8 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorHelperPrototype::return_)
|
|||
Completion completion { Completion::Type::Return, js_undefined() };
|
||||
|
||||
// 6. Return ? GeneratorResumeAbrupt(O, C, "Iterator Helper").
|
||||
return TRY(iterator->resume_abrupt(vm, move(completion), "Iterator Helper"sv));
|
||||
auto iteration_result = TRY(iterator->resume_abrupt(vm, move(completion), "Iterator Helper"sv));
|
||||
return create_iterator_result_object(vm, iteration_result.value, iteration_result.done);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue