LibJS+LibWeb: Allow instantiating DataBlock with an external buffer

This requires the minimal API exposed by ByteBuffer, allowing external
users to implement them as needed instead of being forced to use a
ByteBuffer.
This commit is contained in:
Ali Mohammad Pur 2026-04-04 19:25:48 +02:00 committed by Ali Mohammad Pur
parent 0a7bef349d
commit 0bb987e809
22 changed files with 163 additions and 68 deletions

View file

@ -1063,7 +1063,7 @@ i64 asm_try_get_by_value_typed_array(VM* vm, u32 pc)
}
auto* buffer = typed_array.viewed_array_buffer();
auto const* data = buffer->buffer().data() + typed_array.byte_offset();
auto const* data = buffer->data() + typed_array.byte_offset();
Value result;
switch (typed_array.kind()) {
@ -1133,7 +1133,7 @@ i64 asm_try_put_by_value_typed_array(VM* vm, u32 pc)
return 0;
auto* buffer = typed_array.viewed_array_buffer();
auto* data = buffer->buffer().data() + typed_array.byte_offset();
auto* data = buffer->data() + typed_array.byte_offset();
auto value = vm->get(insn.src());
if (value.is_int32()) {

View file

@ -893,7 +893,7 @@ inline Value fast_typed_array_get_element(TypedArrayBase& typed_array, u32 index
}
auto const& array_buffer = *typed_array.viewed_array_buffer();
auto const* slot = reinterpret_cast<T const*>(array_buffer.buffer().offset_pointer(offset_into_array_buffer.value()));
auto const* slot = reinterpret_cast<T const*>(array_buffer.span().offset_pointer(offset_into_array_buffer.value()));
return Value { *slot };
}
@ -911,7 +911,7 @@ inline void fast_typed_array_set_element(TypedArrayBase& typed_array, u32 index,
}
auto& array_buffer = *typed_array.viewed_array_buffer();
auto* slot = reinterpret_cast<T*>(array_buffer.buffer().offset_pointer(offset_into_array_buffer.value()));
auto* slot = reinterpret_cast<T*>(array_buffer.span().offset_pointer(offset_into_array_buffer.value()));
*slot = value;
}

View file

@ -401,10 +401,10 @@ ErrorOr<void> print_array_buffer(JS::PrintContext& print_context, JS::ArrayBuffe
if (byte_length == 0)
return {};
auto& buffer = array_buffer.buffer();
auto const* buffer_data = array_buffer.data();
TRY(js_out(print_context, "\n"));
for (size_t i = 0; i < byte_length; ++i) {
TRY(js_out(print_context, "{:02x}", buffer[i]));
TRY(js_out(print_context, "{:02x}", buffer_data[i]));
if (i + 1 < byte_length) {
if ((i + 1) % 32 == 0)
TRY(js_out(print_context, "\n"));

View file

@ -43,6 +43,11 @@ GC::Ref<ArrayBuffer> ArrayBuffer::create(Realm& realm, ByteBuffer* buffer, DataB
return realm.create<ArrayBuffer>(buffer, is_shared, prototype_for_shared_state(realm, is_shared));
}
GC::Ref<ArrayBuffer> ArrayBuffer::create(Realm& realm, DataBlock::UnownedExternalBuffer buffer, DataBlock::Shared is_shared)
{
return realm.create<ArrayBuffer>(buffer, is_shared, prototype_for_shared_state(realm, is_shared));
}
ArrayBuffer::ArrayBuffer(ByteBuffer buffer, DataBlock::Shared is_shared, Object& prototype)
: Object(ConstructWithPrototypeTag::Tag, prototype)
, m_data_block(DataBlock { move(buffer), is_shared })
@ -57,6 +62,13 @@ ArrayBuffer::ArrayBuffer(ByteBuffer* buffer, DataBlock::Shared is_shared, Object
{
}
ArrayBuffer::ArrayBuffer(DataBlock::UnownedExternalBuffer buffer, DataBlock::Shared is_shared, Object& prototype)
: Object(ConstructWithPrototypeTag::Tag, prototype)
, m_data_block(DataBlock { move(buffer), is_shared })
, m_detach_key(js_undefined())
{
}
void ArrayBuffer::account_external_memory_change(size_t old_external_memory_size, size_t new_external_memory_size)
{
if (new_external_memory_size > old_external_memory_size) {
@ -83,6 +95,8 @@ void ArrayBuffer::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_detach_key);
if (auto* external = m_data_block.byte_buffer.get_pointer<DataBlock::UnownedExternalBuffer>())
visitor.visit(external->owner);
}
// 6.2.9.1 CreateByteDataBlock ( size ), https://tc39.es/ecma262/#sec-createbytedatablock
@ -195,7 +209,7 @@ ThrowCompletionOr<ArrayBuffer*> allocate_array_buffer(VM& vm, FunctionObject& co
}
// 4. Let obj be ? OrdinaryCreateFromConstructor(constructor, "%ArrayBuffer.prototype%", slots).
auto obj = TRY(ordinary_create_from_constructor<ArrayBuffer>(vm, constructor, &Intrinsics::array_buffer_prototype, nullptr, DataBlock::Shared::No));
auto obj = TRY(ordinary_create_from_constructor<ArrayBuffer>(vm, constructor, &Intrinsics::array_buffer_prototype, static_cast<ByteBuffer*>(nullptr), DataBlock::Shared::No));
// 5. Let block be ? CreateByteDataBlock(byteLength).
auto block = TRY(create_byte_data_block(vm, byte_length));
@ -269,7 +283,7 @@ ThrowCompletionOr<ArrayBuffer*> array_buffer_copy_and_detach(VM& vm, ArrayBuffer
// 12. Let toBlock be newBuffer.[[ArrayBufferData]].
// 13. Perform CopyDataBlockBytes(toBlock, 0, fromBlock, 0, copyLength).
// 14. NOTE: Neither creation of the new Data Block nor copying from the old Data Block are observable. Implementations may implement this method as a zero-copy move or a realloc.
copy_data_block_bytes(new_buffer->buffer(), 0, array_buffer.buffer(), 0, copy_length);
new_buffer->overwrite(0, array_buffer.data(), copy_length);
// 15. Perform ! DetachArrayBuffer(arrayBuffer).
MUST(detach_array_buffer(vm, array_buffer));
@ -299,10 +313,11 @@ ThrowCompletionOr<ByteBuffer> ArrayBuffer::detach_and_take_bytes(VM& vm)
return vm.throw_completion<TypeError>(ErrorType::DetachKeyMismatch, js_undefined(), detach_key());
auto old_external_memory_size = external_memory_size();
auto bytes = m_data_block.byte_buffer.visit(
[](Empty) -> ByteBuffer { VERIFY_NOT_REACHED(); },
[](ByteBuffer& value) -> ByteBuffer { return move(value); },
[](DataBlock::UnownedFixedLengthByteBuffer& value) -> ByteBuffer { return MUST(ByteBuffer::copy(value.buffer->span())); });
ByteBuffer bytes;
if (auto* buffer = m_data_block.byte_buffer.get_pointer<ByteBuffer>())
bytes = move(*buffer);
else
bytes = MUST(ByteBuffer::copy(span()));
for (auto& cached_view : m_cached_views) {
auto& view = static_cast<TypedArrayBase&>(cached_view);
if (view.viewed_array_buffer() == this)
@ -353,13 +368,11 @@ ThrowCompletionOr<ArrayBuffer*> clone_array_buffer(VM& vm, ArrayBuffer& source_b
auto* target_buffer = TRY(allocate_array_buffer(vm, realm.intrinsics().array_buffer_constructor(), source_length));
// 3. Let srcBlock be srcBuffer.[[ArrayBufferData]].
auto& source_block = source_buffer.buffer();
auto source_block = source_buffer.bytes().slice(source_byte_offset, source_length);
// 4. Let targetBlock be targetBuffer.[[ArrayBufferData]].
auto& target_block = target_buffer->buffer();
// 5. Perform CopyDataBlockBytes(targetBlock, 0, srcBlock, srcByteOffset, srcLength).
copy_data_block_bytes(target_block, 0, source_block, source_byte_offset, source_length);
target_buffer->overwrite(0, source_block.data(), source_length);
// 6. Return targetBuffer.
return target_buffer;
@ -404,7 +417,7 @@ ThrowCompletionOr<GC::Ref<ArrayBuffer>> allocate_shared_array_buffer(VM& vm, Fun
// a. Append [[ArrayBufferByteLength]] to slots.
// 5. Let obj be ? OrdinaryCreateFromConstructor(constructor, "%SharedArrayBuffer.prototype%", slots).
auto obj = TRY(ordinary_create_from_constructor<ArrayBuffer>(vm, constructor, &Intrinsics::shared_array_buffer_prototype, nullptr, DataBlock::Shared::Yes));
auto obj = TRY(ordinary_create_from_constructor<ArrayBuffer>(vm, constructor, &Intrinsics::shared_array_buffer_prototype, static_cast<ByteBuffer*>(nullptr), DataBlock::Shared::Yes));
// 6. If allocatingGrowableBuffer is true, let allocLength be maxByteLength; otherwise let allocLength be byteLength.
auto alloc_length = allocating_growable_buffer ? *max_byte_length : byte_length;

View file

@ -63,21 +63,74 @@ struct DataBlock {
size_t size = 0;
};
// AD-HOC: ECMA-262 models ArrayBuffer backing storage as a Data Block. We additionally allow
// host code to provide an external byte store via callbacks so engine-independent
// consumers like LibWeb can project spec-defined host objects onto ArrayBuffer without
// teaching LibJS about those hosts.
struct UnownedExternalBuffer {
using DataFunction = u8* (*)(void*);
using SizeFunction = size_t (*)(void*);
explicit UnownedExternalBuffer(GC::Ref<GC::Cell> owner, void* context, DataFunction data, SizeFunction size)
: context(context)
, data(data)
, size(size)
, owner(owner)
{
}
explicit UnownedExternalBuffer(GC::Ref<GC::Cell> owner, void* context, DataFunction data, size_t fixed_size)
: context(context)
, data(data)
, size(fixed_size)
, owner(owner)
{
}
void* context { nullptr };
DataFunction data { nullptr };
Variant<SizeFunction, size_t> size;
GC::Ref<GC::Cell> owner;
};
ByteBuffer& buffer()
{
return byte_buffer.visit(
[&](Empty) -> ByteBuffer& { VERIFY_NOT_REACHED(); },
[&](ByteBuffer& value) -> ByteBuffer& { return value; },
[&](UnownedFixedLengthByteBuffer& value) -> ByteBuffer& { return *value.buffer; });
[&](UnownedFixedLengthByteBuffer& value) -> ByteBuffer& { return *value.buffer; },
[&](UnownedExternalBuffer&) -> ByteBuffer& { VERIFY_NOT_REACHED(); });
}
ByteBuffer const& buffer() const { return const_cast<DataBlock*>(this)->buffer(); }
u8* data()
{
return byte_buffer.visit(
[](Empty) -> u8* { VERIFY_NOT_REACHED(); },
[](ByteBuffer& value) -> u8* { return value.data(); },
[](UnownedFixedLengthByteBuffer& value) -> u8* { return value.buffer->data(); },
[](UnownedExternalBuffer& value) -> u8* { return value.data ? value.data(value.context) : nullptr; });
}
u8 const* data() const { return const_cast<DataBlock*>(this)->data(); }
Bytes span() { return { data(), size() }; }
ReadonlyBytes span() const { return { data(), size() }; }
Bytes bytes() { return { data(), size() }; }
ReadonlyBytes bytes() const { return { data(), size() }; }
void overwrite(size_t offset, void const* source, size_t count)
{
VERIFY(offset <= size());
VERIFY(count <= size() - offset);
__builtin_memcpy(data() + offset, source, count);
}
size_t size() const
{
return byte_buffer.visit(
[](Empty) -> size_t { return 0u; },
[](ByteBuffer const& buffer) { return buffer.size(); },
[](UnownedFixedLengthByteBuffer const& value) { return value.size; });
[](UnownedFixedLengthByteBuffer const& value) { return value.size; },
[](UnownedExternalBuffer const& value) { return value.size ? value.size(value.context) : 0; });
}
size_t external_memory_size() const
@ -85,10 +138,13 @@ struct DataBlock {
return byte_buffer.visit(
[](Empty) -> size_t { return 0; },
[](ByteBuffer const& buffer) { return buffer.is_inline() ? 0 : buffer.capacity(); },
[](UnownedFixedLengthByteBuffer const&) -> size_t { return 0; });
[](UnownedFixedLengthByteBuffer const&) -> size_t { return 0; },
[](UnownedExternalBuffer const&) -> size_t { return 0; });
}
Variant<Empty, ByteBuffer, UnownedFixedLengthByteBuffer> byte_buffer;
bool is_external() const { return byte_buffer.has<UnownedExternalBuffer>(); }
Variant<Empty, ByteBuffer, UnownedFixedLengthByteBuffer, UnownedExternalBuffer> byte_buffer;
Shared is_shared = { Shared::No };
};
@ -100,6 +156,7 @@ public:
static ThrowCompletionOr<GC::Ref<ArrayBuffer>> create(Realm&, size_t, DataBlock::Shared = DataBlock::Shared::No);
static GC::Ref<ArrayBuffer> create(Realm&, ByteBuffer, DataBlock::Shared = DataBlock::Shared::No);
static GC::Ref<ArrayBuffer> create(Realm&, ByteBuffer*, DataBlock::Shared = DataBlock::Shared::No);
static GC::Ref<ArrayBuffer> create(Realm&, DataBlock::UnownedExternalBuffer, DataBlock::Shared = DataBlock::Shared::No);
virtual ~ArrayBuffer() override = default;
@ -109,6 +166,14 @@ public:
// [[ArrayBufferData]]
ByteBuffer& buffer() { return m_data_block.buffer(); }
ByteBuffer const& buffer() const { return m_data_block.buffer(); }
u8* data() { return m_data_block.data(); }
u8 const* data() const { return m_data_block.data(); }
Bytes span() { return m_data_block.span(); }
ReadonlyBytes span() const { return m_data_block.span(); }
Bytes bytes() { return m_data_block.bytes(); }
ReadonlyBytes bytes() const { return m_data_block.bytes(); }
void overwrite(size_t offset, void const* source, size_t count) { m_data_block.overwrite(offset, source, count); }
bool is_external() const { return m_data_block.is_external(); }
// Detaches this ArrayBuffer and returns its underlying bytes as a ByteBuffer for use in a TransferArrayBuffer-like
// operation. Moves the storage when we own it and copies it for externally-owned buffers (e.g. Wasm memory).
@ -185,6 +250,7 @@ public:
private:
ArrayBuffer(ByteBuffer buffer, DataBlock::Shared, Object& prototype);
ArrayBuffer(ByteBuffer* buffer, DataBlock::Shared, Object& prototype);
ArrayBuffer(DataBlock::UnownedExternalBuffer buffer, DataBlock::Shared, Object& prototype);
virtual bool is_array_buffer() const final { return true; }
@ -325,10 +391,10 @@ Value ArrayBuffer::get_value(size_t byte_index, [[maybe_unused]] bool is_typed_a
VERIFY(!is_detached());
// 2. Assert: There are sufficient bytes in arrayBuffer starting at byteIndex to represent a value of type.
VERIFY(m_data_block.buffer().bytes().slice(byte_index).size() >= sizeof(T));
VERIFY(m_data_block.bytes().slice(byte_index).size() >= sizeof(T));
// 3. Let block be arrayBuffer.[[ArrayBufferData]].
auto& block = m_data_block.buffer();
auto block = m_data_block.bytes();
// 4. Let elementSize be the Element Size value specified in Table 70 for Element Type type.
auto element_size = sizeof(T);
@ -349,7 +415,7 @@ Value ArrayBuffer::get_value(size_t byte_index, [[maybe_unused]] bool is_typed_a
// 6. Else,
else {
// a. Let rawValue be a List whose elements are bytes from block at indices in the interval from byteIndex (inclusive) to byteIndex + elementSize (exclusive).
block.bytes().slice(byte_index, element_size).copy_to(raw_value);
block.slice(byte_index, element_size).copy_to(raw_value);
}
// 7. Assert: The number of elements in rawValue is elementSize.
@ -443,7 +509,7 @@ void ArrayBuffer::set_value(size_t byte_index, Value value, [[maybe_unused]] boo
VERIFY(!is_detached());
// 2. Assert: There are sufficient bytes in arrayBuffer starting at byteIndex to represent a value of type.
VERIFY(m_data_block.buffer().bytes().slice(byte_index).size() >= sizeof(T));
VERIFY(m_data_block.bytes().slice(byte_index).size() >= sizeof(T));
// 3. Assert: value is a BigInt if IsBigIntElementType(type) is true; otherwise, value is a Number.
if constexpr (IsIntegral<T> && sizeof(T) == 8)
@ -452,7 +518,7 @@ void ArrayBuffer::set_value(size_t byte_index, Value value, [[maybe_unused]] boo
VERIFY(value.is_number());
// 4. Let block be arrayBuffer.[[ArrayBufferData]].
auto& block = m_data_block.buffer();
auto block = m_data_block.span();
// FIXME: 5. Let elementSize be the Element Size value specified in Table 70 for Element Type type.
@ -473,7 +539,7 @@ void ArrayBuffer::set_value(size_t byte_index, Value value, [[maybe_unused]] boo
// 9. Else,
else {
// a. Store the individual bytes of rawBytes into block, starting at block[byteIndex].
raw_bytes.span().copy_to(block.span().slice(byte_index));
raw_bytes.span().copy_to(block.slice(byte_index));
}
// 10. Return unused.
@ -491,9 +557,9 @@ Value ArrayBuffer::get_modify_set_value(size_t byte_index, Value value, ReadWrit
// FIXME: Check for shared buffer
auto raw_bytes_read = MUST(ByteBuffer::create_uninitialized(sizeof(T)));
m_data_block.buffer().bytes().slice(byte_index, sizeof(T)).copy_to(raw_bytes_read);
m_data_block.bytes().slice(byte_index, sizeof(T)).copy_to(raw_bytes_read);
auto raw_bytes_modified = operation(raw_bytes_read, raw_bytes);
raw_bytes_modified.span().copy_to(m_data_block.buffer().span().slice(byte_index));
raw_bytes_modified.span().copy_to(m_data_block.span().slice(byte_index));
return raw_bytes_to_numeric<T>(vm, raw_bytes_read, is_little_endian);
}

View file

@ -154,7 +154,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::resize)
return js_undefined();
// 9. Let oldBlock be O.[[ArrayBufferData]].
auto const& old_block = array_buffer_object->buffer();
// NOTE: oldBlock is read directly in step 12.
// 10. Let newBlock be ? CreateByteDataBlock(newByteLength).
auto new_block = TRY(create_byte_data_block(vm, new_byte_length));
@ -163,7 +163,10 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::resize)
auto copy_length = min(new_byte_length, array_buffer_object->byte_length());
// 12. Perform CopyDataBlockBytes(newBlock, 0, oldBlock, 0, copyLength).
copy_data_block_bytes(new_block.buffer(), 0, old_block, 0, copy_length);
if (array_buffer_object->is_external())
new_block.overwrite(0, array_buffer_object->data(), copy_length);
else
copy_data_block_bytes(new_block.buffer(), 0, array_buffer_object->buffer(), 0, copy_length);
// 13. NOTE: Neither creation of the new Data Block nor copying from the old Data Block are observable. Implementations may implement this method as in-place growth or shrinkage.
@ -264,10 +267,8 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::slice)
return vm.throw_completion<TypeError>(ErrorType::DetachedArrayBuffer);
// 24. Let fromBuf be O.[[ArrayBufferData]].
auto& from_buf = array_buffer_object->buffer();
// 25. Let toBuf be new.[[ArrayBufferData]].
auto& to_buf = new_array_buffer_object->buffer();
// NOTE: fromBuf and toBuf are read directly in step 27b.
// 26. Let currentLen be O.[[ArrayBufferByteLength]].
auto current_length = array_buffer_object->byte_length();
@ -278,7 +279,10 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::slice)
auto count = min(new_length, current_length - first);
// b. Perform CopyDataBlockBytes(toBuf, 0, fromBuf, first, count).
copy_data_block_bytes(to_buf, 0, from_buf, first, count);
if (array_buffer_object->is_external())
new_array_buffer_object->overwrite(0, array_buffer_object->data() + (size_t)first, (size_t)count);
else
copy_data_block_bytes(new_array_buffer_object->buffer(), 0, array_buffer_object->buffer(), first, count);
}
// 28. Return new.

View file

@ -107,7 +107,7 @@ protected:
}
m_viewed_array_buffer->register_cached_typed_array_view(*this);
m_data = m_viewed_array_buffer->buffer().data() + m_byte_offset;
m_data = m_viewed_array_buffer->data() + m_byte_offset;
}
u32 m_element_size { 0 };
@ -506,7 +506,7 @@ public:
}
auto length = typed_array_length(typed_array_record);
return { reinterpret_cast<UnderlyingBufferDataType const*>(m_viewed_array_buffer->buffer().data() + m_byte_offset), length };
return { reinterpret_cast<UnderlyingBufferDataType const*>(m_viewed_array_buffer->data() + m_byte_offset), length };
}
Span<UnderlyingBufferDataType> data()
@ -519,7 +519,7 @@ public:
}
auto length = typed_array_length(typed_array_record);
return { reinterpret_cast<UnderlyingBufferDataType*>(m_viewed_array_buffer->buffer().data() + m_byte_offset), length };
return { reinterpret_cast<UnderlyingBufferDataType*>(m_viewed_array_buffer->data() + m_byte_offset), length };
}
bool is_unclamped_integer_element_type() const override

View file

@ -376,7 +376,7 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::copy_within)
to_end += count_bytes;
if (!from_end.has_overflow() && !to_end.has_overflow()) {
auto* base = buffer->buffer().data();
auto* base = buffer->data();
void const* src = base + from_byte_index;
void* dst = base + to_byte_index;
memmove(dst, src, count_bytes);
@ -511,7 +511,7 @@ inline void fast_typed_array_fill(TypedArrayBase& typed_array, u32 begin, u32 en
}
auto& array_buffer = *typed_array.viewed_array_buffer();
auto* slot = reinterpret_cast<T*>(array_buffer.buffer().offset_pointer(computed_begin.value()));
auto* slot = reinterpret_cast<T*>(array_buffer.data() + computed_begin.value());
for (auto i = begin; i < end; ++i)
*(slot++) = value;
}
@ -1469,7 +1469,7 @@ static ThrowCompletionOr<void> set_typed_array_from_typed_array(VM& vm, TypedArr
auto same_shared_array_buffer = false;
// 18. If IsSharedArrayBuffer(srcBuffer) is true, IsSharedArrayBuffer(targetBuffer) is true, and srcBuffer.[[ArrayBufferData]] is targetBuffer.[[ArrayBufferData]], let sameSharedArrayBuffer be true; otherwise, let sameSharedArrayBuffer be false.
if (source_buffer->is_shared_array_buffer() && target_buffer->is_shared_array_buffer() && (&source_buffer->buffer() == &target_buffer->buffer()))
if (source_buffer->is_shared_array_buffer() && target_buffer->is_shared_array_buffer() && (source_buffer->data() == target_buffer->data()))
same_shared_array_buffer = true;
size_t source_byte_index = 0;
@ -1517,7 +1517,7 @@ static ThrowCompletionOr<void> set_typed_array_from_typed_array(VM& vm, TypedArr
// ii. Perform SetValueInBuffer(targetBuffer, targetByteIndex, Uint8, value, true, Unordered).
// iii. Set srcByteIndex to srcByteIndex + 1.
// iv. Set targetByteIndex to targetByteIndex + 1.
target_buffer->buffer().overwrite(target_byte_index, source_buffer->buffer().data() + source_byte_index, limit - target_byte_index);
target_buffer->overwrite(target_byte_index, source_buffer->data() + source_byte_index, limit - target_byte_index);
}
// 24. Else,
else {
@ -1757,8 +1757,8 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::slice)
// OPTIMIZATION: If the buffers are not detached and not shared, we can do a single bulk copy.
if (!target_buffer.is_detached() && !target_buffer.is_shared_array_buffer()
&& !source_buffer.is_detached() && !source_buffer.is_shared_array_buffer()
&& &target_buffer.buffer() != &source_buffer.buffer()) {
target_buffer.buffer().overwrite(target_byte_index, source_buffer.buffer().data() + source_byte_index.value(), limit.value() - target_byte_index);
&& target_buffer.data() != source_buffer.data()) {
target_buffer.overwrite(target_byte_index, source_buffer.data() + source_byte_index.value(), limit.value() - target_byte_index);
} else {
// ix. Repeat, while targetByteIndex < limit,
while (target_byte_index < limit) {

View file

@ -163,7 +163,7 @@ JS_DEFINE_NATIVE_FUNCTION(Uint8ArrayConstructorHelpers::from_hex)
// 7. Set the value at each index of ta.[[ViewedArrayBuffer]].[[ArrayBufferData]] to the value at the corresponding
// index of result.[[Bytes]].
auto& array_buffer_data = typed_array->viewed_array_buffer()->buffer();
auto* array_buffer_data = typed_array->viewed_array_buffer()->data();
for (size_t index = 0; index < result_length; ++index)
array_buffer_data[index] = result.bytes[index];
@ -464,7 +464,7 @@ ThrowCompletionOr<ReadonlyBytes> get_uint8_array_bytes_view(VM& vm, TypedArrayBa
auto byte_offset = typed_array.byte_offset();
return ReadonlyBytes {
typed_array.viewed_array_buffer()->buffer().data() + byte_offset,
typed_array.viewed_array_buffer()->data() + byte_offset,
length
};
}

View file

@ -70,7 +70,7 @@ WebIDL::ExceptionOr<GC::Root<WebIDL::ArrayBufferView>> Crypto::get_random_values
return WebIDL::QuotaExceededError::create(realm(), "array's byteLength may not be greater than 65536"_utf16);
// 3. Overwrite all elements of array with cryptographically strong random values of the appropriate type.
fill_with_random(array->viewed_array_buffer()->buffer().bytes().slice(array->byte_offset(), array->byte_length()));
fill_with_random(array->viewed_array_buffer()->bytes().slice(array->byte_offset(), array->byte_length()));
// 4. Return array.
return array;

View file

@ -92,9 +92,9 @@ static ::Crypto::UnsignedBigInteger big_integer_from_api_big_integer(GC::Ptr<JS:
// (that is, at most 7 leading zero bits, except the value 0 which shall have length 8 bits).
// The API SHALL accept values with any number of leading zero bits, including the empty array, which represents zero.
auto const& buffer = big_integer->viewed_array_buffer()->buffer();
auto buffer = big_integer->viewed_array_buffer()->bytes();
if (buffer.size() > 0)
if (!buffer.is_empty())
return ::Crypto::UnsignedBigInteger::import_data(buffer);
return ::Crypto::UnsignedBigInteger(0);
}

View file

@ -104,7 +104,7 @@ WebIDL::ExceptionOr<void> RsaKeyAlgorithm::set_public_exponent(::Crypto::Unsigne
// The BigInteger typedef from the WebCrypto spec requires the bytes in the Uint8Array be ordered in Big Endian
m_public_exponent = TRY(JS::Uint8Array::create(realm, result.size()));
m_public_exponent->viewed_array_buffer()->buffer().overwrite(0, result.data(), result.size());
m_public_exponent->viewed_array_buffer()->overwrite(0, result.data(), result.size());
return {};
}

View file

@ -859,7 +859,8 @@ GC::Ref<WebIDL::Promise> SubtleCrypto::derive_key(AlgorithmIdentifier algorithm,
}
// 15. Let result be the result of performing the import key operation specified by normalizedDerivedKeyAlgorithmImport using "raw" as format, secret as keyData, derivedKeyType as algorithm and using extractable and usages.
auto result_or_error = normalized_derived_key_algorithm_import.methods->import_key(*normalized_derived_key_algorithm_import.parameter, Bindings::KeyFormat::Raw, secret.release_value()->buffer(), extractable, key_usages);
auto secret_bytes = MUST(ByteBuffer::copy(secret.release_value()->bytes()));
auto result_or_error = normalized_derived_key_algorithm_import.methods->import_key(*normalized_derived_key_algorithm_import.parameter, Bindings::KeyFormat::Raw, move(secret_bytes), extractable, key_usages);
if (result_or_error.is_error()) {
WebIDL::reject_promise(realm, promise, Bindings::exception_to_throw_completion(realm.vm(), result_or_error.release_error()).release_value());
return;
@ -1134,7 +1135,7 @@ GC::Ref<WebIDL::Promise> SubtleCrypto::unwrap_key(Bindings::KeyFormat format, Ke
// 15. If format is equal to the string "jwk":
if (format == Bindings::KeyFormat::Jwk) {
// Let key be the result of executing the parse a JWK algorithm, with bytes as the data to be parsed.
auto maybe_parsed = JsonWebKey::parse(realm, bytes->buffer());
auto maybe_parsed = JsonWebKey::parse(realm, bytes->bytes());
if (maybe_parsed.is_error()) {
WebIDL::reject_promise(realm, promise, maybe_parsed.release_error().release_value());
return;
@ -1466,7 +1467,7 @@ GC::Ref<WebIDL::Promise> SubtleCrypto::decapsulate_key(AlgorithmIdentifier decap
auto maybe_shared_key = normalized_shared_key_algorithm.methods->import_key(
*normalized_shared_key_algorithm.parameter,
Bindings::KeyFormat::RawSecret,
decapsulated_bits->buffer(),
MUST(ByteBuffer::copy(decapsulated_bits->bytes())),
extractable,
usages);
if (maybe_shared_key.is_error()) {

View file

@ -393,10 +393,10 @@ GC::Ref<WebIDL::Promise> Blob::text()
return WebIDL::upon_fulfillment(*promise, GC::create_function(heap(), [&vm](JS::Value first_argument) -> WebIDL::ExceptionOr<JS::Value> {
auto const& object = first_argument.as_object();
VERIFY(is<JS::ArrayBuffer>(object));
auto const& buffer = static_cast<JS::ArrayBuffer const&>(object).buffer();
auto const& array_buffer = static_cast<JS::ArrayBuffer const&>(object);
auto decoder = TextCodec::decoder_for("UTF-8"sv);
auto utf8_text = TRY_OR_THROW_OOM(vm, TextCodec::convert_input_to_utf8_using_given_decoder_unless_there_is_a_byte_order_mark(*decoder, buffer));
auto utf8_text = TRY_OR_THROW_OOM(vm, TextCodec::convert_input_to_utf8_using_given_decoder_unless_there_is_a_byte_order_mark(*decoder, array_buffer.bytes()));
return JS::PrimitiveString::create(vm, move(utf8_text));
}));
}
@ -422,9 +422,9 @@ GC::Ref<WebIDL::Promise> Blob::array_buffer()
return WebIDL::upon_fulfillment(*promise, GC::create_function(heap(), [&realm](JS::Value first_argument) -> WebIDL::ExceptionOr<JS::Value> {
auto const& object = first_argument.as_object();
VERIFY(is<JS::ArrayBuffer>(object));
auto const& buffer = static_cast<JS::ArrayBuffer const&>(object).buffer();
auto const& array_buffer = static_cast<JS::ArrayBuffer const&>(object);
return JS::ArrayBuffer::create(realm, buffer);
return JS::ArrayBuffer::create(realm, MUST(ByteBuffer::copy(array_buffer.bytes())));
}));
}

View file

@ -94,7 +94,7 @@ WebIDL::ExceptionOr<Result> FileReaderSync::read_as(Blob& blob, FileReader::Type
if (promise->state() == JS::Promise::State::Fulfilled && array_buffer) {
// AD-HOC: This diverges from the spec as wrritten, where the type argument is specified explicitly for each caller.
// 1. Return the result of package data given bytes, type, blobs type, and encoding.
auto result = TRY(FileReader::blob_package_data(realm(), array_buffer->buffer(), type, blob.type(), encoding));
auto result = TRY(FileReader::blob_package_data(realm(), MUST(ByteBuffer::copy(array_buffer->bytes())), type, blob.type(), encoding));
return result.get<Result>();
}

View file

@ -167,7 +167,10 @@ static WebIDL::ExceptionOr<void> serialize_array_buffer(JS::VM& vm, TransferData
auto data_copy = TRY(JS::create_byte_data_block(vm, size));
// 4. Perform CopyDataBlockBytes(dataCopy, 0, value.[[ArrayBufferData]], 0, size).
JS::copy_data_block_bytes(data_copy.buffer(), 0, array_buffer.buffer(), 0, size);
if (array_buffer.is_external())
data_copy.overwrite(0, array_buffer.data(), size);
else
JS::copy_data_block_bytes(data_copy.buffer(), 0, array_buffer.buffer(), 0, size);
// 5. If value has an [[ArrayBufferMaxByteLength]] internal slot, then set serialized to { [[Type]]: "ResizableArrayBuffer",
// [[ArrayBufferData]]: dataCopy, [[ArrayBufferByteLength]]: size, [[ArrayBufferMaxByteLength]]: value.[[ArrayBufferMaxByteLength]] }.
@ -1040,13 +1043,17 @@ WebIDL::ExceptionOr<SerializedTransferRecord> structured_serialize_with_transfer
// 4. If transferable has an [[ArrayBufferData]] internal slot, then:
if (array_buffer) {
// 1. If transferable has an [[ArrayBufferMaxByteLength]] internal slot, then:
auto buffer_data = array_buffer->is_external()
? MUST(ByteBuffer::copy(array_buffer->bytes()))
: ByteBuffer(array_buffer->buffer());
if (!array_buffer->is_fixed_length()) {
// 1. Set dataHolder.[[Type]] to "ResizableArrayBuffer".
data_holder.encode(TransferType::ResizableArrayBuffer);
// 2. Set dataHolder.[[ArrayBufferData]] to transferable.[[ArrayBufferData]].
// 3. Set dataHolder.[[ArrayBufferByteLength]] to transferable.[[ArrayBufferByteLength]].
data_holder.encode(array_buffer->buffer());
data_holder.encode(buffer_data);
// 4. Set dataHolder.[[ArrayBufferMaxByteLength]] to transferable.[[ArrayBufferMaxByteLength]].
data_holder.encode(array_buffer->max_byte_length());
@ -1058,7 +1065,7 @@ WebIDL::ExceptionOr<SerializedTransferRecord> structured_serialize_with_transfer
// 2. Set dataHolder.[[ArrayBufferData]] to transferable.[[ArrayBufferData]].
// 3. Set dataHolder.[[ArrayBufferByteLength]] to transferable.[[ArrayBufferByteLength]].
data_holder.encode(array_buffer->buffer());
data_holder.encode(buffer_data);
}
// 3. Perform ? DetachArrayBuffer(transferable).

View file

@ -763,7 +763,7 @@ JS::Value convert_a_key_to_a_value(JS::Realm& realm, GC::Ref<Key> key)
auto array_buffer = MUST(JS::ArrayBuffer::create(realm, len));
// 4. Set the entries in buffers [[ArrayBufferData]] internal slot to the entries in value.
buffer.span().copy_to(array_buffer->buffer());
array_buffer->overwrite(0, buffer.data(), buffer.size());
// 5. Return buffer.
return array_buffer;

View file

@ -89,7 +89,7 @@ void ReadLoopReadRequest::on_chunk(JS::Value chunk)
}
auto const& array = static_cast<JS::Uint8Array const&>(chunk.as_object());
auto const& buffer = array.viewed_array_buffer()->buffer();
auto buffer = array.viewed_array_buffer()->bytes().slice(array.byte_offset(), array.byte_length().length());
// 2. Append the bytes represented by chunk to bytes.
m_bytes.append(buffer);

View file

@ -2077,7 +2077,11 @@ bool readable_byte_stream_controller_fill_pull_into_descriptor_from_queue(Readab
VERIFY(can_copy_data_block_bytes_buffer(descriptor_buffer, dest_start, queue_buffer, queue_byte_offset, bytes_to_copy));
// 8. Perform ! CopyDataBlockBytes(pullIntoDescriptors buffer.[[ArrayBufferData]], destStart, headOfQueues buffer.[[ArrayBufferData]], headOfQueues byte offset, bytesToCopy).
JS::copy_data_block_bytes(pull_into_descriptor.buffer->buffer(), dest_start, head_of_queue.buffer->buffer(), head_of_queue.byte_offset, bytes_to_copy);
// NOTE: Stream buffers are never externally backed, so copy_data_block_bytes is safe here.
if (pull_into_descriptor.buffer->is_external() || head_of_queue.buffer->is_external())
pull_into_descriptor.buffer->overwrite(dest_start, head_of_queue.buffer->data() + head_of_queue.byte_offset, bytes_to_copy);
else
JS::copy_data_block_bytes(pull_into_descriptor.buffer->buffer(), dest_start, head_of_queue.buffer->buffer(), head_of_queue.byte_offset, bytes_to_copy);
// 9. If headOfQueues byte length is bytesToCopy,
if (head_of_queue.byte_length == bytes_to_copy) {

View file

@ -93,11 +93,11 @@ protected:
auto raw_object = src_data->raw_object();
if (auto* array_buffer = as_if<JS::ArrayBuffer>(*raw_object)) {
return TRY(get_offset_span(array_buffer->buffer().span(), src_offset, src_length_override)).reinterpret<T>();
return TRY(get_offset_span(array_buffer->span(), src_offset, src_length_override)).reinterpret<T>();
}
if (auto* data_view = as_if<JS::DataView>(*raw_object)) {
return TRY(get_offset_span(data_view->viewed_array_buffer()->buffer().span(), src_offset, src_length_override)).reinterpret<T>();
return TRY(get_offset_span(data_view->viewed_array_buffer()->span(), src_offset, src_length_override)).reinterpret<T>();
}
// NOTE: This has to be done because src_offset is the number of elements to offset by, not the number of bytes.

View file

@ -122,7 +122,7 @@ void ArrayBufferView::write(ReadonlyBytes bytes, u32 starting_offset)
auto array_buffer = viewed_array_buffer();
// 5. Write bytes into arrayBuffer with startingOffset set to jsView.[[ByteOffset]] + startingOffset.
array_buffer->buffer().overwrite(byte_offset() + starting_offset, bytes.data(), bytes.size());
array_buffer->overwrite(byte_offset() + starting_offset, bytes.data(), bytes.size());
}
BufferSource::~BufferSource() = default;

View file

@ -317,7 +317,7 @@ WebIDL::ExceptionOr<void> WebSocket::send(Variant<GC::Root<WebIDL::BufferSource>
ReadonlyBytes buffer;
if (auto array_buffer = buffer_source->viewed_array_buffer(); array_buffer && !array_buffer->is_detached())
buffer = array_buffer->buffer();
buffer = array_buffer->bytes();
m_websocket->send(buffer, false);
},