LibWeb: Do not throw exceptions from Promise-type attributes

If an attribute's getter were to throw an exception, we must instead
return a rejected promise. We already supported this behavior for
functions.
This commit is contained in:
Timothy Flynn 2025-04-14 14:52:22 -04:00
commit fbea9b86c5
2 changed files with 34 additions and 6 deletions

View file

@ -3729,8 +3729,19 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::@attribute.getter_callback@)
{ {
WebIDL::log_trace(vm, "@class_name@::@attribute.getter_callback@"); WebIDL::log_trace(vm, "@class_name@::@attribute.getter_callback@");
[[maybe_unused]] auto& realm = *vm.current_realm(); [[maybe_unused]] auto& realm = *vm.current_realm();
)~~~");
// NOTE: Create a wrapper lambda so that if the function steps return an exception, we can return that in a rejected promise.
if (attribute.type->name() == "Promise"sv) {
attribute_generator.append(R"~~~(
auto steps = [&]() -> JS::ThrowCompletionOr<GC::Ptr<WebIDL::Promise>> {
)~~~");
}
attribute_generator.append(R"~~~(
[[maybe_unused]] auto* impl = TRY(impl_from(vm)); [[maybe_unused]] auto* impl = TRY(impl_from(vm));
)~~~"); )~~~");
if (attribute.extended_attributes.contains("CEReactions")) { if (attribute.extended_attributes.contains("CEReactions")) {
// 1. Push a new element queue onto this object's relevant agent's custom element reactions stack. // 1. Push a new element queue onto this object's relevant agent's custom element reactions stack.
attribute_generator.append(R"~~~( attribute_generator.append(R"~~~(
@ -4069,6 +4080,23 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::@attribute.getter_callback@)
} }
} }
if (attribute.type->name() == "Promise"sv) {
attribute_generator.append(R"~~~(
return retval;
};
auto maybe_retval = steps();
// And then, if an exception E was thrown:
// 1. If attributes type is a promise type, then return ! Call(%Promise.reject%, %Promise%, «E»).
// 2. Otherwise, end these steps and allow the exception to propagate.
if (maybe_retval.is_throw_completion())
return WebIDL::create_rejected_promise(realm, maybe_retval.error_value())->promise();
auto retval = maybe_retval.release_value();
)~~~");
}
generate_return_statement(generator, *attribute.type, interface); generate_return_statement(generator, *attribute.type, interface);
attribute_generator.append(R"~~~( attribute_generator.append(R"~~~(

View file

@ -2,8 +2,8 @@ Harness status: OK
Found 229 tests Found 229 tests
224 Pass 228 Pass
5 Fail 1 Fail
Pass idl_test setup Pass idl_test setup
Pass idl_test validation Pass idl_test validation
Pass ReadableStreamDefaultReader includes ReadableStreamGenericReader: member names are unique Pass ReadableStreamDefaultReader includes ReadableStreamGenericReader: member names are unique
@ -44,7 +44,7 @@ Pass ReadableStreamDefaultReader interface: existence and properties of interfac
Pass ReadableStreamDefaultReader interface: existence and properties of interface prototype object's @@unscopables property Pass ReadableStreamDefaultReader interface: existence and properties of interface prototype object's @@unscopables property
Pass ReadableStreamDefaultReader interface: operation read() Pass ReadableStreamDefaultReader interface: operation read()
Pass ReadableStreamDefaultReader interface: operation releaseLock() Pass ReadableStreamDefaultReader interface: operation releaseLock()
Fail ReadableStreamDefaultReader interface: attribute closed Pass ReadableStreamDefaultReader interface: attribute closed
Pass ReadableStreamDefaultReader interface: operation cancel(optional any) Pass ReadableStreamDefaultReader interface: operation cancel(optional any)
Pass ReadableStreamDefaultReader must be primary interface of (new ReadableStream()).getReader() Pass ReadableStreamDefaultReader must be primary interface of (new ReadableStream()).getReader()
Pass Stringification of (new ReadableStream()).getReader() Pass Stringification of (new ReadableStream()).getReader()
@ -61,7 +61,7 @@ Pass ReadableStreamBYOBReader interface: existence and properties of interface p
Pass ReadableStreamBYOBReader interface: existence and properties of interface prototype object's @@unscopables property Pass ReadableStreamBYOBReader interface: existence and properties of interface prototype object's @@unscopables property
Pass ReadableStreamBYOBReader interface: operation read(ArrayBufferView, optional ReadableStreamBYOBReaderReadOptions) Pass ReadableStreamBYOBReader interface: operation read(ArrayBufferView, optional ReadableStreamBYOBReaderReadOptions)
Pass ReadableStreamBYOBReader interface: operation releaseLock() Pass ReadableStreamBYOBReader interface: operation releaseLock()
Fail ReadableStreamBYOBReader interface: attribute closed Pass ReadableStreamBYOBReader interface: attribute closed
Pass ReadableStreamBYOBReader interface: operation cancel(optional any) Pass ReadableStreamBYOBReader interface: operation cancel(optional any)
Pass ReadableStreamBYOBReader must be primary interface of (new ReadableStream({ type: 'bytes' })).getReader({ mode: 'byob' }) Pass ReadableStreamBYOBReader must be primary interface of (new ReadableStream({ type: 'bytes' })).getReader({ mode: 'byob' })
Pass Stringification of (new ReadableStream({ type: 'bytes' })).getReader({ mode: 'byob' }) Pass Stringification of (new ReadableStream({ type: 'bytes' })).getReader({ mode: 'byob' })
@ -148,9 +148,9 @@ Pass WritableStreamDefaultWriter interface object name
Pass WritableStreamDefaultWriter interface: existence and properties of interface prototype object Pass WritableStreamDefaultWriter interface: existence and properties of interface prototype object
Pass WritableStreamDefaultWriter interface: existence and properties of interface prototype object's "constructor" property Pass WritableStreamDefaultWriter interface: existence and properties of interface prototype object's "constructor" property
Pass WritableStreamDefaultWriter interface: existence and properties of interface prototype object's @@unscopables property Pass WritableStreamDefaultWriter interface: existence and properties of interface prototype object's @@unscopables property
Fail WritableStreamDefaultWriter interface: attribute closed Pass WritableStreamDefaultWriter interface: attribute closed
Pass WritableStreamDefaultWriter interface: attribute desiredSize Pass WritableStreamDefaultWriter interface: attribute desiredSize
Fail WritableStreamDefaultWriter interface: attribute ready Pass WritableStreamDefaultWriter interface: attribute ready
Pass WritableStreamDefaultWriter interface: operation abort(optional any) Pass WritableStreamDefaultWriter interface: operation abort(optional any)
Pass WritableStreamDefaultWriter interface: operation close() Pass WritableStreamDefaultWriter interface: operation close()
Pass WritableStreamDefaultWriter interface: operation releaseLock() Pass WritableStreamDefaultWriter interface: operation releaseLock()