LibWeb/Bindings: Pass exact typed array types through WebIDL bindings

Generate exact JS buffer types for exact IDL buffer arguments instead
of widening them to BufferSource or ArrayBufferView.

This fixes cases like TextEncoder.encodeInto(), whose IDL requires
a Uint8Array destination. Previously the generated binding accepted
any BufferSource, so DataView, other typed arrays, and
ArrayBuffer-backed values were let through. With exact conversion,
those are rejected at the binding layer as expected.
This commit is contained in:
Shannon Booth 2026-04-26 15:59:50 +02:00 committed by Shannon Booth
parent 54244f9e4a
commit 7beac55210
18 changed files with 192 additions and 156 deletions

View file

@ -51,7 +51,7 @@ GC::Ref<JS::Uint8Array> TextEncoder::encode(String const& input) const
}
// https://encoding.spec.whatwg.org/#dom-textencoder-encodeinto
TextEncoderEncodeIntoResult TextEncoder::encode_into(String const& source, GC::Root<WebIDL::BufferSource> const& destination) const
TextEncoderEncodeIntoResult TextEncoder::encode_into(String const& source, GC::Root<JS::Uint8Array> const& destination) const
{
// AD-HOC: Return early if destination is detached. This is not explicitly handled in the spec,
// however no bytes are copied as destinations size is always zero in this case.
@ -59,7 +59,7 @@ TextEncoderEncodeIntoResult TextEncoder::encode_into(String const& source, GC::R
if (destination->viewed_array_buffer()->is_detached())
return { 0, 0 };
auto data = destination->viewed_array_buffer()->buffer().bytes().slice(destination->byte_offset(), destination->byte_length());
auto data = destination->data();
// 1. Let read be 0.
WebIDL::UnsignedLongLong read = 0;
@ -85,7 +85,7 @@ TextEncoderEncodeIntoResult TextEncoder::encode_into(String const& source, GC::R
// 6.4. Otherwise:
// 6.4.1. If destinations byte length written is greater than or equal to the number of bytes in result, then:
if (destination->byte_length() - written >= result.size()) {
if (data.size() - written >= result.size()) {
// 6.4.1.1. If item is greater than U+FFFF, then increment read by 2.
if (item > 0xffff) {
read += 2;

View file

@ -37,7 +37,7 @@ public:
virtual ~TextEncoder() override;
GC::Ref<JS::Uint8Array> encode(String const& input) const;
TextEncoderEncodeIntoResult encode_into(String const& source, GC::Root<WebIDL::BufferSource> const& destination) const;
TextEncoderEncodeIntoResult encode_into(String const& source, GC::Root<JS::Uint8Array> const& destination) const;
protected:
// https://encoding.spec.whatwg.org/#dom-textencoder

View file

@ -159,14 +159,10 @@ WebIDL::ExceptionOr<GC::Ref<DOMMatrix>> DOMMatrix::from_matrix(JS::VM& vm, DOMMa
}
// https://drafts.fxtf.org/geometry/#dom-dommatrix-fromfloat32array
WebIDL::ExceptionOr<GC::Ref<DOMMatrix>> DOMMatrix::from_float32_array(JS::VM& vm, GC::Root<WebIDL::BufferSource> const& array32)
WebIDL::ExceptionOr<GC::Ref<DOMMatrix>> DOMMatrix::from_float32_array(JS::VM& vm, GC::Root<JS::Float32Array> const& array)
{
if (!is<JS::Float32Array>(*array32->raw_object()))
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "Float32Array");
auto& realm = *vm.current_realm();
auto& float32_array = static_cast<JS::Float32Array&>(*array32->raw_object());
ReadonlySpan<float> elements = float32_array.data();
ReadonlySpan<float> elements = array->data();
// If array32 has 6 elements, return the result of invoking create a 2d matrix of type DOMMatrixReadOnly or DOMMatrix as appropriate, with a sequence of numbers taking the values from array32 in the provided order.
if (elements.size() == 6)
@ -184,14 +180,10 @@ WebIDL::ExceptionOr<GC::Ref<DOMMatrix>> DOMMatrix::from_float32_array(JS::VM& vm
}
// https://drafts.fxtf.org/geometry/#dom-dommatrix-fromfloat64array
WebIDL::ExceptionOr<GC::Ref<DOMMatrix>> DOMMatrix::from_float64_array(JS::VM& vm, GC::Root<WebIDL::BufferSource> const& array64)
WebIDL::ExceptionOr<GC::Ref<DOMMatrix>> DOMMatrix::from_float64_array(JS::VM& vm, GC::Root<JS::Float64Array> const& array)
{
if (!is<JS::Float64Array>(*array64->raw_object()))
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "Float64Array");
auto& realm = *vm.current_realm();
auto& float64_array = static_cast<JS::Float64Array&>(*array64->raw_object());
ReadonlySpan<double> elements = float64_array.data();
ReadonlySpan<double> elements = array->data();
// If array64 has 6 elements, return the result of invoking create a 2d matrix of type DOMMatrixReadOnly or DOMMatrix as appropriate, with a sequence of numbers taking the values from array64 in the provided order.
if (elements.size() == 6)

View file

@ -28,8 +28,8 @@ public:
virtual ~DOMMatrix() override;
static WebIDL::ExceptionOr<GC::Ref<DOMMatrix>> from_matrix(JS::VM&, DOMMatrixInit other = {});
static WebIDL::ExceptionOr<GC::Ref<DOMMatrix>> from_float32_array(JS::VM&, GC::Root<WebIDL::BufferSource> const& array32);
static WebIDL::ExceptionOr<GC::Ref<DOMMatrix>> from_float64_array(JS::VM&, GC::Root<WebIDL::BufferSource> const& array64);
static WebIDL::ExceptionOr<GC::Ref<DOMMatrix>> from_float32_array(JS::VM&, GC::Root<JS::Float32Array> const&);
static WebIDL::ExceptionOr<GC::Ref<DOMMatrix>> from_float64_array(JS::VM&, GC::Root<JS::Float64Array> const&);
void set_m11(double value);
void set_m12(double value);

View file

@ -230,14 +230,10 @@ WebIDL::ExceptionOr<GC::Ref<DOMMatrixReadOnly>> DOMMatrixReadOnly::from_matrix(J
}
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-fromfloat32array
WebIDL::ExceptionOr<GC::Ref<DOMMatrixReadOnly>> DOMMatrixReadOnly::from_float32_array(JS::VM& vm, GC::Root<WebIDL::BufferSource> const& array32)
WebIDL::ExceptionOr<GC::Ref<DOMMatrixReadOnly>> DOMMatrixReadOnly::from_float32_array(JS::VM& vm, GC::Root<JS::Float32Array> const& array)
{
if (!is<JS::Float32Array>(*array32))
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "Float32Array");
auto& realm = *vm.current_realm();
auto& float32_array = static_cast<JS::Float32Array&>(*array32->raw_object());
ReadonlySpan<float> elements = float32_array.data();
ReadonlySpan<float> elements = array->data();
// If array32 has 6 elements, return the result of invoking create a 2d matrix of type DOMMatrixReadOnly or DOMMatrix as appropriate, with a sequence of numbers taking the values from array32 in the provided order.
if (elements.size() == 6)
@ -255,14 +251,10 @@ WebIDL::ExceptionOr<GC::Ref<DOMMatrixReadOnly>> DOMMatrixReadOnly::from_float32_
}
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-fromfloat64array
WebIDL::ExceptionOr<GC::Ref<DOMMatrixReadOnly>> DOMMatrixReadOnly::from_float64_array(JS::VM& vm, GC::Root<WebIDL::BufferSource> const& array64)
WebIDL::ExceptionOr<GC::Ref<DOMMatrixReadOnly>> DOMMatrixReadOnly::from_float64_array(JS::VM& vm, GC::Root<JS::Float64Array> const& array)
{
if (!is<JS::Float64Array>(*array64))
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "Float64Array");
auto& realm = *vm.current_realm();
auto& float64_array = static_cast<JS::Float64Array&>(*array64->raw_object());
ReadonlySpan<double> elements = float64_array.data();
ReadonlySpan<double> elements = array->data();
// If array64 has 6 elements, return the result of invoking create a 2d matrix of type DOMMatrixReadOnly or DOMMatrix as appropriate, with a sequence of numbers taking the values from array64 in the provided order.
if (elements.size() == 6)

View file

@ -63,8 +63,8 @@ public:
virtual ~DOMMatrixReadOnly() override;
static WebIDL::ExceptionOr<GC::Ref<DOMMatrixReadOnly>> from_matrix(JS::VM&, DOMMatrixInit& other);
static WebIDL::ExceptionOr<GC::Ref<DOMMatrixReadOnly>> from_float32_array(JS::VM&, GC::Root<WebIDL::BufferSource> const& array32);
static WebIDL::ExceptionOr<GC::Ref<DOMMatrixReadOnly>> from_float64_array(JS::VM&, GC::Root<WebIDL::BufferSource> const& array64);
static WebIDL::ExceptionOr<GC::Ref<DOMMatrixReadOnly>> from_float32_array(JS::VM&, GC::Root<JS::Float32Array> const&);
static WebIDL::ExceptionOr<GC::Ref<DOMMatrixReadOnly>> from_float64_array(JS::VM&, GC::Root<JS::Float64Array> const&);
// https://drafts.fxtf.org/geometry/#dommatrix-attributes
double m11() const { return m_matrix[0, 0]; }

View file

@ -47,17 +47,10 @@ WebIDL::ExceptionOr<GC::Ref<ImageData>> ImageData::construct_impl(JS::Realm& rea
}
// https://html.spec.whatwg.org/multipage/canvas.html#dom-imagedata-with-data
WebIDL::ExceptionOr<GC::Ref<ImageData>> ImageData::create(JS::Realm& realm, GC::Root<WebIDL::BufferSource> const& data, u32 sw, Optional<u32> sh, Optional<ImageDataSettings> const& settings)
WebIDL::ExceptionOr<GC::Ref<ImageData>> ImageData::create(JS::Realm& realm, GC::Root<JS::Uint8ClampedArray> const& uint8_clamped_array_data, u32 sw, Optional<u32> sh, Optional<ImageDataSettings> const& settings)
{
auto& vm = realm.vm();
if (!is<JS::Uint8ClampedArray>(*data->raw_object()))
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "Uint8ClampedArray");
auto& uint8_clamped_array_data = static_cast<JS::Uint8ClampedArray&>(*data->raw_object());
// 1. Let length be the number of bytes in data.
auto length = uint8_clamped_array_data.byte_length().length();
auto length = uint8_clamped_array_data->byte_length().length();
// 2. If length is not a nonzero integral multiple of four, then throw an "InvalidStateError" DOMException.
if (length == 0 || length % 4 != 0)
@ -81,10 +74,10 @@ WebIDL::ExceptionOr<GC::Ref<ImageData>> ImageData::create(JS::Realm& realm, GC::
// 7. Initialize this given sw, sh, settings set to settings, and source set to data.
// FIXME: This seems to be a spec issue, sh is an optional but height always have a value.
return initialize(realm, height, sw, settings, uint8_clamped_array_data);
return initialize(realm, height, sw, settings, *uint8_clamped_array_data);
}
WebIDL::ExceptionOr<GC::Ref<ImageData>> ImageData::construct_impl(JS::Realm& realm, GC::Root<WebIDL::BufferSource> const& data, u32 sw, Optional<u32> sh, Optional<ImageDataSettings> const& settings)
WebIDL::ExceptionOr<GC::Ref<ImageData>> ImageData::construct_impl(JS::Realm& realm, GC::Root<JS::Uint8ClampedArray> const& data, u32 sw, Optional<u32> sh, Optional<ImageDataSettings> const& settings)
{
return ImageData::create(realm, data, sw, move(sh), settings);
}

View file

@ -28,10 +28,10 @@ class ImageData final
public:
[[nodiscard]] static GC::Ref<ImageData> create(JS::Realm&);
[[nodiscard]] static WebIDL::ExceptionOr<GC::Ref<ImageData>> create(JS::Realm&, u32 sw, u32 sh, Optional<ImageDataSettings> const& settings = {});
[[nodiscard]] static WebIDL::ExceptionOr<GC::Ref<ImageData>> create(JS::Realm&, GC::Root<WebIDL::BufferSource> const& data, u32 sw, Optional<u32> sh = {}, Optional<ImageDataSettings> const& settings = {});
[[nodiscard]] static WebIDL::ExceptionOr<GC::Ref<ImageData>> create(JS::Realm&, GC::Root<JS::Uint8ClampedArray> const& data, u32 sw, Optional<u32> sh = {}, Optional<ImageDataSettings> const& settings = {});
[[nodiscard]] static WebIDL::ExceptionOr<GC::Ref<ImageData>> construct_impl(JS::Realm&, u32 sw, u32 sh, Optional<ImageDataSettings> const& settings = {});
[[nodiscard]] static WebIDL::ExceptionOr<GC::Ref<ImageData>> construct_impl(JS::Realm&, GC::Root<WebIDL::BufferSource> const& data, u32 sw, Optional<u32> sh = {}, Optional<ImageDataSettings> const& settings = {});
[[nodiscard]] static WebIDL::ExceptionOr<GC::Ref<ImageData>> construct_impl(JS::Realm&, GC::Root<JS::Uint8ClampedArray> const& data, u32 sw, Optional<u32> sh = {}, Optional<ImageDataSettings> const& settings = {});
virtual ~ImageData() override;

View file

@ -135,9 +135,8 @@ Vector<f32> AnalyserNode::current_frequency_data()
}
// https://webaudio.github.io/web-audio-api/#dom-analysernode-getfloatfrequencydata
WebIDL::ExceptionOr<void> AnalyserNode::get_float_frequency_data(GC::Root<WebIDL::BufferSource> const& array)
WebIDL::ExceptionOr<void> AnalyserNode::get_float_frequency_data(GC::Root<JS::Float32Array> const& array)
{
// Write the current frequency data into array. If array has fewer elements than the frequencyBinCount,
// the excess elements will be dropped. If array has more elements than the frequencyBinCount, the
// excess elements will be ignored. The most recent fftSize frames are used in computing the frequency data.
@ -147,22 +146,17 @@ WebIDL::ExceptionOr<void> AnalyserNode::get_float_frequency_data(GC::Root<WebIDL
// quantum as a previous call, the current frequency data is not updated with the same data. Instead, the
// previously computed data is returned.
auto& vm = this->vm();
if (!is<JS::Float32Array>(*array->raw_object()))
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "Float32Array");
auto& output_array = static_cast<JS::Float32Array&>(*array->raw_object());
size_t floats_to_write = min(output_array.data().size(), frequency_bin_count());
auto output_data = array->data();
size_t floats_to_write = min(output_data.size(), static_cast<size_t>(frequency_bin_count()));
for (size_t i = 0; i < floats_to_write; i++) {
output_array.data()[i] = frequency_data[i];
output_data[i] = frequency_data[i];
}
return {};
}
// https://webaudio.github.io/web-audio-api/#dom-analysernode-getbytefrequencydata
WebIDL::ExceptionOr<void> AnalyserNode::get_byte_frequency_data(GC::Root<WebIDL::BufferSource> const& array)
WebIDL::ExceptionOr<void> AnalyserNode::get_byte_frequency_data(GC::Root<JS::Uint8Array> const& array)
{
// FIXME: If another call to getByteFrequencyData() or getFloatFrequencyData() occurs within the same render
// quantum as a previous call, the current frequency data is not updated with the same data. Instead,
@ -194,17 +188,17 @@ WebIDL::ExceptionOr<void> AnalyserNode::get_byte_frequency_data(GC::Root<WebIDL:
// Write the current frequency data into array. If arrays byte length is less than frequencyBinCount,
// the excess elements will be dropped. If arrays byte length is greater than the frequencyBinCount ,
// the excess elements will be ignored. The most recent fftSize frames are used in computing the frequency data.
auto& output_buffer = array->viewed_array_buffer()->buffer();
size_t bytes_to_write = min(array->byte_length(), frequency_bin_count());
auto output_data = array->data();
size_t bytes_to_write = min(output_data.size(), static_cast<size_t>(frequency_bin_count()));
for (size_t i = 0; i < bytes_to_write; i++)
output_buffer[i] = byte_data[i];
output_data[i] = byte_data[i];
return {};
}
// https://webaudio.github.io/web-audio-api/#dom-analysernode-getfloattimedomaindata
WebIDL::ExceptionOr<void> AnalyserNode::get_float_time_domain_data(GC::Root<WebIDL::BufferSource> const& array)
WebIDL::ExceptionOr<void> AnalyserNode::get_float_time_domain_data(GC::Root<JS::Float32Array> const& array)
{
// Write the current time-domain data (waveform data) into array. If array has fewer elements than the
// value of fftSize, the excess elements will be dropped. If array has more elements than the value of
@ -212,22 +206,17 @@ WebIDL::ExceptionOr<void> AnalyserNode::get_float_time_domain_data(GC::Root<WebI
Vector<f32> time_domain_data = current_time_domain_data();
auto& vm = this->vm();
if (!is<JS::Float32Array>(*array->raw_object()))
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "Float32Array");
auto& output_array = static_cast<JS::Float32Array&>(*array->raw_object());
size_t floats_to_write = min(output_array.data().size(), frequency_bin_count());
auto output_data = array->data();
size_t floats_to_write = min(output_data.size(), static_cast<size_t>(fft_size()));
for (size_t i = 0; i < floats_to_write; i++) {
output_array.data()[i] = time_domain_data[i];
output_data[i] = time_domain_data[i];
}
return {};
}
// https://webaudio.github.io/web-audio-api/#dom-analysernode-getbytetimedomaindata
WebIDL::ExceptionOr<void> AnalyserNode::get_byte_time_domain_data(GC::Root<WebIDL::BufferSource> const& array)
WebIDL::ExceptionOr<void> AnalyserNode::get_byte_time_domain_data(GC::Root<JS::Uint8Array> const& array)
{
// Write the current time-domain data (waveform data) into array. If arrays byte length is less than
// fftSize, the excess elements will be dropped. If arrays byte length is greater than the fftSize,
@ -247,11 +236,11 @@ WebIDL::ExceptionOr<void> AnalyserNode::get_byte_time_domain_data(GC::Root<WebID
byte_data.unchecked_append(static_cast<u8>(x));
}
auto& output_buffer = array->viewed_array_buffer()->buffer();
size_t bytes_to_write = min(array->byte_length(), fft_size());
auto output_data = array->data();
size_t bytes_to_write = min(output_data.size(), static_cast<size_t>(fft_size()));
for (size_t i = 0; i < bytes_to_write; i++)
output_buffer[i] = byte_data[i];
output_data[i] = byte_data[i];
return {};
}

View file

@ -33,10 +33,10 @@ public:
virtual WebIDL::UnsignedLong number_of_inputs() override { return 1; }
virtual WebIDL::UnsignedLong number_of_outputs() override { return 1; }
WebIDL::ExceptionOr<void> get_float_frequency_data(GC::Root<WebIDL::BufferSource> const& array); // Float32Array
WebIDL::ExceptionOr<void> get_byte_frequency_data(GC::Root<WebIDL::BufferSource> const& array); // Uint8Array
WebIDL::ExceptionOr<void> get_float_time_domain_data(GC::Root<WebIDL::BufferSource> const& array); // Float32Array
WebIDL::ExceptionOr<void> get_byte_time_domain_data(GC::Root<WebIDL::BufferSource> const& array); // Uint8Array
WebIDL::ExceptionOr<void> get_float_frequency_data(GC::Root<JS::Float32Array> const&);
WebIDL::ExceptionOr<void> get_byte_frequency_data(GC::Root<JS::Uint8Array> const&);
WebIDL::ExceptionOr<void> get_float_time_domain_data(GC::Root<JS::Float32Array> const&);
WebIDL::ExceptionOr<void> get_byte_time_domain_data(GC::Root<JS::Uint8Array> const&);
unsigned long fft_size() const { return m_fft_size; }
unsigned long frequency_bin_count() const { return m_fft_size / 2; }

View file

@ -82,7 +82,7 @@ WebIDL::ExceptionOr<GC::Ref<JS::Float32Array>> AudioBuffer::get_channel_data(Web
}
// https://webaudio.github.io/web-audio-api/#dom-audiobuffer-copyfromchannel
WebIDL::ExceptionOr<void> AudioBuffer::copy_from_channel(GC::Root<WebIDL::BufferSource> const& destination, WebIDL::UnsignedLong channel_number, WebIDL::UnsignedLong buffer_offset) const
WebIDL::ExceptionOr<void> AudioBuffer::copy_from_channel(GC::Root<JS::Float32Array> const& destination, WebIDL::UnsignedLong channel_number, WebIDL::UnsignedLong buffer_offset) const
{
// The copyFromChannel() method copies the samples from the specified channel of the AudioBuffer to the destination array.
//
@ -91,10 +91,7 @@ WebIDL::ExceptionOr<void> AudioBuffer::copy_from_channel(GC::Root<WebIDL::Buffer
// then the remaining elements of destination are not modified.
auto& vm = this->vm();
if (!is<JS::Float32Array>(*destination->raw_object()))
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "Float32Array");
auto& float32_array = static_cast<JS::Float32Array&>(*destination->raw_object());
if (float32_array.viewed_array_buffer()->is_shared_array_buffer())
if (destination->viewed_array_buffer()->is_shared_array_buffer())
return vm.throw_completion<JS::TypeError>(JS::ErrorType::SharedArrayBuffer, "Float32Array");
auto const channel = TRY(get_channel_data(channel_number));
@ -103,14 +100,15 @@ WebIDL::ExceptionOr<void> AudioBuffer::copy_from_channel(GC::Root<WebIDL::Buffer
if (buffer_offset >= channel_length)
return {};
u32 count = min(float32_array.data().size(), channel_length - buffer_offset);
channel->data().slice(buffer_offset, count).copy_to(float32_array.data());
auto destination_data = destination->data();
auto count = min(destination_data.size(), channel_length - buffer_offset);
channel->data().slice(buffer_offset, count).copy_to(destination_data.slice(0, count));
return {};
}
// https://webaudio.github.io/web-audio-api/#dom-audiobuffer-copytochannel
WebIDL::ExceptionOr<void> AudioBuffer::copy_to_channel(GC::Root<WebIDL::BufferSource> const& source, WebIDL::UnsignedLong channel_number, WebIDL::UnsignedLong buffer_offset)
WebIDL::ExceptionOr<void> AudioBuffer::copy_to_channel(GC::Root<JS::Float32Array> const& source, WebIDL::UnsignedLong channel_number, WebIDL::UnsignedLong buffer_offset)
{
// The copyToChannel() method copies the samples to the specified channel of the AudioBuffer from the source array.
//
@ -121,10 +119,7 @@ WebIDL::ExceptionOr<void> AudioBuffer::copy_to_channel(GC::Root<WebIDL::BufferSo
// then the remaining elements of buffer are not modified.
auto& vm = this->vm();
if (!is<JS::Float32Array>(*source->raw_object()))
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "Float32Array");
auto const& float32_array = static_cast<JS::Float32Array const&>(*source->raw_object());
if (float32_array.viewed_array_buffer()->is_shared_array_buffer())
if (source->viewed_array_buffer()->is_shared_array_buffer())
return vm.throw_completion<JS::TypeError>(JS::ErrorType::SharedArrayBuffer, "Float32Array");
auto channel = TRY(get_channel_data(channel_number));
@ -133,8 +128,9 @@ WebIDL::ExceptionOr<void> AudioBuffer::copy_to_channel(GC::Root<WebIDL::BufferSo
if (buffer_offset >= channel_length)
return {};
u32 count = min(float32_array.data().size(), channel_length - buffer_offset);
float32_array.data().slice(0, count).copy_to(channel->data().slice(buffer_offset, count));
auto source_data = source->data();
auto count = min(source_data.size(), channel_length - buffer_offset);
source_data.slice(0, count).copy_to(channel->data().slice(buffer_offset, count));
return {};
}

View file

@ -37,8 +37,8 @@ public:
double duration() const;
WebIDL::UnsignedLong number_of_channels() const;
WebIDL::ExceptionOr<GC::Ref<JS::Float32Array>> get_channel_data(WebIDL::UnsignedLong channel) const;
WebIDL::ExceptionOr<void> copy_from_channel(GC::Root<WebIDL::BufferSource> const&, WebIDL::UnsignedLong channel_number, WebIDL::UnsignedLong buffer_offset = 0) const;
WebIDL::ExceptionOr<void> copy_to_channel(GC::Root<WebIDL::BufferSource> const&, WebIDL::UnsignedLong channel_number, WebIDL::UnsignedLong buffer_offset = 0);
WebIDL::ExceptionOr<void> copy_from_channel(GC::Root<JS::Float32Array> const&, WebIDL::UnsignedLong channel_number, WebIDL::UnsignedLong buffer_offset = 0) const;
WebIDL::ExceptionOr<void> copy_to_channel(GC::Root<JS::Float32Array> const&, WebIDL::UnsignedLong channel_number, WebIDL::UnsignedLong buffer_offset = 0);
private:
explicit AudioBuffer(JS::Realm&, AudioBufferOptions const&);

View file

@ -227,7 +227,7 @@ void BaseAudioContext::queue_control_message(ControlMessage message)
}
// https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-decodeaudiodata
GC::Ref<WebIDL::Promise> BaseAudioContext::decode_audio_data(GC::Root<WebIDL::BufferSource> audio_data, GC::Ptr<WebIDL::CallbackType> success_callback, GC::Ptr<WebIDL::CallbackType> error_callback)
GC::Ref<WebIDL::Promise> BaseAudioContext::decode_audio_data(GC::Root<JS::ArrayBuffer> const& audio_data, GC::Ptr<WebIDL::CallbackType> success_callback, GC::Ptr<WebIDL::CallbackType> error_callback)
{
auto& realm = this->realm();
@ -252,7 +252,7 @@ GC::Ref<WebIDL::Promise> BaseAudioContext::decode_audio_data(GC::Root<WebIDL::Bu
// FIXME: 3.2. Detach the audioData ArrayBuffer. If this operations throws, jump to the step 3.
// 3.3. Queue a decoding operation to be performed on another thread.
queue_a_decoding_operation(promise, move(audio_data), success_callback, error_callback);
queue_a_decoding_operation(promise, audio_data, success_callback, error_callback);
}
// 4. Else, execute the following error steps:
@ -281,7 +281,7 @@ GC::Ref<WebIDL::Promise> BaseAudioContext::decode_audio_data(GC::Root<WebIDL::Bu
}
// https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-decodeaudiodata
void BaseAudioContext::queue_a_decoding_operation(GC::Ref<JS::PromiseCapability> promise, [[maybe_unused]] GC::Root<WebIDL::BufferSource> audio_data, GC::Ptr<WebIDL::CallbackType> success_callback, GC::Ptr<WebIDL::CallbackType> error_callback)
void BaseAudioContext::queue_a_decoding_operation(GC::Ref<JS::PromiseCapability> promise, [[maybe_unused]] GC::Root<JS::ArrayBuffer> audio_data, GC::Ptr<WebIDL::CallbackType> success_callback, GC::Ptr<WebIDL::CallbackType> error_callback)
{
auto& realm = this->realm();

View file

@ -88,7 +88,7 @@ public:
WebIDL::UnsignedLong number_of_output_channels);
WebIDL::ExceptionOr<GC::Ref<StereoPannerNode>> create_stereo_panner();
GC::Ref<WebIDL::Promise> decode_audio_data(GC::Root<WebIDL::BufferSource>, GC::Ptr<WebIDL::CallbackType>, GC::Ptr<WebIDL::CallbackType>);
GC::Ref<WebIDL::Promise> decode_audio_data(GC::Root<JS::ArrayBuffer> const&, GC::Ptr<WebIDL::CallbackType>, GC::Ptr<WebIDL::CallbackType>);
void queue_control_message(ControlMessage);
@ -109,7 +109,7 @@ private:
// https://webaudio.github.io/web-audio-api/#render-quantum-size
static constexpr WebIDL::UnsignedLong s_render_quantum_size { 128 };
void queue_a_decoding_operation(GC::Ref<JS::PromiseCapability>, GC::Root<WebIDL::BufferSource>, GC::Ptr<WebIDL::CallbackType>, GC::Ptr<WebIDL::CallbackType>);
void queue_a_decoding_operation(GC::Ref<JS::PromiseCapability>, GC::Root<JS::ArrayBuffer>, GC::Ptr<WebIDL::CallbackType>, GC::Ptr<WebIDL::CallbackType>);
u64 m_next_node_id { 0 };

View file

@ -66,7 +66,7 @@ GC::Ref<AudioParam> BiquadFilterNode::gain() const
}
// https://webaudio.github.io/web-audio-api/#dom-biquadfilternode-getfrequencyresponse
WebIDL::ExceptionOr<void> BiquadFilterNode::get_frequency_response(GC::Root<WebIDL::BufferSource> const& frequency_hz, GC::Root<WebIDL::BufferSource> const& mag_response, GC::Root<WebIDL::BufferSource> const& phase_response)
WebIDL::ExceptionOr<void> BiquadFilterNode::get_frequency_response(GC::Root<JS::Float32Array> const& frequency_hz, GC::Root<JS::Float32Array> const& mag_response, GC::Root<JS::Float32Array> const& phase_response)
{
(void)frequency_hz;
(void)mag_response;

View file

@ -38,7 +38,7 @@ public:
GC::Ref<AudioParam> detune() const;
GC::Ref<AudioParam> q() const;
GC::Ref<AudioParam> gain() const;
WebIDL::ExceptionOr<void> get_frequency_response(GC::Root<WebIDL::BufferSource> const&, GC::Root<WebIDL::BufferSource> const&, GC::Root<WebIDL::BufferSource> const&);
WebIDL::ExceptionOr<void> get_frequency_response(GC::Root<JS::Float32Array> const&, GC::Root<JS::Float32Array> const&, GC::Root<JS::Float32Array> const&);
static WebIDL::ExceptionOr<GC::Ref<BiquadFilterNode>> create(JS::Realm&, GC::Ref<BaseAudioContext>, BiquadFilterOptions const& = {});
static WebIDL::ExceptionOr<GC::Ref<BiquadFilterNode>> construct_impl(JS::Realm&, GC::Ref<BaseAudioContext>, BiquadFilterOptions const& = {});

View file

@ -953,53 +953,128 @@ static void generate_object_to_cpp(SourceGenerator& scoped_generator, IDL::Type
static void generate_buffer_source_to_cpp(SourceGenerator& scoped_generator, IDL::Type const& type, bool optional)
{
size_t buffer_source_nesting_level = optional ? 2 : 1;
auto buffer_source_indent = ByteString::repeated(' ', buffer_source_nesting_level * 4);
scoped_generator.set("buffer_source.indent", buffer_source_indent);
auto is_exact_javascript_buffer_source_type = is_javascript_builtin_buffer_source_type(type);
if (is_exact_javascript_buffer_source_type)
scoped_generator.set("parameter.type.buffer_cpp", ByteString::formatted("JS::{}", type.name()));
else
scoped_generator.set("parameter.type.buffer_cpp", "WebIDL::BufferSource");
if (optional || type.is_nullable()) {
scoped_generator.append(R"~~~(
Optional<GC::Root<WebIDL::BufferSource>> @cpp_name@;
Optional<GC::Root<@parameter.type.buffer_cpp@>> @cpp_name@;
)~~~");
} else {
scoped_generator.append(R"~~~(
GC::Root<WebIDL::BufferSource> @cpp_name@;
GC::Root<@parameter.type.buffer_cpp@> @cpp_name@;
)~~~");
}
if (optional) {
scoped_generator.append(R"~~~(
if (optional && type.is_nullable()) {
if (is_exact_javascript_buffer_source_type) {
scoped_generator.append(R"~~~(
if (!@js_name@@js_suffix@.is_undefined()) {
)~~~");
} else if (type.is_nullable()) {
scoped_generator.append(R"~~~(
if (@js_name@@js_suffix@.is_undefined())
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "@parameter.type.name@");
)~~~");
if (!@js_name@@js_suffix@.is_null()) {
auto @cpp_name@_builtin_buffer = @js_name@@js_suffix@.as_if<@parameter.type.buffer_cpp@>();
if (!@cpp_name@_builtin_buffer) {
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "@parameter.type.name@");
}
@cpp_name@ = GC::make_root(*@cpp_name@_builtin_buffer);
}
}
if (type.is_nullable()) {
scoped_generator.append(R"~~~(
@buffer_source.indent@if (!@js_name@@js_suffix@.is_null()) {
)~~~");
} else {
scoped_generator.append(R"~~~(
if (!@js_name@@js_suffix@.is_undefined()) {
if (!@js_name@@js_suffix@.is_null()) {
if (!@js_name@@js_suffix@.is_object() || !(is<JS::TypedArrayBase>(@js_name@@js_suffix@.as_object()) || is<JS::ArrayBuffer>(@js_name@@js_suffix@.as_object()) || is<JS::DataView>(@js_name@@js_suffix@.as_object()))) {
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "@parameter.type.name@");
}
@cpp_name@ = GC::make_root(realm.create<WebIDL::BufferSource>(@js_name@@js_suffix@.as_object()));
}
}
scoped_generator.append(R"~~~(
@buffer_source.indent@ if (!@js_name@@js_suffix@.is_object() || !(is<JS::TypedArrayBase>(@js_name@@js_suffix@.as_object()) || is<JS::ArrayBuffer>(@js_name@@js_suffix@.as_object()) || is<JS::DataView>(@js_name@@js_suffix@.as_object())))
@buffer_source.indent@ return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "@parameter.type.name@");
@buffer_source.indent@ @cpp_name@ = GC::make_root(realm.create<WebIDL::BufferSource>(@js_name@@js_suffix@.as_object()));
)~~~");
if (type.is_nullable()) {
scoped_generator.append(R"~~~(
@buffer_source.indent@}
)~~~");
}
return;
}
if (optional) {
scoped_generator.append(R"~~~(
if (is_exact_javascript_buffer_source_type) {
scoped_generator.append(R"~~~(
if (!@js_name@@js_suffix@.is_undefined()) {
auto @cpp_name@_builtin_buffer = @js_name@@js_suffix@.as_if<@parameter.type.buffer_cpp@>();
if (!@cpp_name@_builtin_buffer) {
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "@parameter.type.name@");
}
@cpp_name@ = GC::make_root(*@cpp_name@_builtin_buffer);
}
)~~~");
} else {
scoped_generator.append(R"~~~(
if (!@js_name@@js_suffix@.is_undefined()) {
if (!@js_name@@js_suffix@.is_object() || !(is<JS::TypedArrayBase>(@js_name@@js_suffix@.as_object()) || is<JS::ArrayBuffer>(@js_name@@js_suffix@.as_object()) || is<JS::DataView>(@js_name@@js_suffix@.as_object()))) {
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "@parameter.type.name@");
}
@cpp_name@ = GC::make_root(realm.create<WebIDL::BufferSource>(@js_name@@js_suffix@.as_object()));
}
)~~~");
}
return;
}
if (type.is_nullable()) {
if (is_exact_javascript_buffer_source_type) {
scoped_generator.append(R"~~~(
if (@js_name@@js_suffix@.is_undefined()) {
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "@parameter.type.name@");
}
if (!@js_name@@js_suffix@.is_null()) {
auto @cpp_name@_builtin_buffer = @js_name@@js_suffix@.as_if<@parameter.type.buffer_cpp@>();
if (!@cpp_name@_builtin_buffer) {
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "@parameter.type.name@");
}
@cpp_name@ = GC::make_root(*@cpp_name@_builtin_buffer);
}
)~~~");
} else {
scoped_generator.append(R"~~~(
if (@js_name@@js_suffix@.is_undefined()) {
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "@parameter.type.name@");
}
if (!@js_name@@js_suffix@.is_null()) {
if (!@js_name@@js_suffix@.is_object() || !(is<JS::TypedArrayBase>(@js_name@@js_suffix@.as_object()) || is<JS::ArrayBuffer>(@js_name@@js_suffix@.as_object()) || is<JS::DataView>(@js_name@@js_suffix@.as_object()))) {
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "@parameter.type.name@");
}
@cpp_name@ = GC::make_root(realm.create<WebIDL::BufferSource>(@js_name@@js_suffix@.as_object()));
}
)~~~");
}
return;
}
if (is_exact_javascript_buffer_source_type) {
scoped_generator.append(R"~~~(
auto @cpp_name@_builtin_buffer = @js_name@@js_suffix@.as_if<@parameter.type.buffer_cpp@>();
if (!@cpp_name@_builtin_buffer) {
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "@parameter.type.name@");
}
@cpp_name@ = GC::make_root(*@cpp_name@_builtin_buffer);
)~~~");
} else {
scoped_generator.append(R"~~~(
if (!@js_name@@js_suffix@.is_object() || !(is<JS::TypedArrayBase>(@js_name@@js_suffix@.as_object()) || is<JS::ArrayBuffer>(@js_name@@js_suffix@.as_object()) || is<JS::DataView>(@js_name@@js_suffix@.as_object()))) {
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "@parameter.type.name@");
}
@cpp_name@ = GC::make_root(realm.create<WebIDL::BufferSource>(@js_name@@js_suffix@.as_object()));
)~~~");
}
}

View file

@ -2,8 +2,7 @@ Harness status: OK
Found 111 tests
85 Pass
26 Fail
111 Pass
Pass encodeInto() into ArrayBuffer with Hi and destination length 0, offset 0, filler 0
Pass encodeInto() into SharedArrayBuffer with Hi and destination length 0, offset 0, filler 0
Pass encodeInto() into ArrayBuffer with Hi and destination length 0, offset 4, filler 0
@ -88,30 +87,30 @@ Pass encodeInto() into ArrayBuffer with ¥¥ and destination length 4, offset 0,
Pass encodeInto() into SharedArrayBuffer with ¥¥ and destination length 4, offset 0, filler random
Pass encodeInto() into ArrayBuffer with ¥¥ and destination length 4, offset 4, filler random
Pass encodeInto() into SharedArrayBuffer with ¥¥ and destination length 4, offset 4, filler random
Fail Invalid encodeInto() destination: DataView, backed by: ArrayBuffer
Fail Invalid encodeInto() destination: DataView, backed by: SharedArrayBuffer
Fail Invalid encodeInto() destination: Int8Array, backed by: ArrayBuffer
Fail Invalid encodeInto() destination: Int8Array, backed by: SharedArrayBuffer
Fail Invalid encodeInto() destination: Int16Array, backed by: ArrayBuffer
Fail Invalid encodeInto() destination: Int16Array, backed by: SharedArrayBuffer
Fail Invalid encodeInto() destination: Int32Array, backed by: ArrayBuffer
Fail Invalid encodeInto() destination: Int32Array, backed by: SharedArrayBuffer
Fail Invalid encodeInto() destination: Uint16Array, backed by: ArrayBuffer
Fail Invalid encodeInto() destination: Uint16Array, backed by: SharedArrayBuffer
Fail Invalid encodeInto() destination: Uint32Array, backed by: ArrayBuffer
Fail Invalid encodeInto() destination: Uint32Array, backed by: SharedArrayBuffer
Fail Invalid encodeInto() destination: Uint8ClampedArray, backed by: ArrayBuffer
Fail Invalid encodeInto() destination: Uint8ClampedArray, backed by: SharedArrayBuffer
Fail Invalid encodeInto() destination: BigInt64Array, backed by: ArrayBuffer
Fail Invalid encodeInto() destination: BigInt64Array, backed by: SharedArrayBuffer
Fail Invalid encodeInto() destination: BigUint64Array, backed by: ArrayBuffer
Fail Invalid encodeInto() destination: BigUint64Array, backed by: SharedArrayBuffer
Fail Invalid encodeInto() destination: Float16Array, backed by: ArrayBuffer
Fail Invalid encodeInto() destination: Float16Array, backed by: SharedArrayBuffer
Fail Invalid encodeInto() destination: Float32Array, backed by: ArrayBuffer
Fail Invalid encodeInto() destination: Float32Array, backed by: SharedArrayBuffer
Fail Invalid encodeInto() destination: Float64Array, backed by: ArrayBuffer
Fail Invalid encodeInto() destination: Float64Array, backed by: SharedArrayBuffer
Fail Invalid encodeInto() destination: ArrayBuffer
Fail Invalid encodeInto() destination: SharedArrayBuffer
Pass Invalid encodeInto() destination: DataView, backed by: ArrayBuffer
Pass Invalid encodeInto() destination: DataView, backed by: SharedArrayBuffer
Pass Invalid encodeInto() destination: Int8Array, backed by: ArrayBuffer
Pass Invalid encodeInto() destination: Int8Array, backed by: SharedArrayBuffer
Pass Invalid encodeInto() destination: Int16Array, backed by: ArrayBuffer
Pass Invalid encodeInto() destination: Int16Array, backed by: SharedArrayBuffer
Pass Invalid encodeInto() destination: Int32Array, backed by: ArrayBuffer
Pass Invalid encodeInto() destination: Int32Array, backed by: SharedArrayBuffer
Pass Invalid encodeInto() destination: Uint16Array, backed by: ArrayBuffer
Pass Invalid encodeInto() destination: Uint16Array, backed by: SharedArrayBuffer
Pass Invalid encodeInto() destination: Uint32Array, backed by: ArrayBuffer
Pass Invalid encodeInto() destination: Uint32Array, backed by: SharedArrayBuffer
Pass Invalid encodeInto() destination: Uint8ClampedArray, backed by: ArrayBuffer
Pass Invalid encodeInto() destination: Uint8ClampedArray, backed by: SharedArrayBuffer
Pass Invalid encodeInto() destination: BigInt64Array, backed by: ArrayBuffer
Pass Invalid encodeInto() destination: BigInt64Array, backed by: SharedArrayBuffer
Pass Invalid encodeInto() destination: BigUint64Array, backed by: ArrayBuffer
Pass Invalid encodeInto() destination: BigUint64Array, backed by: SharedArrayBuffer
Pass Invalid encodeInto() destination: Float16Array, backed by: ArrayBuffer
Pass Invalid encodeInto() destination: Float16Array, backed by: SharedArrayBuffer
Pass Invalid encodeInto() destination: Float32Array, backed by: ArrayBuffer
Pass Invalid encodeInto() destination: Float32Array, backed by: SharedArrayBuffer
Pass Invalid encodeInto() destination: Float64Array, backed by: ArrayBuffer
Pass Invalid encodeInto() destination: Float64Array, backed by: SharedArrayBuffer
Pass Invalid encodeInto() destination: ArrayBuffer
Pass Invalid encodeInto() destination: SharedArrayBuffer
Pass encodeInto() and a detached output buffer