LibJS: Implement ArrayBuffer.prototype.transfer & transferToFixedLength

This commit is contained in:
PrestonLTaylor 2023-06-30 18:00:56 +01:00 committed by Jelle Raaijmakers
parent 34c5d34b95
commit 850c252b3e
5 changed files with 81 additions and 0 deletions

View file

@ -182,4 +182,51 @@ ThrowCompletionOr<ArrayBuffer*> clone_array_buffer(VM& vm, ArrayBuffer& source_b
return target_buffer;
}
// 25.1.2.14 ArrayBufferCopyAndDetach ( arrayBuffer, newLength, preserveResizability ), https://tc39.es/proposal-arraybuffer-transfer/#sec-arraybuffer.prototype.transfertofixedlength
ThrowCompletionOr<ArrayBuffer*> array_buffer_copy_and_detach(VM& vm, ArrayBuffer& array_buffer, Value new_length, PreserveResizability)
{
auto& realm = *vm.current_realm();
// 1. Perform ? RequireInternalSlot(arrayBuffer, [[ArrayBufferData]]).
// FIXME: 2. If IsSharedArrayBuffer(arrayBuffer) is true, throw a TypeError exception.
// 3. If newLength is undefined, then
// a. Let newByteLength be arrayBuffer.[[ArrayBufferByteLength]].
// 4. Else,
// a. Let newByteLength be ? ToIndex(newLength).
auto new_byte_length = new_length.is_undefined() ? array_buffer.byte_length() : TRY(new_length.to_index(vm));
// 5. If IsDetachedBuffer(arrayBuffer) is true, throw a TypeError exception.
if (array_buffer.is_detached())
return vm.throw_completion<TypeError>(ErrorType::DetachedArrayBuffer);
// FIXME: 6. If preserveResizability is preserve-resizability and IsResizableArrayBuffer(arrayBuffer) is true, then
// a. Let newMaxByteLength be arrayBuffer.[[ArrayBufferMaxByteLength]].
// 7. Else,
// a. Let newMaxByteLength be empty.
// 8. If arrayBuffer.[[ArrayBufferDetachKey]] is not undefined, throw a TypeError exception.
if (!array_buffer.detach_key().is_undefined())
return vm.throw_completion<TypeError>(ErrorType::DetachKeyMismatch, array_buffer.detach_key(), js_undefined());
// 9. Let newBuffer be ? AllocateArrayBuffer(%ArrayBuffer%, newByteLength, FIXME: newMaxByteLength).
auto* new_buffer = TRY(allocate_array_buffer(vm, realm.intrinsics().array_buffer_constructor(), new_byte_length));
// 10. Let copyLength be min(newByteLength, arrayBuffer.[[ArrayBufferByteLength]]).
auto copy_length = min(new_byte_length, array_buffer.byte_length());
// 11. Let fromBlock be arrayBuffer.[[ArrayBufferData]].
// 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);
// 15. Perform ! DetachArrayBuffer(arrayBuffer).
TRY(detach_array_buffer(vm, array_buffer));
// 16. Return newBuffer.
return new_buffer;
}
}

View file

@ -22,6 +22,11 @@ struct ClampedU8 {
// 25.1.1 Notation (read-modify-write modification function), https://tc39.es/ecma262/#sec-arraybuffer-notation
using ReadWriteModifyFunction = Function<ByteBuffer(ByteBuffer, ByteBuffer)>;
enum class PreserveResizability {
FixedLength,
PreserveResizability
};
class ArrayBuffer : public Object {
JS_OBJECT(ArrayBuffer, Object);
@ -98,6 +103,7 @@ void copy_data_block_bytes(ByteBuffer& to_block, u64 to_index, ByteBuffer& from_
ThrowCompletionOr<ArrayBuffer*> allocate_array_buffer(VM&, FunctionObject& constructor, size_t byte_length);
ThrowCompletionOr<void> detach_array_buffer(VM&, ArrayBuffer& array_buffer, Optional<Value> key = {});
ThrowCompletionOr<ArrayBuffer*> clone_array_buffer(VM&, ArrayBuffer& source_buffer, size_t source_byte_offset, size_t source_length);
ThrowCompletionOr<ArrayBuffer*> array_buffer_copy_and_detach(VM&, ArrayBuffer& array_buffer, Value new_length, PreserveResizability preserve_resizability);
// 25.1.2.9 RawBytesToNumeric ( type, rawBytes, isLittleEndian ), https://tc39.es/ecma262/#sec-rawbytestonumeric
template<typename T>

View file

@ -25,6 +25,8 @@ ThrowCompletionOr<void> ArrayBufferPrototype::initialize(Realm& realm)
MUST_OR_THROW_OOM(Base::initialize(realm));
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(realm, vm.names.slice, slice, 2, attr);
define_native_function(realm, vm.names.transfer, transfer, 0, attr);
define_native_function(realm, vm.names.transferToFixedLength, transfer_to_fixed_length, 0, attr);
define_native_accessor(realm, vm.names.byteLength, byte_length_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.detached, detached_getter, {}, Attribute::Configurable);
@ -159,4 +161,26 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::detached_getter)
return Value(array_buffer_object->is_detached());
}
// 25.1.5.5 ArrayBuffer.prototype.transfer ( [ newLength ] ), https://tc39.es/proposal-arraybuffer-transfer/#sec-arraybuffer.prototype.transfer
JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::transfer)
{
// 1. Let O be the this value.
auto array_buffer_object = TRY(typed_this_value(vm));
// 2. Return ? ArrayBufferCopyAndDetach(O, newLength, preserve-resizability).
auto new_length = vm.argument(0);
return TRY(array_buffer_copy_and_detach(vm, array_buffer_object, new_length, PreserveResizability::PreserveResizability));
}
// 25.1.5.6 ArrayBuffer.prototype.transferToFixedLength ( [ newLength ] ), https://tc39.es/proposal-arraybuffer-transfer/#sec-arraybuffer.prototype.transfertofixedlength
JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::transfer_to_fixed_length)
{
// 1. Let O be the this value.
auto array_buffer_object = TRY(typed_this_value(vm));
// 2. Return ? ArrayBufferCopyAndDetach(O, newLength, fixed-length).
auto new_length = vm.argument(0);
return TRY(array_buffer_copy_and_detach(vm, array_buffer_object, new_length, PreserveResizability::FixedLength));
}
}

View file

@ -22,6 +22,8 @@ private:
explicit ArrayBufferPrototype(Realm&);
JS_DECLARE_NATIVE_FUNCTION(slice);
JS_DECLARE_NATIVE_FUNCTION(transfer);
JS_DECLARE_NATIVE_FUNCTION(transfer_to_fixed_length);
JS_DECLARE_NATIVE_FUNCTION(byte_length_getter);
JS_DECLARE_NATIVE_FUNCTION(detached_getter);
};

View file

@ -547,6 +547,8 @@ namespace JS {
P(toZonedDateTimeISO) \
P(trace) \
P(trailingZeroDisplay) \
P(transfer) \
P(transferToFixedLength) \
P(trim) \
P(trimEnd) \
P(trimLeft) \