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:
parent
0a7bef349d
commit
0bb987e809
22 changed files with 163 additions and 68 deletions
|
|
@ -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()) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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"));
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
|
|
|
|||
|
|
@ -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())));
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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, blob’s 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>();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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 buffer’s [[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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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(pullIntoDescriptor’s buffer.[[ArrayBufferData]], destStart, headOfQueue’s buffer.[[ArrayBufferData]], headOfQueue’s 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 headOfQueue’s byte length is bytesToCopy,
|
||||
if (head_of_queue.byte_length == bytes_to_copy) {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue