mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-24 21:45:20 +00:00
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:
parent
1022566bff
commit
a0bb31f7a0
Notes:
github-actions[bot]
2025-04-01 00:31:35 +00:00
Author: https://github.com/awesomekling Commit: https://github.com/LadybirdBrowser/ladybird/commit/a0bb31f7a0e Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4161
12 changed files with 259 additions and 93 deletions
|
@ -1836,9 +1836,7 @@ static ScopedOperand generate_await(
|
||||||
ScopedOperand argument,
|
ScopedOperand argument,
|
||||||
ScopedOperand received_completion,
|
ScopedOperand received_completion,
|
||||||
ScopedOperand received_completion_type,
|
ScopedOperand received_completion_type,
|
||||||
ScopedOperand received_completion_value,
|
ScopedOperand received_completion_value);
|
||||||
Bytecode::IdentifierTableIndex type_identifier,
|
|
||||||
Bytecode::IdentifierTableIndex value_identifier);
|
|
||||||
|
|
||||||
// https://tc39.es/ecma262/#sec-return-statement-runtime-semantics-evaluation
|
// https://tc39.es/ecma262/#sec-return-statement-runtime-semantics-evaluation
|
||||||
Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> ReturnStatement::generate_bytecode(Bytecode::Generator& generator, Optional<ScopedOperand>) const
|
Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> ReturnStatement::generate_bytecode(Bytecode::Generator& generator, Optional<ScopedOperand>) const
|
||||||
|
@ -1863,10 +1861,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> ReturnStatement::genera
|
||||||
auto received_completion = generator.allocate_register();
|
auto received_completion = generator.allocate_register();
|
||||||
auto received_completion_type = generator.allocate_register();
|
auto received_completion_type = generator.allocate_register();
|
||||||
auto received_completion_value = generator.allocate_register();
|
auto received_completion_value = generator.allocate_register();
|
||||||
|
return_value = generate_await(generator, *return_value, received_completion, received_completion_type, received_completion_value);
|
||||||
auto type_identifier = generator.intern_identifier("type"_fly_string);
|
|
||||||
auto value_identifier = generator.intern_identifier("value"_fly_string);
|
|
||||||
return_value = generate_await(generator, *return_value, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Return Completion Record { [[Type]]: return, [[Value]]: exprValue, [[Target]]: empty }.
|
// 4. Return Completion Record { [[Type]]: return, [[Value]]: exprValue, [[Target]]: empty }.
|
||||||
|
@ -1888,12 +1883,9 @@ static void get_received_completion_type_and_value(
|
||||||
Bytecode::Generator& generator,
|
Bytecode::Generator& generator,
|
||||||
ScopedOperand received_completion,
|
ScopedOperand received_completion,
|
||||||
ScopedOperand received_completion_type,
|
ScopedOperand received_completion_type,
|
||||||
ScopedOperand received_completion_value,
|
ScopedOperand received_completion_value)
|
||||||
Bytecode::IdentifierTableIndex type_identifier,
|
|
||||||
Bytecode::IdentifierTableIndex value_identifier)
|
|
||||||
{
|
{
|
||||||
generator.emit_get_by_id(received_completion_type, received_completion, type_identifier);
|
generator.emit<Op::GetCompletionFields>(received_completion_type, received_completion_value, received_completion);
|
||||||
generator.emit_get_by_id(received_completion_value, received_completion, value_identifier);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class AwaitBeforeYield {
|
enum class AwaitBeforeYield {
|
||||||
|
@ -1907,8 +1899,6 @@ static void generate_yield(Bytecode::Generator& generator,
|
||||||
ScopedOperand received_completion,
|
ScopedOperand received_completion,
|
||||||
ScopedOperand received_completion_type,
|
ScopedOperand received_completion_type,
|
||||||
ScopedOperand received_completion_value,
|
ScopedOperand received_completion_value,
|
||||||
Bytecode::IdentifierTableIndex type_identifier,
|
|
||||||
Bytecode::IdentifierTableIndex value_identifier,
|
|
||||||
AwaitBeforeYield await_before_yield)
|
AwaitBeforeYield await_before_yield)
|
||||||
{
|
{
|
||||||
if (!generator.is_in_async_generator_function()) {
|
if (!generator.is_in_async_generator_function()) {
|
||||||
|
@ -1917,14 +1907,14 @@ static void generate_yield(Bytecode::Generator& generator,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await_before_yield == AwaitBeforeYield::Yes)
|
if (await_before_yield == AwaitBeforeYield::Yes)
|
||||||
argument = generate_await(generator, argument, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier);
|
argument = generate_await(generator, argument, received_completion, received_completion_type, received_completion_value);
|
||||||
|
|
||||||
auto& unwrap_yield_resumption_block = generator.make_block();
|
auto& unwrap_yield_resumption_block = generator.make_block();
|
||||||
generator.emit<Bytecode::Op::Yield>(Bytecode::Label { unwrap_yield_resumption_block }, argument);
|
generator.emit<Bytecode::Op::Yield>(Bytecode::Label { unwrap_yield_resumption_block }, argument);
|
||||||
generator.switch_to_basic_block(unwrap_yield_resumption_block);
|
generator.switch_to_basic_block(unwrap_yield_resumption_block);
|
||||||
|
|
||||||
generator.emit_mov(received_completion, generator.accumulator());
|
generator.emit_mov(received_completion, generator.accumulator());
|
||||||
get_received_completion_type_and_value(generator, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier);
|
get_received_completion_type_and_value(generator, received_completion, received_completion_type, received_completion_value);
|
||||||
|
|
||||||
// 27.6.3.7 AsyncGeneratorUnwrapYieldResumption ( resumptionValue ), https://tc39.es/ecma262/#sec-asyncgeneratorunwrapyieldresumption
|
// 27.6.3.7 AsyncGeneratorUnwrapYieldResumption ( resumptionValue ), https://tc39.es/ecma262/#sec-asyncgeneratorunwrapyieldresumption
|
||||||
// 1. If resumptionValue.[[Type]] is not return, return ? resumptionValue.
|
// 1. If resumptionValue.[[Type]] is not return, return ? resumptionValue.
|
||||||
|
@ -1942,7 +1932,7 @@ static void generate_yield(Bytecode::Generator& generator,
|
||||||
generator.switch_to_basic_block(resumption_value_type_is_return_block);
|
generator.switch_to_basic_block(resumption_value_type_is_return_block);
|
||||||
|
|
||||||
// 2. Let awaited be Completion(Await(resumptionValue.[[Value]])).
|
// 2. Let awaited be Completion(Await(resumptionValue.[[Value]])).
|
||||||
generate_await(generator, received_completion_value, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier);
|
generate_await(generator, received_completion_value, received_completion, received_completion_type, received_completion_value);
|
||||||
|
|
||||||
// 3. If awaited.[[Type]] is throw, return ? awaited.
|
// 3. If awaited.[[Type]] is throw, return ? awaited.
|
||||||
auto& awaited_type_is_normal_block = generator.make_block();
|
auto& awaited_type_is_normal_block = generator.make_block();
|
||||||
|
@ -1960,12 +1950,7 @@ static void generate_yield(Bytecode::Generator& generator,
|
||||||
generator.switch_to_basic_block(awaited_type_is_normal_block);
|
generator.switch_to_basic_block(awaited_type_is_normal_block);
|
||||||
|
|
||||||
// 5. Return Completion Record { [[Type]]: return, [[Value]]: awaited.[[Value]], [[Target]]: empty }.
|
// 5. Return Completion Record { [[Type]]: return, [[Value]]: awaited.[[Value]], [[Target]]: empty }.
|
||||||
generator.emit<Bytecode::Op::PutById>(
|
generator.emit<Bytecode::Op::SetCompletionType>(received_completion, Completion::Type::Return);
|
||||||
received_completion,
|
|
||||||
type_identifier,
|
|
||||||
generator.add_constant(Value(to_underlying(Completion::Type::Return))),
|
|
||||||
Bytecode::Op::PropertyKind::KeyValue,
|
|
||||||
generator.next_property_lookup_cache());
|
|
||||||
generator.emit<Bytecode::Op::Jump>(continuation_label);
|
generator.emit<Bytecode::Op::Jump>(continuation_label);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1984,9 +1969,6 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> YieldExpression::genera
|
||||||
auto received_completion_type = generator.allocate_register();
|
auto received_completion_type = generator.allocate_register();
|
||||||
auto received_completion_value = generator.allocate_register();
|
auto received_completion_value = generator.allocate_register();
|
||||||
|
|
||||||
auto type_identifier = generator.intern_identifier("type"_fly_string);
|
|
||||||
auto value_identifier = generator.intern_identifier("value"_fly_string);
|
|
||||||
|
|
||||||
if (m_is_yield_from) {
|
if (m_is_yield_from) {
|
||||||
// 15.5.5 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-generator-function-definitions-runtime-semantics-evaluation
|
// 15.5.5 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-generator-function-definitions-runtime-semantics-evaluation
|
||||||
// 1. Let generatorKind be GetGeneratorKind().
|
// 1. Let generatorKind be GetGeneratorKind().
|
||||||
|
@ -2048,7 +2030,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> YieldExpression::genera
|
||||||
|
|
||||||
// ii. If generatorKind is async, set innerResult to ? Await(innerResult).
|
// ii. If generatorKind is async, set innerResult to ? Await(innerResult).
|
||||||
if (generator.is_in_async_generator_function()) {
|
if (generator.is_in_async_generator_function()) {
|
||||||
auto new_inner_result = generate_await(generator, inner_result, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier);
|
auto new_inner_result = generate_await(generator, inner_result, received_completion, received_completion_type, received_completion_value);
|
||||||
generator.emit_mov(inner_result, new_inner_result);
|
generator.emit_mov(inner_result, new_inner_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2096,8 +2078,6 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> YieldExpression::genera
|
||||||
received_completion,
|
received_completion,
|
||||||
received_completion_type,
|
received_completion_type,
|
||||||
received_completion_value,
|
received_completion_value,
|
||||||
type_identifier,
|
|
||||||
value_identifier,
|
|
||||||
AwaitBeforeYield::No);
|
AwaitBeforeYield::No);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2139,7 +2119,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> YieldExpression::genera
|
||||||
|
|
||||||
// 2. If generatorKind is async, set innerResult to ? Await(innerResult).
|
// 2. If generatorKind is async, set innerResult to ? Await(innerResult).
|
||||||
if (generator.is_in_async_generator_function()) {
|
if (generator.is_in_async_generator_function()) {
|
||||||
auto new_result = generate_await(generator, inner_result, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier);
|
auto new_result = generate_await(generator, inner_result, received_completion, received_completion_type, received_completion_value);
|
||||||
generator.emit_mov(inner_result, new_result);
|
generator.emit_mov(inner_result, new_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2174,7 +2154,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> YieldExpression::genera
|
||||||
// This only matters for non-async generators.
|
// This only matters for non-async generators.
|
||||||
auto yield_value = generator.allocate_register();
|
auto yield_value = generator.allocate_register();
|
||||||
generator.emit_iterator_value(yield_value, inner_result);
|
generator.emit_iterator_value(yield_value, inner_result);
|
||||||
generate_yield(generator, Bytecode::Label { continuation_block }, yield_value, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier, AwaitBeforeYield::No);
|
generate_yield(generator, Bytecode::Label { continuation_block }, yield_value, received_completion, received_completion_type, received_completion_value, AwaitBeforeYield::No);
|
||||||
}
|
}
|
||||||
|
|
||||||
generator.switch_to_basic_block(throw_method_is_undefined_block);
|
generator.switch_to_basic_block(throw_method_is_undefined_block);
|
||||||
|
@ -2219,7 +2199,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> YieldExpression::genera
|
||||||
|
|
||||||
// 1. If generatorKind is async, set received.[[Value]] to ? Await(received.[[Value]]).
|
// 1. If generatorKind is async, set received.[[Value]] to ? Await(received.[[Value]]).
|
||||||
if (generator.is_in_async_generator_function()) {
|
if (generator.is_in_async_generator_function()) {
|
||||||
generate_await(generator, received_completion_value, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier);
|
generate_await(generator, received_completion_value, received_completion, received_completion_type, received_completion_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Return ? received.
|
// 2. Return ? received.
|
||||||
|
@ -2236,7 +2216,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> YieldExpression::genera
|
||||||
|
|
||||||
// v. If generatorKind is async, set innerReturnResult to ? Await(innerReturnResult).
|
// v. If generatorKind is async, set innerReturnResult to ? Await(innerReturnResult).
|
||||||
if (generator.is_in_async_generator_function()) {
|
if (generator.is_in_async_generator_function()) {
|
||||||
auto new_value = generate_await(generator, inner_return_result, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier);
|
auto new_value = generate_await(generator, inner_return_result, received_completion, received_completion_type, received_completion_value);
|
||||||
generator.emit_mov(inner_return_result, new_value);
|
generator.emit_mov(inner_return_result, new_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2272,7 +2252,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> YieldExpression::genera
|
||||||
auto received = generator.allocate_register();
|
auto received = generator.allocate_register();
|
||||||
generator.emit_iterator_value(received, inner_return_result);
|
generator.emit_iterator_value(received, inner_return_result);
|
||||||
|
|
||||||
generate_yield(generator, Bytecode::Label { continuation_block }, received, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier, AwaitBeforeYield::No);
|
generate_yield(generator, Bytecode::Label { continuation_block }, received, received_completion, received_completion_type, received_completion_value, AwaitBeforeYield::No);
|
||||||
|
|
||||||
generator.switch_to_basic_block(continuation_block);
|
generator.switch_to_basic_block(continuation_block);
|
||||||
|
|
||||||
|
@ -2280,7 +2260,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> YieldExpression::genera
|
||||||
generator.emit_mov(Bytecode::Operand(Bytecode::Register::exception()), Bytecode::Operand(*saved_exception));
|
generator.emit_mov(Bytecode::Operand(Bytecode::Register::exception()), Bytecode::Operand(*saved_exception));
|
||||||
|
|
||||||
generator.emit_mov(received_completion, generator.accumulator());
|
generator.emit_mov(received_completion, generator.accumulator());
|
||||||
get_received_completion_type_and_value(generator, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier);
|
get_received_completion_type_and_value(generator, received_completion, received_completion_type, received_completion_value);
|
||||||
generator.emit<Bytecode::Op::Jump>(Bytecode::Label { loop_block });
|
generator.emit<Bytecode::Op::Jump>(Bytecode::Label { loop_block });
|
||||||
|
|
||||||
generator.switch_to_basic_block(loop_end_block);
|
generator.switch_to_basic_block(loop_end_block);
|
||||||
|
@ -2300,7 +2280,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> YieldExpression::genera
|
||||||
generator.emit_mov(Bytecode::Operand(*saved_exception), Bytecode::Operand(Bytecode::Register::exception()));
|
generator.emit_mov(Bytecode::Operand(*saved_exception), Bytecode::Operand(Bytecode::Register::exception()));
|
||||||
}
|
}
|
||||||
|
|
||||||
generate_yield(generator, Bytecode::Label { continuation_block }, *argument, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier, AwaitBeforeYield::Yes);
|
generate_yield(generator, Bytecode::Label { continuation_block }, *argument, received_completion, received_completion_type, received_completion_value, AwaitBeforeYield::Yes);
|
||||||
generator.switch_to_basic_block(continuation_block);
|
generator.switch_to_basic_block(continuation_block);
|
||||||
|
|
||||||
if (is_in_finalizer)
|
if (is_in_finalizer)
|
||||||
|
@ -2308,7 +2288,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> YieldExpression::genera
|
||||||
|
|
||||||
generator.emit_mov(received_completion, generator.accumulator());
|
generator.emit_mov(received_completion, generator.accumulator());
|
||||||
|
|
||||||
get_received_completion_type_and_value(generator, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier);
|
get_received_completion_type_and_value(generator, received_completion, received_completion_type, received_completion_value);
|
||||||
|
|
||||||
auto& normal_completion_continuation_block = generator.make_block();
|
auto& normal_completion_continuation_block = generator.make_block();
|
||||||
auto& throw_completion_continuation_block = generator.make_block();
|
auto& throw_completion_continuation_block = generator.make_block();
|
||||||
|
@ -2957,9 +2937,7 @@ static ScopedOperand generate_await(
|
||||||
ScopedOperand argument,
|
ScopedOperand argument,
|
||||||
ScopedOperand received_completion,
|
ScopedOperand received_completion,
|
||||||
ScopedOperand received_completion_type,
|
ScopedOperand received_completion_type,
|
||||||
ScopedOperand received_completion_value,
|
ScopedOperand received_completion_value)
|
||||||
Bytecode::IdentifierTableIndex type_identifier,
|
|
||||||
Bytecode::IdentifierTableIndex value_identifier)
|
|
||||||
{
|
{
|
||||||
VERIFY(generator.is_in_async_function());
|
VERIFY(generator.is_in_async_function());
|
||||||
|
|
||||||
|
@ -2971,7 +2949,7 @@ static ScopedOperand generate_await(
|
||||||
// It ends up there because we "return" from the Await instruction above via the synthetic
|
// It ends up there because we "return" from the Await instruction above via the synthetic
|
||||||
// generator function that actually drives async execution.
|
// generator function that actually drives async execution.
|
||||||
generator.emit_mov(received_completion, generator.accumulator());
|
generator.emit_mov(received_completion, generator.accumulator());
|
||||||
get_received_completion_type_and_value(generator, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier);
|
get_received_completion_type_and_value(generator, received_completion, received_completion_type, received_completion_value);
|
||||||
|
|
||||||
auto& normal_completion_continuation_block = generator.make_block();
|
auto& normal_completion_continuation_block = generator.make_block();
|
||||||
auto& throw_value_block = generator.make_block();
|
auto& throw_value_block = generator.make_block();
|
||||||
|
@ -3007,10 +2985,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> AwaitExpression::genera
|
||||||
|
|
||||||
generator.emit_mov(received_completion, generator.accumulator());
|
generator.emit_mov(received_completion, generator.accumulator());
|
||||||
|
|
||||||
auto type_identifier = generator.intern_identifier("type"_fly_string);
|
return generate_await(generator, argument, received_completion, received_completion_type, received_completion_value);
|
||||||
auto value_identifier = generator.intern_identifier("value"_fly_string);
|
|
||||||
|
|
||||||
return generate_await(generator, argument, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> WithStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional<ScopedOperand> preferred_dst) const
|
Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> WithStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional<ScopedOperand> preferred_dst) const
|
||||||
|
@ -3204,11 +3179,8 @@ static Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> for_in_of_body_e
|
||||||
auto received_completion_type = generator.allocate_register();
|
auto received_completion_type = generator.allocate_register();
|
||||||
auto received_completion_value = generator.allocate_register();
|
auto received_completion_value = generator.allocate_register();
|
||||||
|
|
||||||
auto type_identifier = generator.intern_identifier("type"_fly_string);
|
|
||||||
auto value_identifier = generator.intern_identifier("value"_fly_string);
|
|
||||||
|
|
||||||
generator.emit_mov(received_completion, generator.accumulator());
|
generator.emit_mov(received_completion, generator.accumulator());
|
||||||
auto new_result = generate_await(generator, next_result, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier);
|
auto new_result = generate_await(generator, next_result, received_completion, received_completion_type, received_completion_value);
|
||||||
generator.emit_mov(next_result, new_result);
|
generator.emit_mov(next_result, new_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,7 @@
|
||||||
O(GetByValue) \
|
O(GetByValue) \
|
||||||
O(GetByValueWithThis) \
|
O(GetByValueWithThis) \
|
||||||
O(GetCalleeAndThisFromEnvironment) \
|
O(GetCalleeAndThisFromEnvironment) \
|
||||||
|
O(GetCompletionFields) \
|
||||||
O(GetGlobal) \
|
O(GetGlobal) \
|
||||||
O(GetImportMeta) \
|
O(GetImportMeta) \
|
||||||
O(GetIterator) \
|
O(GetIterator) \
|
||||||
|
@ -131,6 +132,7 @@
|
||||||
O(RightShift) \
|
O(RightShift) \
|
||||||
O(ScheduleJump) \
|
O(ScheduleJump) \
|
||||||
O(SetArgument) \
|
O(SetArgument) \
|
||||||
|
O(SetCompletionType) \
|
||||||
O(SetLexicalBinding) \
|
O(SetLexicalBinding) \
|
||||||
O(SetVariableBinding) \
|
O(SetVariableBinding) \
|
||||||
O(StrictlyEquals) \
|
O(StrictlyEquals) \
|
||||||
|
|
|
@ -18,10 +18,12 @@
|
||||||
#include <LibJS/Runtime/Accessor.h>
|
#include <LibJS/Runtime/Accessor.h>
|
||||||
#include <LibJS/Runtime/Array.h>
|
#include <LibJS/Runtime/Array.h>
|
||||||
#include <LibJS/Runtime/BigInt.h>
|
#include <LibJS/Runtime/BigInt.h>
|
||||||
|
#include <LibJS/Runtime/CompletionCell.h>
|
||||||
#include <LibJS/Runtime/DeclarativeEnvironment.h>
|
#include <LibJS/Runtime/DeclarativeEnvironment.h>
|
||||||
#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
|
#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
|
||||||
#include <LibJS/Runtime/Environment.h>
|
#include <LibJS/Runtime/Environment.h>
|
||||||
#include <LibJS/Runtime/FunctionEnvironment.h>
|
#include <LibJS/Runtime/FunctionEnvironment.h>
|
||||||
|
#include <LibJS/Runtime/GeneratorResult.h>
|
||||||
#include <LibJS/Runtime/GlobalEnvironment.h>
|
#include <LibJS/Runtime/GlobalEnvironment.h>
|
||||||
#include <LibJS/Runtime/GlobalObject.h>
|
#include <LibJS/Runtime/GlobalObject.h>
|
||||||
#include <LibJS/Runtime/Iterator.h>
|
#include <LibJS/Runtime/Iterator.h>
|
||||||
|
@ -192,18 +194,10 @@ ALWAYS_INLINE void Interpreter::set(Operand op, Value value)
|
||||||
|
|
||||||
ALWAYS_INLINE Value Interpreter::do_yield(Value value, Optional<Label> continuation)
|
ALWAYS_INLINE Value Interpreter::do_yield(Value value, Optional<Label> continuation)
|
||||||
{
|
{
|
||||||
auto object = Object::create(realm(), nullptr);
|
// FIXME: If we get a pointer, which is not accurately representable as a double
|
||||||
object->define_direct_property(m_vm.names.result, value, JS::default_attributes);
|
// will cause this to explode
|
||||||
|
auto continuation_value = continuation.has_value() ? Value(continuation->address()) : js_null();
|
||||||
if (continuation.has_value())
|
return vm().heap().allocate<GeneratorResult>(value, continuation_value, false).ptr();
|
||||||
// FIXME: If we get a pointer, which is not accurately representable as a double
|
|
||||||
// will cause this to explode
|
|
||||||
object->define_direct_property(m_vm.names.continuation, Value(continuation->address()), JS::default_attributes);
|
|
||||||
else
|
|
||||||
object->define_direct_property(m_vm.names.continuation, js_null(), JS::default_attributes);
|
|
||||||
|
|
||||||
object->define_direct_property(m_vm.names.isAwait, Value(false), JS::default_attributes);
|
|
||||||
return object;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 16.1.6 ScriptEvaluation ( scriptRecord ), https://tc39.es/ecma262/#sec-runtime-semantics-scriptevaluation
|
// 16.1.6 ScriptEvaluation ( scriptRecord ), https://tc39.es/ecma262/#sec-runtime-semantics-scriptevaluation
|
||||||
|
@ -624,6 +618,7 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point)
|
||||||
HANDLE_INSTRUCTION(GetByValue);
|
HANDLE_INSTRUCTION(GetByValue);
|
||||||
HANDLE_INSTRUCTION(GetByValueWithThis);
|
HANDLE_INSTRUCTION(GetByValueWithThis);
|
||||||
HANDLE_INSTRUCTION(GetCalleeAndThisFromEnvironment);
|
HANDLE_INSTRUCTION(GetCalleeAndThisFromEnvironment);
|
||||||
|
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(GetCompletionFields);
|
||||||
HANDLE_INSTRUCTION(GetGlobal);
|
HANDLE_INSTRUCTION(GetGlobal);
|
||||||
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(GetImportMeta);
|
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(GetImportMeta);
|
||||||
HANDLE_INSTRUCTION(GetIterator);
|
HANDLE_INSTRUCTION(GetIterator);
|
||||||
|
@ -680,6 +675,7 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point)
|
||||||
HANDLE_INSTRUCTION(ResolveThisBinding);
|
HANDLE_INSTRUCTION(ResolveThisBinding);
|
||||||
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(RestoreScheduledJump);
|
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(RestoreScheduledJump);
|
||||||
HANDLE_INSTRUCTION(RightShift);
|
HANDLE_INSTRUCTION(RightShift);
|
||||||
|
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(SetCompletionType);
|
||||||
HANDLE_INSTRUCTION(SetLexicalBinding);
|
HANDLE_INSTRUCTION(SetLexicalBinding);
|
||||||
HANDLE_INSTRUCTION(SetVariableBinding);
|
HANDLE_INSTRUCTION(SetVariableBinding);
|
||||||
HANDLE_INSTRUCTION(StrictlyEquals);
|
HANDLE_INSTRUCTION(StrictlyEquals);
|
||||||
|
@ -2840,15 +2836,12 @@ void PrepareYield::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||||
|
|
||||||
void Await::execute_impl(Bytecode::Interpreter& interpreter) const
|
void Await::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||||
{
|
{
|
||||||
auto& vm = interpreter.vm();
|
|
||||||
auto yielded_value = interpreter.get(m_argument).value_or(js_undefined());
|
auto yielded_value = interpreter.get(m_argument).value_or(js_undefined());
|
||||||
auto object = Object::create(interpreter.realm(), nullptr);
|
|
||||||
object->define_direct_property(vm.names.result, yielded_value, JS::default_attributes);
|
|
||||||
// FIXME: If we get a pointer, which is not accurately representable as a double
|
// FIXME: If we get a pointer, which is not accurately representable as a double
|
||||||
// will cause this to explode
|
// will cause this to explode
|
||||||
object->define_direct_property(vm.names.continuation, Value(m_continuation_label.address()), JS::default_attributes);
|
auto continuation_value = Value(m_continuation_label.address());
|
||||||
object->define_direct_property(vm.names.isAwait, Value(true), JS::default_attributes);
|
auto result = interpreter.vm().heap().allocate<GeneratorResult>(yielded_value, continuation_value, true);
|
||||||
interpreter.do_return(object);
|
interpreter.do_return(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
ThrowCompletionOr<void> GetByValue::execute_impl(Bytecode::Interpreter& interpreter) const
|
ThrowCompletionOr<void> GetByValue::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||||
|
@ -3836,4 +3829,33 @@ ByteString Dump::to_byte_string_impl(Bytecode::Executable const& executable) con
|
||||||
format_operand("value"sv, m_value, executable));
|
format_operand("value"sv, m_value, executable));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GetCompletionFields::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||||
|
{
|
||||||
|
auto const& completion_cell = static_cast<CompletionCell const&>(interpreter.get(m_completion).as_cell());
|
||||||
|
interpreter.set(m_value_dst, completion_cell.completion().value().value_or(js_undefined()));
|
||||||
|
interpreter.set(m_type_dst, Value(to_underlying(completion_cell.completion().type())));
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteString GetCompletionFields::to_byte_string_impl(Bytecode::Executable const& executable) const
|
||||||
|
{
|
||||||
|
return ByteString::formatted("GetCompletionFields {}, {}, {}",
|
||||||
|
format_operand("value_dst"sv, m_value_dst, executable),
|
||||||
|
format_operand("type_dst"sv, m_type_dst, executable),
|
||||||
|
format_operand("completion"sv, m_completion, executable));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetCompletionType::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||||
|
{
|
||||||
|
auto& completion_cell = static_cast<CompletionCell&>(interpreter.get(m_completion).as_cell());
|
||||||
|
auto completion = completion_cell.completion();
|
||||||
|
completion_cell.set_completion(Completion { completion.type(), completion.value() });
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteString SetCompletionType::to_byte_string_impl(Bytecode::Executable const& executable) const
|
||||||
|
{
|
||||||
|
return ByteString::formatted("SetCompletionType {}, type={}",
|
||||||
|
format_operand("completion"sv, m_completion, executable),
|
||||||
|
to_underlying(m_type));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -962,6 +962,58 @@ private:
|
||||||
u32 m_cache_index { 0 };
|
u32 m_cache_index { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class GetCompletionFields final : public Instruction {
|
||||||
|
public:
|
||||||
|
GetCompletionFields(Operand type_dst, Operand value_dst, Operand completion)
|
||||||
|
: Instruction(Type::GetCompletionFields)
|
||||||
|
, m_type_dst(type_dst)
|
||||||
|
, m_value_dst(value_dst)
|
||||||
|
, m_completion(completion)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void execute_impl(Bytecode::Interpreter&) const;
|
||||||
|
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
|
||||||
|
void visit_operands_impl(Function<void(Operand&)> visitor)
|
||||||
|
{
|
||||||
|
visitor(m_type_dst);
|
||||||
|
visitor(m_value_dst);
|
||||||
|
visitor(m_completion);
|
||||||
|
}
|
||||||
|
|
||||||
|
Operand type_dst() const { return m_type_dst; }
|
||||||
|
Operand value_dst() const { return m_value_dst; }
|
||||||
|
Operand completion() const { return m_completion; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Operand m_type_dst;
|
||||||
|
Operand m_value_dst;
|
||||||
|
Operand m_completion;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SetCompletionType final : public Instruction {
|
||||||
|
public:
|
||||||
|
SetCompletionType(Operand completion, Completion::Type type)
|
||||||
|
: Instruction(Type::SetCompletionType)
|
||||||
|
, m_completion(completion)
|
||||||
|
, m_type(type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void execute_impl(Bytecode::Interpreter&) const;
|
||||||
|
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
|
||||||
|
void visit_operands_impl(Function<void(Operand&)> visitor)
|
||||||
|
{
|
||||||
|
visitor(m_completion);
|
||||||
|
}
|
||||||
|
|
||||||
|
Operand completion() const { return m_completion; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Operand m_completion;
|
||||||
|
Completion::Type m_type;
|
||||||
|
};
|
||||||
|
|
||||||
class GetByIdWithThis final : public Instruction {
|
class GetByIdWithThis final : public Instruction {
|
||||||
public:
|
public:
|
||||||
GetByIdWithThis(Operand dst, Operand base, IdentifierTableIndex property, Operand this_value, u32 cache_index)
|
GetByIdWithThis(Operand dst, Operand base, IdentifierTableIndex property, Operand this_value, u32 cache_index)
|
||||||
|
|
|
@ -64,6 +64,7 @@ set(SOURCES
|
||||||
Runtime/BooleanPrototype.cpp
|
Runtime/BooleanPrototype.cpp
|
||||||
Runtime/BoundFunction.cpp
|
Runtime/BoundFunction.cpp
|
||||||
Runtime/Completion.cpp
|
Runtime/Completion.cpp
|
||||||
|
Runtime/CompletionCell.cpp
|
||||||
Runtime/ConsoleObjectPrototype.cpp
|
Runtime/ConsoleObjectPrototype.cpp
|
||||||
Runtime/ConsoleObject.cpp
|
Runtime/ConsoleObject.cpp
|
||||||
Runtime/DataView.cpp
|
Runtime/DataView.cpp
|
||||||
|
@ -94,6 +95,7 @@ set(SOURCES
|
||||||
Runtime/GeneratorFunctionPrototype.cpp
|
Runtime/GeneratorFunctionPrototype.cpp
|
||||||
Runtime/GeneratorObject.cpp
|
Runtime/GeneratorObject.cpp
|
||||||
Runtime/GeneratorPrototype.cpp
|
Runtime/GeneratorPrototype.cpp
|
||||||
|
Runtime/GeneratorResult.cpp
|
||||||
Runtime/GlobalEnvironment.cpp
|
Runtime/GlobalEnvironment.cpp
|
||||||
Runtime/GlobalObject.cpp
|
Runtime/GlobalObject.cpp
|
||||||
Runtime/IndexedProperties.cpp
|
Runtime/IndexedProperties.cpp
|
||||||
|
|
|
@ -8,7 +8,9 @@
|
||||||
#include <LibJS/Runtime/AsyncGenerator.h>
|
#include <LibJS/Runtime/AsyncGenerator.h>
|
||||||
#include <LibJS/Runtime/AsyncGeneratorPrototype.h>
|
#include <LibJS/Runtime/AsyncGeneratorPrototype.h>
|
||||||
#include <LibJS/Runtime/AsyncGeneratorRequest.h>
|
#include <LibJS/Runtime/AsyncGeneratorRequest.h>
|
||||||
|
#include <LibJS/Runtime/CompletionCell.h>
|
||||||
#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
|
#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
|
||||||
|
#include <LibJS/Runtime/GeneratorResult.h>
|
||||||
#include <LibJS/Runtime/GlobalObject.h>
|
#include <LibJS/Runtime/GlobalObject.h>
|
||||||
#include <LibJS/Runtime/PromiseConstructor.h>
|
#include <LibJS/Runtime/PromiseConstructor.h>
|
||||||
|
|
||||||
|
@ -155,15 +157,15 @@ void AsyncGenerator::execute(VM& vm, Completion completion)
|
||||||
// Loosely based on step 4 of https://tc39.es/ecma262/#sec-asyncgeneratorstart
|
// Loosely based on step 4 of https://tc39.es/ecma262/#sec-asyncgeneratorstart
|
||||||
VERIFY(completion.value().has_value());
|
VERIFY(completion.value().has_value());
|
||||||
|
|
||||||
auto generated_value = [&vm](Value value) -> Value {
|
auto generated_value = [](Value value) -> Value {
|
||||||
if (value.is_object())
|
if (value.is_cell())
|
||||||
return value.as_object().get_without_side_effects(vm.names.result);
|
return static_cast<GeneratorResult const&>(value.as_cell()).result();
|
||||||
return value.is_empty() ? js_undefined() : value;
|
return value.is_empty() ? js_undefined() : value;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto generated_continuation = [&](Value value) -> Optional<size_t> {
|
auto generated_continuation = [&](Value value) -> Optional<size_t> {
|
||||||
if (value.is_object()) {
|
if (value.is_cell()) {
|
||||||
auto number_value = value.as_object().get_without_side_effects(vm.names.continuation);
|
auto number_value = static_cast<GeneratorResult const&>(value.as_cell()).continuation();
|
||||||
if (number_value.is_null())
|
if (number_value.is_null())
|
||||||
return {};
|
return {};
|
||||||
return static_cast<size_t>(number_value.as_double());
|
return static_cast<size_t>(number_value.as_double());
|
||||||
|
@ -171,16 +173,13 @@ void AsyncGenerator::execute(VM& vm, Completion completion)
|
||||||
return {};
|
return {};
|
||||||
};
|
};
|
||||||
|
|
||||||
auto generated_is_await = [&vm](Value value) -> bool {
|
auto generated_is_await = [](Value value) -> bool {
|
||||||
if (value.is_object())
|
if (value.is_cell())
|
||||||
return value.as_object().get_without_side_effects(vm.names.isAwait).as_bool();
|
return static_cast<GeneratorResult const&>(value.as_cell()).is_await();
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto& realm = *vm.current_realm();
|
auto completion_cell = heap().allocate<CompletionCell>(completion);
|
||||||
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& bytecode_interpreter = vm.bytecode_interpreter();
|
auto& bytecode_interpreter = vm.bytecode_interpreter();
|
||||||
|
|
||||||
|
@ -189,7 +188,7 @@ void AsyncGenerator::execute(VM& vm, Completion completion)
|
||||||
// We should never enter `execute` again after the generator is complete.
|
// We should never enter `execute` again after the generator is complete.
|
||||||
VERIFY(continuation_address.has_value());
|
VERIFY(continuation_address.has_value());
|
||||||
|
|
||||||
auto next_result = bytecode_interpreter.run_executable(*m_generating_function->bytecode_executable(), continuation_address, completion_object);
|
auto next_result = bytecode_interpreter.run_executable(*m_generating_function->bytecode_executable(), continuation_address, completion_cell);
|
||||||
|
|
||||||
auto result_value = move(next_result.value);
|
auto result_value = move(next_result.value);
|
||||||
if (!result_value.is_throw_completion()) {
|
if (!result_value.is_throw_completion()) {
|
||||||
|
|
|
@ -91,7 +91,6 @@ namespace JS {
|
||||||
P(construct) \
|
P(construct) \
|
||||||
P(constructor) \
|
P(constructor) \
|
||||||
P(containing) \
|
P(containing) \
|
||||||
P(continuation) \
|
|
||||||
P(copyWithin) \
|
P(copyWithin) \
|
||||||
P(cos) \
|
P(cos) \
|
||||||
P(cosh) \
|
P(cosh) \
|
||||||
|
@ -277,7 +276,6 @@ namespace JS {
|
||||||
P(Intl) \
|
P(Intl) \
|
||||||
P(is) \
|
P(is) \
|
||||||
P(isArray) \
|
P(isArray) \
|
||||||
P(isAwait) \
|
|
||||||
P(isDisjointFrom) \
|
P(isDisjointFrom) \
|
||||||
P(isError) \
|
P(isError) \
|
||||||
P(isExtensible) \
|
P(isExtensible) \
|
||||||
|
@ -409,7 +407,6 @@ namespace JS {
|
||||||
P(reason) \
|
P(reason) \
|
||||||
P(reduce) \
|
P(reduce) \
|
||||||
P(reduceRight) \
|
P(reduceRight) \
|
||||||
P(result) \
|
|
||||||
P(Reflect) \
|
P(Reflect) \
|
||||||
P(RegExp) \
|
P(RegExp) \
|
||||||
P(region) \
|
P(region) \
|
||||||
|
|
22
Libraries/LibJS/Runtime/CompletionCell.cpp
Normal file
22
Libraries/LibJS/Runtime/CompletionCell.cpp
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2025, Andreas Kling <andreas@ladybird.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <LibJS/Runtime/CompletionCell.h>
|
||||||
|
|
||||||
|
namespace JS {
|
||||||
|
|
||||||
|
GC_DEFINE_ALLOCATOR(CompletionCell);
|
||||||
|
|
||||||
|
CompletionCell::~CompletionCell() = default;
|
||||||
|
|
||||||
|
void CompletionCell::visit_edges(Cell::Visitor& visitor)
|
||||||
|
{
|
||||||
|
Base::visit_edges(visitor);
|
||||||
|
if (m_completion.value().has_value())
|
||||||
|
visitor.visit(m_completion.value().value());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
36
Libraries/LibJS/Runtime/CompletionCell.h
Normal file
36
Libraries/LibJS/Runtime/CompletionCell.h
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2025, Andreas Kling <andreas@ladybird.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <LibGC/CellAllocator.h>
|
||||||
|
#include <LibJS/Heap/Cell.h>
|
||||||
|
#include <LibJS/Runtime/Completion.h>
|
||||||
|
|
||||||
|
namespace JS {
|
||||||
|
|
||||||
|
class CompletionCell final : public Cell {
|
||||||
|
GC_CELL(CompletionCell, Cell);
|
||||||
|
GC_DECLARE_ALLOCATOR(CompletionCell);
|
||||||
|
|
||||||
|
public:
|
||||||
|
CompletionCell(Completion completion)
|
||||||
|
: m_completion(move(completion))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~CompletionCell() override;
|
||||||
|
|
||||||
|
[[nodiscard]] Completion const& completion() const { return m_completion; }
|
||||||
|
void set_completion(Completion completion) { m_completion = move(completion); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void visit_edges(Cell::Visitor& visitor) override;
|
||||||
|
|
||||||
|
Completion m_completion;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -7,8 +7,10 @@
|
||||||
#include <AK/TemporaryChange.h>
|
#include <AK/TemporaryChange.h>
|
||||||
#include <LibJS/Bytecode/Generator.h>
|
#include <LibJS/Bytecode/Generator.h>
|
||||||
#include <LibJS/Bytecode/Interpreter.h>
|
#include <LibJS/Bytecode/Interpreter.h>
|
||||||
|
#include <LibJS/Runtime/CompletionCell.h>
|
||||||
#include <LibJS/Runtime/GeneratorObject.h>
|
#include <LibJS/Runtime/GeneratorObject.h>
|
||||||
#include <LibJS/Runtime/GeneratorPrototype.h>
|
#include <LibJS/Runtime/GeneratorPrototype.h>
|
||||||
|
#include <LibJS/Runtime/GeneratorResult.h>
|
||||||
#include <LibJS/Runtime/GlobalObject.h>
|
#include <LibJS/Runtime/GlobalObject.h>
|
||||||
#include <LibJS/Runtime/Iterator.h>
|
#include <LibJS/Runtime/Iterator.h>
|
||||||
|
|
||||||
|
@ -82,15 +84,15 @@ ThrowCompletionOr<Value> GeneratorObject::execute(VM& vm, Completion const& comp
|
||||||
|
|
||||||
VERIFY(completion.value().has_value());
|
VERIFY(completion.value().has_value());
|
||||||
|
|
||||||
auto generated_value = [&vm](Value value) -> Value {
|
auto generated_value = [](Value value) -> Value {
|
||||||
if (value.is_object())
|
if (value.is_cell())
|
||||||
return value.as_object().get_without_side_effects(vm.names.result);
|
return static_cast<GeneratorResult const&>(value.as_cell()).result();
|
||||||
return value.is_empty() ? js_undefined() : value;
|
return value.is_empty() ? js_undefined() : value;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto generated_continuation = [&](Value value) -> Optional<size_t> {
|
auto generated_continuation = [&](Value value) -> Optional<size_t> {
|
||||||
if (value.is_object()) {
|
if (value.is_cell()) {
|
||||||
auto number_value = value.as_object().get_without_side_effects(vm.names.continuation);
|
auto number_value = static_cast<GeneratorResult const&>(value.as_cell()).continuation();
|
||||||
if (number_value.is_null())
|
if (number_value.is_null())
|
||||||
return {};
|
return {};
|
||||||
return static_cast<u64>(number_value.as_double());
|
return static_cast<u64>(number_value.as_double());
|
||||||
|
@ -98,10 +100,7 @@ ThrowCompletionOr<Value> GeneratorObject::execute(VM& vm, Completion const& comp
|
||||||
return {};
|
return {};
|
||||||
};
|
};
|
||||||
|
|
||||||
auto& realm = *vm.current_realm();
|
auto compleion_cell = heap().allocate<CompletionCell>(completion);
|
||||||
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& bytecode_interpreter = vm.bytecode_interpreter();
|
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.
|
// We should never enter `execute` again after the generator is complete.
|
||||||
VERIFY(next_block.has_value());
|
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();
|
vm.pop_execution_context();
|
||||||
|
|
||||||
|
|
22
Libraries/LibJS/Runtime/GeneratorResult.cpp
Normal file
22
Libraries/LibJS/Runtime/GeneratorResult.cpp
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2025, Andreas Kling <andreas@ladybird.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <LibJS/Runtime/GeneratorResult.h>
|
||||||
|
|
||||||
|
namespace JS {
|
||||||
|
|
||||||
|
GC_DEFINE_ALLOCATOR(GeneratorResult);
|
||||||
|
|
||||||
|
GeneratorResult::~GeneratorResult() = default;
|
||||||
|
|
||||||
|
void GeneratorResult::visit_edges(Cell::Visitor& visitor)
|
||||||
|
{
|
||||||
|
Base::visit_edges(visitor);
|
||||||
|
visitor.visit(m_result);
|
||||||
|
visitor.visit(m_continuation);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
41
Libraries/LibJS/Runtime/GeneratorResult.h
Normal file
41
Libraries/LibJS/Runtime/GeneratorResult.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2025, Andreas Kling <andreas@ladybird.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <LibGC/CellAllocator.h>
|
||||||
|
#include <LibJS/Heap/Cell.h>
|
||||||
|
#include <LibJS/Runtime/Value.h>
|
||||||
|
|
||||||
|
namespace JS {
|
||||||
|
|
||||||
|
class GeneratorResult final : public Cell {
|
||||||
|
GC_CELL(GeneratorResult, Cell);
|
||||||
|
GC_DECLARE_ALLOCATOR(GeneratorResult);
|
||||||
|
|
||||||
|
public:
|
||||||
|
GeneratorResult(Value result, Value continuation, bool is_await)
|
||||||
|
: m_is_await(is_await)
|
||||||
|
, m_result(result)
|
||||||
|
, m_continuation(continuation)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~GeneratorResult() override;
|
||||||
|
|
||||||
|
[[nodiscard]] Value result() const { return m_result; }
|
||||||
|
[[nodiscard]] Value continuation() const { return m_continuation; }
|
||||||
|
[[nodiscard]] bool is_await() const { return m_is_await; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void visit_edges(Cell::Visitor& visitor) override;
|
||||||
|
|
||||||
|
bool m_is_await { false };
|
||||||
|
Value m_result;
|
||||||
|
Value m_continuation;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue