LibJS: Make Completion.[[Value]] non-optional

Instead, just use js_undefined() whenever the [[Value]] field is unused.
This avoids a whole bunch of presence checks.
This commit is contained in:
Andreas Kling 2025-04-04 18:11:45 +02:00 committed by Andreas Kling
commit de424d6879
Notes: github-actions[bot] 2025-04-05 09:21:48 +00:00
65 changed files with 225 additions and 250 deletions

View file

@ -194,7 +194,7 @@ ThrowCompletionOr<ClassElement::ClassValue> ClassMethod::class_element_evaluatio
VERIFY_NOT_REACHED();
}
return ClassValue { normal_completion({}) };
return ClassValue { normal_completion(js_undefined()) };
} else {
auto& private_name = property_key_or_private_name.get<PrivateName>();
switch (kind()) {
@ -417,8 +417,7 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> ClassExpression::create_class_const
instance_fields.append(move(*class_field_definition_ptr));
} else if (element->class_element_kind() == ClassElement::ElementKind::StaticInitializer) {
// We use Completion to hold the ClassStaticBlockDefinition Record.
VERIFY(element_value.has<Completion>() && element_value.get<Completion>().value().has_value());
auto& element_object = element_value.get<Completion>().value()->as_object();
auto& element_object = element_value.get<Completion>().value().as_object();
VERIFY(is<ECMAScriptFunctionObject>(element_object));
static_elements.append(GC::Ref { static_cast<ECMAScriptFunctionObject&>(element_object) });
}

View file

@ -244,7 +244,7 @@ ThrowCompletionOr<Value> Interpreter::run(Script& script_record, GC::Ptr<Environ
// 12. Let result be Completion(GlobalDeclarationInstantiation(script, globalEnv)).
auto instantiation_result = script.global_declaration_instantiation(vm, global_environment);
Completion result = instantiation_result.is_throw_completion() ? instantiation_result.throw_completion() : normal_completion({});
Completion result = instantiation_result.is_throw_completion() ? instantiation_result.throw_completion() : normal_completion(js_undefined());
// 13. If result.[[Type]] is normal, then
if (result.type() == Completion::Type::Normal) {
@ -269,11 +269,11 @@ ThrowCompletionOr<Value> Interpreter::run(Script& script_record, GC::Ptr<Environ
if (result_or_error.value.is_error())
result = result_or_error.value.release_error();
else
result = result_or_error.return_register_value;
result = result_or_error.return_register_value.value_or(js_undefined());
}
// b. If result is a normal completion and result.[[Value]] is empty, then
if (result.type() == Completion::Type::Normal && !result.value().has_value()) {
if (result.type() == Completion::Type::Normal && result.value().is_empty()) {
// i. Set result to NormalCompletion(undefined).
result = normal_completion(js_undefined());
}
@ -300,11 +300,12 @@ ThrowCompletionOr<Value> Interpreter::run(Script& script_record, GC::Ptr<Environ
// 17. Return ? result.
if (result.is_abrupt()) {
VERIFY(result.type() == Completion::Type::Throw);
return result.release_error();
auto error = result.release_error();
VERIFY(!error.value().is_empty());
return error;
}
VERIFY(result.value().has_value());
return *result.value();
return result.value();
}
ThrowCompletionOr<Value> Interpreter::run(SourceTextModule& module)
@ -359,7 +360,7 @@ Interpreter::HandleExceptionResponse Interpreter::handle_exception(size_t& progr
FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point)
{
if (vm().did_reach_stack_space_limit()) {
reg(Register::exception()) = vm().throw_completion<InternalError>(ErrorType::CallStackSizeExceeded).release_value().value();
reg(Register::exception()) = vm().throw_completion<InternalError>(ErrorType::CallStackSizeExceeded).value();
return;
}
@ -2940,7 +2941,7 @@ ThrowCompletionOr<void> IteratorClose::execute_impl(Bytecode::Interpreter& inter
auto& iterator = static_cast<IteratorRecord&>(interpreter.get(m_iterator_record).as_cell());
// FIXME: Return the value of the resulting completion. (Note that m_completion_value can be empty!)
TRY(iterator_close(vm, iterator, Completion { m_completion_type, m_completion_value }));
TRY(iterator_close(vm, iterator, Completion { m_completion_type, m_completion_value.value_or(js_undefined()) }));
return {};
}
@ -2950,7 +2951,7 @@ ThrowCompletionOr<void> AsyncIteratorClose::execute_impl(Bytecode::Interpreter&
auto& iterator = static_cast<IteratorRecord&>(interpreter.get(m_iterator_record).as_cell());
// FIXME: Return the value of the resulting completion. (Note that m_completion_value can be empty!)
TRY(async_iterator_close(vm, iterator, Completion { m_completion_type, m_completion_value }));
TRY(async_iterator_close(vm, iterator, Completion { m_completion_type, m_completion_value.value_or(js_undefined()) }));
return {};
}

View file

@ -161,7 +161,7 @@ void continue_module_loading(GraphLoadingState& state, ThrowCompletionOr<GC::Ref
auto value = module_completion.throw_completion().value();
// b. Perform ! Call(state.[[PromiseCapability]].[[Reject]], undefined, « moduleCompletion.[[Value]] »).
MUST(call(state.vm(), *state.promise_capability->reject(), js_undefined(), *value));
MUST(call(state.vm(), *state.promise_capability->reject(), js_undefined(), value));
}
// 4. Return UNUSED.
@ -389,10 +389,10 @@ ThrowCompletionOr<Promise*> CyclicModule::evaluate(VM& vm)
// c. Assert: module.[[EvaluationError]] is result.
VERIFY(m_evaluation_error.is_error());
VERIFY(same_value(*m_evaluation_error.throw_completion().value(), *result.throw_completion().value()));
VERIFY(same_value(m_evaluation_error.throw_completion().value(), result.throw_completion().value()));
// d. Perform ! Call(capability.[[Reject]], undefined, « result.[[Value]] »).
MUST(call(vm, *m_top_level_capability->reject(), js_undefined(), *result.throw_completion().value()));
MUST(call(vm, *m_top_level_capability->reject(), js_undefined(), result.throw_completion().value()));
}
// 10. Else,
else {
@ -741,7 +741,7 @@ void CyclicModule::async_module_execution_fulfilled(VM& vm)
// ii. If result is an abrupt completion, then
if (result.is_throw_completion()) {
// 1. Perform AsyncModuleExecutionRejected(m, result.[[Value]]).
module->async_module_execution_rejected(vm, *result.throw_completion().value());
module->async_module_execution_rejected(vm, result.throw_completion().value());
}
// iii. Else,
else {
@ -838,7 +838,7 @@ void continue_dynamic_import(GC::Ref<PromiseCapability> promise_capability, Thro
// 1. If moduleCompletion is an abrupt completion, then
if (module_completion.is_throw_completion()) {
// a. Perform ! Call(promiseCapability.[[Reject]], undefined, « moduleCompletion.[[Value]] »).
MUST(call(vm, *promise_capability->reject(), js_undefined(), *module_completion.throw_completion().value()));
MUST(call(vm, *promise_capability->reject(), js_undefined(), module_completion.throw_completion().value()));
// b. Return unused.
return;
@ -874,7 +874,7 @@ void continue_dynamic_import(GC::Ref<PromiseCapability> promise_capability, Thro
// b. If link is an abrupt completion, then
if (link.is_throw_completion()) {
// i. Perform ! Call(promiseCapability.[[Reject]], undefined, « link.[[Value]] »).
MUST(call(vm, *promise_capability->reject(), js_undefined(), *link.throw_completion().value()));
MUST(call(vm, *promise_capability->reject(), js_undefined(), link.throw_completion().value()));
// ii. Return unused.
return js_undefined();

View file

@ -690,7 +690,7 @@ ErrorOr<void> print_intl_date_time_format(JS::PrintContext& print_context, JS::I
return {};
});
if (result.is_throw_completion() && result.throw_completion().value()->is_null())
if (result.is_throw_completion() && result.throw_completion().value().is_null())
return Error::from_errno(ENOMEM); // probably
return {};

View file

@ -1631,10 +1631,10 @@ Completion dispose_resources(VM& vm, DisposeCapability& dispose_capability, Comp
// 1. If completion is a throw completion, then
if (completion.type() == Completion::Type::Throw) {
// a. Set result to result.[[Value]].
auto result_value = result.error().value().value();
auto result_value = result.error().value();
// b. Let suppressed be completion.[[Value]].
auto suppressed = completion.value().value();
auto suppressed = completion.value();
// c. Let error be a newly created SuppressedError object.
auto error = SuppressedError::create(*vm.current_realm());

View file

@ -241,7 +241,7 @@ ThrowCompletionOr<GroupsType> group_by(VM& vm, Value items, Value callback_funct
// f. IfAbruptCloseIterator(key, iteratorRecord).
if (key.is_error())
return Completion { *TRY(iterator_close(vm, iterator_record, key.release_error())) };
return Completion { TRY(iterator_close(vm, iterator_record, key.release_error())) };
// g. If keyCoercion is property, then
if constexpr (IsSame<KeyType, PropertyKey>) {
@ -250,7 +250,7 @@ ThrowCompletionOr<GroupsType> group_by(VM& vm, Value items, Value callback_funct
// ii. IfAbruptCloseIterator(key, iteratorRecord).
if (property_key.is_error())
return Completion { *TRY(iterator_close(vm, iterator_record, property_key.release_error())) };
return Completion { TRY(iterator_close(vm, iterator_record, property_key.release_error())) };
add_value_to_keyed_group(vm, groups, property_key.release_value(), value);
}

View file

@ -60,7 +60,7 @@ ThrowCompletionOr<GC::Ref<Object>> ArrayBufferConstructor::construct(FunctionObj
if (byte_length_or_error.is_error()) {
auto error = byte_length_or_error.release_error();
if (error.value()->is_object() && is<RangeError>(error.value()->as_object())) {
if (error.value().is_object() && is<RangeError>(error.value().as_object())) {
// Re-throw more specific RangeError
return vm.throw_completion<RangeError>(ErrorType::InvalidLength, "array buffer");
}

View file

@ -191,7 +191,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from)
auto error = vm.throw_completion<TypeError>(ErrorType::ArrayMaxSize);
// 2. Return ? IteratorClose(iteratorRecord, error).
return *TRY(iterator_close(vm, iterator, move(error)));
return TRY(iterator_close(vm, iterator, move(error)));
}
// ii. Let Pk be ! ToString(𝔽(k)).
@ -218,7 +218,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from)
// 2. IfAbruptCloseIterator(mappedValue, iteratorRecord).
if (mapped_value_or_error.is_error())
return *TRY(iterator_close(vm, iterator, mapped_value_or_error.release_error()));
return TRY(iterator_close(vm, iterator, mapped_value_or_error.release_error()));
mapped_value = mapped_value_or_error.release_value();
}
// vi. Else, let mappedValue be nextValue.
@ -231,7 +231,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from)
// viii. IfAbruptCloseIterator(defineStatus, iteratorRecord).
if (result_or_error.is_error())
return *TRY(iterator_close(vm, iterator, result_or_error.release_error()));
return TRY(iterator_close(vm, iterator, result_or_error.release_error()));
// ix. Set k to k + 1.
}
@ -374,7 +374,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from_async)
auto error = vm.throw_completion<TypeError>(ErrorType::ArrayMaxSize);
// b. Return ? AsyncIteratorClose(iteratorRecord, error).
return *TRY(async_iterator_close(vm, *iterator_record, move(error)));
return TRY(async_iterator_close(vm, *iterator_record, move(error)));
}
// 2. Let Pk be ! ToString(𝔽(k)).
@ -443,7 +443,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from_async)
// 12. If defineStatus is an abrupt completion, return ? AsyncIteratorClose(iteratorRecord, defineStatus).
if (define_status.is_error())
return *TRY(iterator_close(vm, *iterator_record, define_status.release_error()));
return TRY(iterator_close(vm, *iterator_record, define_status.release_error()));
// 13. Set k to k + 1.
}

View file

@ -141,7 +141,7 @@ JS_DEFINE_NATIVE_FUNCTION(AsyncDisposableStackPrototype::dispose_async)
auto result = TRY_OR_REJECT(vm, promise_capability, dispose_resources(vm, async_disposable_stack.dispose_capability(), normal_completion(js_undefined())));
// 8. Perform ! Call(promiseCapability.[[Resolve]], undefined, « result »).
MUST(call(vm, *promise_capability->resolve(), js_undefined(), *result));
MUST(call(vm, *promise_capability->resolve(), js_undefined(), result));
// 9. Return promiseCapability.[[Promise]].
return promise_capability->promise();

View file

@ -42,8 +42,7 @@ void AsyncGenerator::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
for (auto const& request : m_async_generator_queue) {
if (request.completion.value().has_value())
visitor.visit(*request.completion.value());
visitor.visit(request.completion.value());
visitor.visit(request.capability);
}
visitor.visit(m_generating_function);
@ -155,8 +154,6 @@ void AsyncGenerator::execute(VM& vm, Completion completion)
{
while (true) {
// Loosely based on step 4 of https://tc39.es/ecma262/#sec-asyncgeneratorstart
VERIFY(completion.value().has_value());
auto generated_value = [](Value value) -> Value {
if (value.is_cell())
return static_cast<GeneratorResult const&>(value.as_cell()).result();
@ -349,7 +346,7 @@ void AsyncGenerator::await_return()
VERIFY(completion.type() == Completion::Type::Return);
// 6. Let promiseCompletion be Completion(PromiseResolve(%Promise%, _completion_.[[Value]])).
auto promise_completion = promise_resolve(vm, realm.intrinsics().promise_constructor(), completion.value().value());
auto promise_completion = promise_resolve(vm, realm.intrinsics().promise_constructor(), completion.value());
// 7. If promiseCompletion is an abrupt completion, then
if (promise_completion.is_throw_completion()) {
@ -442,7 +439,7 @@ void AsyncGenerator::complete_step(Completion completion, bool done, Realm* real
auto promise_capability = next.capability;
// 5. Let value be completion.[[Value]].
auto value = completion.value().value();
auto value = completion.value();
// 6. If completion.[[Type]] is throw, then
if (completion.type() == Completion::Type::Throw) {

View file

@ -38,9 +38,8 @@ namespace JS {
if (_temporary_result.is_error()) { \
auto _completion = _temporary_result.release_error(); \
\
VERIFY(_completion.value().has_value()); \
VERIFY(_completion.value()->is_object()); \
VERIFY(::AK::is<JS::InternalError>(_completion.value()->as_object())); \
VERIFY(_completion.value().is_object()); \
VERIFY(::AK::is<JS::InternalError>(_completion.value().as_object())); \
\
return _completion; \
} \
@ -61,13 +60,12 @@ public:
Throw,
};
ALWAYS_INLINE Completion(Type type, Optional<Value> value)
ALWAYS_INLINE Completion(Type type, Value value)
: m_type(type)
, m_value(move(value))
, m_value(value)
{
VERIFY(type != Type::Empty);
if (m_value.has_value())
VERIFY(!m_value->is_empty());
VERIFY(!value.is_empty());
}
Completion(ThrowCompletionOr<Value> const&);
@ -79,11 +77,6 @@ public:
{
}
ALWAYS_INLINE Completion(Optional<Value> value)
: Completion(Type::Normal, move(value))
{
}
ALWAYS_INLINE Completion()
: Completion(js_undefined())
{
@ -99,19 +92,18 @@ public:
VERIFY(m_type != Type::Empty);
return m_type;
}
[[nodiscard]] Optional<Value>& value() { return m_value; }
[[nodiscard]] Optional<Value> const& value() const { return m_value; }
[[nodiscard]] Value& value() { return m_value; }
[[nodiscard]] Value const& value() const { return m_value; }
// "abrupt completion refers to any completion with a [[Type]] value other than normal"
[[nodiscard]] bool is_abrupt() const { return m_type != Type::Normal; }
// These are for compatibility with the TRY() macro in AK.
[[nodiscard]] bool is_error() const { return m_type == Type::Throw; }
[[nodiscard]] Optional<Value> release_value() { return move(m_value); }
[[nodiscard]] Value release_value() { return m_value; }
Completion release_error()
{
VERIFY(is_error());
VERIFY(m_value.has_value());
return { m_type, release_value() };
}
@ -131,7 +123,7 @@ private:
}
Type m_type { Type::Normal }; // [[Type]]
Optional<Value> m_value; // [[Value]]
Value m_value; // [[Value]]
// NOTE: We don't need the [[Target]] slot since control flow is handled in bytecode.
};
@ -244,7 +236,7 @@ public:
// Not `explicit` on purpose so that `return vm.throw_completion<Error>(...);` is possible.
ALWAYS_INLINE ThrowCompletionOr(Completion throw_completion)
: m_value_or_error(ErrorValue { throw_completion.release_error().value().value() })
: m_value_or_error(ErrorValue { throw_completion.release_error().value() })
{
}
@ -314,7 +306,7 @@ public:
ThrowCompletionOr<Value> await(VM&, Value);
// 6.2.4.1 NormalCompletion ( value ), https://tc39.es/ecma262/#sec-normalcompletion
inline Completion normal_completion(Optional<Value> value)
inline Completion normal_completion(Value value)
{
// 1. Return Completion Record { [[Type]]: normal, [[Value]]: value, [[Target]]: empty }.
return { Completion::Type::Normal, move(value) };

View file

@ -15,8 +15,7 @@ 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());
visitor.visit(m_completion.value());
}
}

View file

@ -115,7 +115,7 @@ JS_DEFINE_NATIVE_FUNCTION(DisposableStackPrototype::dispose)
disposable_stack->set_disposed();
// 5. Return DisposeResources(disposableStack.[[DisposeCapability]], NormalCompletion(undefined)).
return *TRY(dispose_resources(vm, disposable_stack->dispose_capability(), normal_completion(js_undefined())));
return TRY(dispose_resources(vm, disposable_stack->dispose_capability(), normal_completion(js_undefined())));
}
// 12.3.3.4 get DisposableStack.prototype.disposed, https://tc39.es/proposal-explicit-resource-management/#sec-get-disposablestack.prototype.disposed

View file

@ -436,7 +436,7 @@ ThrowCompletionOr<Value> ECMAScriptFunctionObject::internal_call(Value this_argu
// 8. If result.[[Type]] is return, return result.[[Value]].
if (result.type() == Completion::Type::Return)
return *result.value();
return result.value();
// 9. Assert: result is a throw completion.
VERIFY(result.type() == Completion::Type::Throw);
@ -520,15 +520,15 @@ ThrowCompletionOr<GC::Ref<Object>> ECMAScriptFunctionObject::internal_construct(
VERIFY(result.type() == Completion::Type::Return);
// 12. If Type(result.[[Value]]) is Object, return result.[[Value]].
if (result.value()->is_object())
return result.value()->as_object();
if (result.value().is_object())
return result.value().as_object();
// 13. If kind is base, return thisArgument.
if (kind == ConstructorKind::Base)
return *this_argument;
// 14. If result.[[Value]] is not undefined, throw a TypeError exception.
if (!result.value()->is_undefined())
if (!result.value().is_undefined())
return vm.throw_completion<TypeError>(ErrorType::DerivedConstructorReturningInvalidValue);
// 15. Let thisBinding be ? constructorEnv.GetThisBinding().
@ -771,7 +771,7 @@ void async_block_start(VM& vm, T const& async_body, PromiseCapability const& pro
// g. Else if result is a return completion, then
else if (result.type() == Completion::Type::Return) {
// i. Perform ! Call(promiseCapability.[[Resolve]], undefined, « result.[[Value]] »).
MUST(call(vm, *promise_capability.resolve(), js_undefined(), *result.value()));
MUST(call(vm, *promise_capability.resolve(), js_undefined(), result.value()));
}
// h. Else,
else {
@ -779,7 +779,7 @@ void async_block_start(VM& vm, T const& async_body, PromiseCapability const& pro
VERIFY(result.type() == Completion::Type::Throw);
// ii. Perform ! Call(promiseCapability.[[Reject]], undefined, « result.[[Value]] »).
MUST(call(vm, *promise_capability.reject(), js_undefined(), *result.value()));
MUST(call(vm, *promise_capability.reject(), js_undefined(), result.value()));
}
// i. Return unused.
// NOTE: We don't support returning an empty/optional/unused value here.

View file

@ -82,8 +82,6 @@ ThrowCompletionOr<Value> GeneratorObject::execute(VM& vm, Completion const& comp
{
// Loosely based on step 4 of https://tc39.es/ecma262/#sec-generatorstart mixed with https://tc39.es/ecma262/#sec-generatoryield at the end.
VERIFY(completion.value().has_value());
auto generated_value = [](Value value) -> Value {
if (value.is_cell())
return static_cast<GeneratorResult const&>(value.as_cell()).result();
@ -167,9 +165,6 @@ 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)
{
// Not part of the spec, but the spec assumes abruptCompletion.[[Value]] is not empty.
VERIFY(abrupt_completion.value().has_value());
// 1. Let state be ? GeneratorValidate(generator, generatorBrand).
auto state = TRY(validate(vm, generator_brand));
@ -190,7 +185,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().value(), true);
return create_iterator_result_object(vm, abrupt_completion.value(), true);
}
// b. Return ? abruptCompletion.

View file

@ -43,7 +43,7 @@ Value IteratorHelper::result(Value value)
ThrowCompletionOr<Value> IteratorHelper::close_result(VM& vm, Completion completion)
{
set_generator_state(GeneratorState::Completed);
return *TRY(iterator_close(vm, underlying_iterator(), move(completion)));
return TRY(iterator_close(vm, underlying_iterator(), move(completion)));
}
ThrowCompletionOr<Value> IteratorHelper::execute(VM& vm, JS::Completion const& completion)

View file

@ -56,7 +56,7 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorHelperPrototype::return_)
// b. NOTE: Once a generator enters the completed state it never leaves it and its associated execution context is never resumed. Any execution state associated with O can be discarded at this point.
// c. Perform ? IteratorClose(O.[[UnderlyingIterator]], NormalCompletion(unused)).
TRY(iterator_close(vm, iterator->underlying_iterator(), normal_completion({})));
TRY(iterator_close(vm, iterator->underlying_iterator(), normal_completion(js_undefined())));
// d. Return CreateIterResultObject(undefined, true).
return create_iterator_result_object(vm, js_undefined(), true);

View file

@ -175,11 +175,11 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::every)
// d. IfAbruptCloseIterator(result, iterated).
if (result.is_error())
return *TRY(iterator_close(vm, iterated, result.release_error()));
return TRY(iterator_close(vm, iterated, result.release_error()));
// e. If ToBoolean(result) is false, return ? IteratorClose(iterated, NormalCompletion(false)).
if (!result.value().to_boolean())
return *TRY(iterator_close(vm, iterated, normal_completion(Value { false })));
return TRY(iterator_close(vm, iterated, normal_completion(Value { false })));
// f. Set counter to counter + 1.
++counter;
@ -280,11 +280,11 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::find)
// d. IfAbruptCloseIterator(result, iterated).
if (result.is_error())
return *TRY(iterator_close(vm, iterated, result.release_error()));
return TRY(iterator_close(vm, iterated, result.release_error()));
// e. If ToBoolean(result) is true, return ? IteratorClose(iterated, NormalCompletion(value)).
if (result.value().to_boolean())
return *TRY(iterator_close(vm, iterated, normal_completion(value)));
return TRY(iterator_close(vm, iterated, normal_completion(*value)));
// f. Set counter to counter + 1.
++counter;
@ -466,7 +466,7 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::for_each)
// d. IfAbruptCloseIterator(result, iterated).
if (result.is_error())
return *TRY(iterator_close(vm, iterated, result.release_error()));
return TRY(iterator_close(vm, iterated, result.release_error()));
// e. Set counter to counter + 1.
++counter;
@ -585,7 +585,7 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::reduce)
// d. IfAbruptCloseIterator(result, iterated).
if (result.is_error())
return *TRY(iterator_close(vm, iterated, result.release_error()));
return TRY(iterator_close(vm, iterated, result.release_error()));
// e. Set accumulator to result.[[Value]].
accumulator = result.release_value();
@ -628,11 +628,11 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::some)
// d. IfAbruptCloseIterator(result, iterated).
if (result.is_error())
return *TRY(iterator_close(vm, iterated, result.release_error()));
return TRY(iterator_close(vm, iterated, result.release_error()));
// e. If ToBoolean(result) is true, return ? IteratorClose(iterated, NormalCompletion(true)).
if (result.value().to_boolean())
return *TRY(iterator_close(vm, iterated, normal_completion(Value { true })));
return TRY(iterator_close(vm, iterated, normal_completion(Value { true })));
// f. Set counter to counter + 1.
++counter;

View file

@ -125,7 +125,7 @@ Promise::ResolvingFunctions Promise::create_resolving_functions()
if (then.is_throw_completion()) {
// a. Perform RejectPromise(promise, then.[[Value]]).
dbgln_if(PROMISE_DEBUG, "[Promise @ {} / PromiseResolvingFunction]: Exception while getting 'then' property, rejecting with error", &promise);
promise.reject(*then.throw_completion().value());
promise.reject(then.throw_completion().value());
// b. Return undefined.
return js_undefined();

View file

@ -39,23 +39,23 @@ private:
};
// 27.2.1.1.1 IfAbruptRejectPromise ( value, capability ), https://tc39.es/ecma262/#sec-ifabruptrejectpromise
#define __TRY_OR_REJECT(vm, capability, expression, CALL_CHECK) \
({ \
auto&& _temporary_try_or_reject_result = (expression); \
/* 1. If value is an abrupt completion, then */ \
if (_temporary_try_or_reject_result.is_error()) { \
/* a. Perform ? Call(capability.[[Reject]], undefined, « value.[[Value]] »). */ \
CALL_CHECK(JS::call(vm, *(capability)->reject(), js_undefined(), *_temporary_try_or_reject_result.release_error().value())); \
\
/* b. Return capability.[[Promise]]. */ \
return (capability)->promise(); \
} \
\
static_assert(!::AK::Detail::IsLvalueReference<decltype(_temporary_try_or_reject_result.release_value())>, \
"Do not return a reference from a fallible expression"); \
\
/* 2. Else if value is a Completion Record, set value to value.[[Value]]. */ \
_temporary_try_or_reject_result.release_value(); \
#define __TRY_OR_REJECT(vm, capability, expression, CALL_CHECK) \
({ \
auto&& _temporary_try_or_reject_result = (expression); \
/* 1. If value is an abrupt completion, then */ \
if (_temporary_try_or_reject_result.is_error()) { \
/* a. Perform ? Call(capability.[[Reject]], undefined, « value.[[Value]] »). */ \
CALL_CHECK(JS::call(vm, *(capability)->reject(), js_undefined(), _temporary_try_or_reject_result.release_error().value())); \
\
/* b. Return capability.[[Promise]]. */ \
return (capability)->promise(); \
} \
\
static_assert(!::AK::Detail::IsLvalueReference<decltype(_temporary_try_or_reject_result.release_value())>, \
"Do not return a reference from a fallible expression"); \
\
/* 2. Else if value is a Completion Record, set value to value.[[Value]]. */ \
_temporary_try_or_reject_result.release_value(); \
})
#define TRY_OR_REJECT(vm, capability, expression) \
@ -65,23 +65,23 @@ private:
__TRY_OR_REJECT(vm, capability, expression, MUST)
// 27.2.1.1.1 IfAbruptRejectPromise ( value, capability ), https://tc39.es/ecma262/#sec-ifabruptrejectpromise
#define TRY_OR_REJECT_WITH_VALUE(vm, capability, expression) \
({ \
auto&& _temporary_try_or_reject_result = (expression); \
/* 1. If value is an abrupt completion, then */ \
if (_temporary_try_or_reject_result.is_error()) { \
/* a. Perform ? Call(capability.[[Reject]], undefined, « value.[[Value]] »). */ \
TRY(JS::call(vm, *(capability)->reject(), js_undefined(), *_temporary_try_or_reject_result.release_error().value())); \
\
/* b. Return capability.[[Promise]]. */ \
return Value { (capability)->promise() }; \
} \
\
static_assert(!::AK::Detail::IsLvalueReference<decltype(_temporary_try_or_reject_result.release_value())>, \
"Do not return a reference from a fallible expression"); \
\
/* 2. Else if value is a Completion Record, set value to value.[[Value]]. */ \
_temporary_try_or_reject_result.release_value(); \
#define TRY_OR_REJECT_WITH_VALUE(vm, capability, expression) \
({ \
auto&& _temporary_try_or_reject_result = (expression); \
/* 1. If value is an abrupt completion, then */ \
if (_temporary_try_or_reject_result.is_error()) { \
/* a. Perform ? Call(capability.[[Reject]], undefined, « value.[[Value]] »). */ \
TRY(JS::call(vm, *(capability)->reject(), js_undefined(), _temporary_try_or_reject_result.release_error().value())); \
\
/* b. Return capability.[[Promise]]. */ \
return Value { (capability)->promise() }; \
} \
\
static_assert(!::AK::Detail::IsLvalueReference<decltype(_temporary_try_or_reject_result.release_value())>, \
"Do not return a reference from a fallible expression"); \
\
/* 2. Else if value is a Completion Record, set value to value.[[Value]]. */ \
_temporary_try_or_reject_result.release_value(); \
})
// 27.2.1.5 NewPromiseCapability ( C ), https://tc39.es/ecma262/#sec-newpromisecapability

View file

@ -282,7 +282,7 @@ ThrowCompletionOr<GC::Ref<Object>> PromiseConstructor::construct(FunctionObject&
// 10. If completion is an abrupt completion, then
if (completion.is_error()) {
// a. Perform ? Call(resolvingFunctions.[[Reject]], undefined, « completion.[[Value]] »).
TRY(JS::call(vm, *reject_function, js_undefined(), *completion.release_error().value()));
TRY(JS::call(vm, *reject_function, js_undefined(), completion.release_error().value()));
}
// 11. Return promise.
@ -484,7 +484,7 @@ JS_DEFINE_NATIVE_FUNCTION(PromiseConstructor::try_)
// 5. If status is an abrupt completion, then
if (status.is_throw_completion()) {
// a. Perform ? Call(promiseCapability.[[Reject]], undefined, « status.[[Value]] »).
TRY(JS::call(vm, *promise_capability->reject(), js_undefined(), *status.throw_completion().value()));
TRY(JS::call(vm, *promise_capability->reject(), js_undefined(), status.throw_completion().value()));
}
// 6. Else,
else {

View file

@ -62,8 +62,6 @@ static ThrowCompletionOr<Value> run_reaction_job(VM& vm, PromiseReaction& reacti
// ii. Return empty.
dbgln_if(PROMISE_DEBUG, "run_reaction_job: Reaction has no PromiseCapability, returning empty value");
// TODO: This can't return an empty value at the moment, because the implicit conversion to Completion would fail.
// Change it back when this is using completions (`return normal_completion({})`)
return js_undefined();
}
@ -74,14 +72,14 @@ static ThrowCompletionOr<Value> run_reaction_job(VM& vm, PromiseReaction& reacti
// i. Return ? Call(promiseCapability.[[Reject]], undefined, « handlerResult.[[Value]] »).
auto reject_function = promise_capability->reject();
dbgln_if(PROMISE_DEBUG, "run_reaction_job: Calling PromiseCapability's reject function @ {}", reject_function.ptr());
return call(vm, *reject_function, js_undefined(), *handler_result.value());
return call(vm, *reject_function, js_undefined(), handler_result.value());
}
// i. Else,
else {
// i. Return ? Call(promiseCapability.[[Resolve]], undefined, « handlerResult.[[Value]] »).
auto resolve_function = promise_capability->resolve();
dbgln_if(PROMISE_DEBUG, "[PromiseReactionJob]: Calling PromiseCapability's resolve function @ {}", resolve_function.ptr());
return call(vm, *resolve_function, js_undefined(), *handler_result.value());
return call(vm, *resolve_function, js_undefined(), handler_result.value());
}
}
@ -134,8 +132,8 @@ static ThrowCompletionOr<Value> run_resolve_thenable_job(VM& vm, Promise& promis
// c. If thenCallResult is an abrupt completion, then
if (then_call_result.is_error()) {
// i. Return ? Call(resolvingFunctions.[[Reject]], undefined, « thenCallResult.[[Value]] »).
dbgln_if(PROMISE_DEBUG, "run_resolve_thenable_job: then_call_result is an abrupt completion, calling reject function with value {}", *then_call_result.throw_completion().value());
return call(vm, *reject_function, js_undefined(), *then_call_result.throw_completion().value());
dbgln_if(PROMISE_DEBUG, "run_resolve_thenable_job: then_call_result is an abrupt completion, calling reject function with value {}", then_call_result.throw_completion().value());
return call(vm, *reject_function, js_undefined(), then_call_result.throw_completion().value());
}
// d. Return ? thenCallResult.

View file

@ -376,7 +376,7 @@ JS_DEFINE_NATIVE_FUNCTION(SetPrototype::is_disjoint_from)
// 1. If SetDataHas(O.[[SetData]], next) is true, then
if (set_data_has(set, *next)) {
// a. Perform ? IteratorClose(keysIter, NormalCompletion(UNUSED)).
TRY(iterator_close(vm, keys_iterator, normal_completion({})));
TRY(iterator_close(vm, keys_iterator, normal_completion(js_undefined())));
// b. Return false.
return false;
@ -455,7 +455,7 @@ JS_DEFINE_NATIVE_FUNCTION(SetPrototype::is_superset_of)
// i. If SetDataHas(O.[[SetData]], next) is false, then
if (!set_data_has(set, *next)) {
// 1. Perform ? IteratorClose(keysIter, NormalCompletion(UNUSED)).
TRY(iterator_close(vm, keys_iterator, normal_completion({})));
TRY(iterator_close(vm, keys_iterator, normal_completion(js_undefined())));
// 2. Return false.
return false;

View file

@ -164,7 +164,7 @@ ThrowCompletionOr<Value> perform_shadow_realm_eval(VM& vm, StringView source_tex
}
// 12. If result.[[Type]] is normal and result.[[Value]] is empty, then
if (result.type() == Completion::Type::Normal && !result.value().has_value()) {
if (result.type() == Completion::Type::Normal && result.value().is_empty()) {
// a. Set result to NormalCompletion(undefined).
result = normal_completion(js_undefined());
}
@ -184,7 +184,7 @@ ThrowCompletionOr<Value> perform_shadow_realm_eval(VM& vm, StringView source_tex
}
// 16. Return ? GetWrappedValue(callerRealm, result.[[Value]]).
return get_wrapped_value(vm, caller_realm, *result.value());
return get_wrapped_value(vm, caller_realm, result.value());
// NOTE: Also see "Editor's Note" in the spec regarding the TypeError above.
}

View file

@ -54,7 +54,7 @@ ThrowCompletionOr<GC::Ref<Object>> SharedArrayBufferConstructor::construct(Funct
if (byte_length_or_error.is_error()) {
auto error = byte_length_or_error.release_error();
if (error.value()->is_object() && is<RangeError>(error.value()->as_object())) {
if (error.value().is_object() && is<RangeError>(error.value().as_object())) {
// Re-throw more specific RangeError
return vm.throw_completion<RangeError>(ErrorType::InvalidLength, "shared array buffer");
}

View file

@ -573,7 +573,7 @@ void TypedArrayBase::visit_edges(Visitor& visitor)
auto array_length_or_error = first_argument.to_index(vm); \
if (array_length_or_error.is_error()) { \
auto error = array_length_or_error.release_error(); \
if (error.value()->is_object() && is<RangeError>(error.value()->as_object())) { \
if (error.value().is_object() && is<RangeError>(error.value().as_object())) { \
/* Re-throw more specific RangeError */ \
return vm.throw_completion<RangeError>(ErrorType::InvalidLength, "typed array"); \
} \

View file

@ -779,7 +779,7 @@ ThrowCompletionOr<void> SourceTextModule::execute_module(VM& vm, GC::Ptr<Promise
// AD-HOC: This is basically analogous to what AsyncBlockStart would do.
if (result.is_throw_completion()) {
MUST(call(vm, *capability->reject(), js_undefined(), result.throw_completion().value().value()));
MUST(call(vm, *capability->reject(), js_undefined(), result.throw_completion().value()));
} else {
MUST(call(vm, *capability->resolve(), js_undefined(), result.value()));
}

View file

@ -113,8 +113,7 @@ ThrowCompletionOr<Promise*> SyntheticModule::evaluate(VM& vm)
// Note: Because we expect it to return a promise we convert this here.
auto promise = Promise::create(realm());
if (result.is_error()) {
VERIFY(result.throw_completion().value().has_value());
promise->reject(*result.throw_completion().value());
promise->reject(result.throw_completion().value());
} else {
// Note: This value probably isn't visible to JS code? But undefined is fine anyway.
promise->fulfill(js_undefined());