From 850c252b3eaf923c71949a3ac5af096565da9cec Mon Sep 17 00:00:00 2001 From: PrestonLTaylor <95388976+PrestonLTaylor@users.noreply.github.com> Date: Fri, 30 Jun 2023 18:00:56 +0100 Subject: [PATCH] LibJS: Implement ArrayBuffer.prototype.transfer & transferToFixedLength --- .../Libraries/LibJS/Runtime/ArrayBuffer.cpp | 47 +++++++++++++++++++ .../Libraries/LibJS/Runtime/ArrayBuffer.h | 6 +++ .../LibJS/Runtime/ArrayBufferPrototype.cpp | 24 ++++++++++ .../LibJS/Runtime/ArrayBufferPrototype.h | 2 + .../LibJS/Runtime/CommonPropertyNames.h | 2 + 5 files changed, 81 insertions(+) diff --git a/Userland/Libraries/LibJS/Runtime/ArrayBuffer.cpp b/Userland/Libraries/LibJS/Runtime/ArrayBuffer.cpp index f18fda4dff..4b7176389e 100644 --- a/Userland/Libraries/LibJS/Runtime/ArrayBuffer.cpp +++ b/Userland/Libraries/LibJS/Runtime/ArrayBuffer.cpp @@ -182,4 +182,51 @@ ThrowCompletionOr 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 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(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(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; +} + } diff --git a/Userland/Libraries/LibJS/Runtime/ArrayBuffer.h b/Userland/Libraries/LibJS/Runtime/ArrayBuffer.h index 9516203eeb..b03735fd2a 100644 --- a/Userland/Libraries/LibJS/Runtime/ArrayBuffer.h +++ b/Userland/Libraries/LibJS/Runtime/ArrayBuffer.h @@ -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; +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 allocate_array_buffer(VM&, FunctionObject& constructor, size_t byte_length); ThrowCompletionOr detach_array_buffer(VM&, ArrayBuffer& array_buffer, Optional key = {}); ThrowCompletionOr clone_array_buffer(VM&, ArrayBuffer& source_buffer, size_t source_byte_offset, size_t source_length); +ThrowCompletionOr 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 diff --git a/Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.cpp b/Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.cpp index 3b7f40e270..7ca18b68b4 100644 --- a/Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.cpp @@ -25,6 +25,8 @@ ThrowCompletionOr 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)); +} + } diff --git a/Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.h b/Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.h index eaaf19571f..3486f70755 100644 --- a/Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.h +++ b/Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.h @@ -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); }; diff --git a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h index ba1bdda8c3..a88eaf332f 100644 --- a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h +++ b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h @@ -547,6 +547,8 @@ namespace JS { P(toZonedDateTimeISO) \ P(trace) \ P(trailingZeroDisplay) \ + P(transfer) \ + P(transferToFixedLength) \ P(trim) \ P(trimEnd) \ P(trimLeft) \