mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-11 09:18:05 +03:00
LibJS: Revert partial resizable ArrayBuffer implementation
This is a manual but clean revert of all commits from #12595. Adding a partial implementation of the resizable ArrayBuffer proposal without implementing all the updates to TypedArray infrastructure that is also covered by the spec introduced a bunch of crashes, so we decided to revert it for now until a full implementation is completed.
This commit is contained in:
parent
69a385f559
commit
028a6b90b1
Notes:
sideshowbarker
2024-07-17 09:39:20 +09:00
Author: https://github.com/linusg Commit: https://github.com/SerenityOS/serenity/commit/028a6b90b1 Pull-request: https://github.com/SerenityOS/serenity/pull/14500 Reviewed-by: https://github.com/IdanHo ✅
@ -1,6 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020-2022, Linus Groh <linusg@serenityos.org>
|
* Copyright (c) 2020-2022, Linus Groh <linusg@serenityos.org>
|
||||||
* Copyright (c) 2022, the SerenityOS developers.
|
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
@ -45,16 +44,6 @@ ArrayBuffer::ArrayBuffer(ByteBuffer* buffer, Object& prototype)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1.1.5 IsResizableArrayBuffer ( arrayBuffer ), https://tc39.es/proposal-resizablearraybuffer/#sec-isresizablearraybuffer
|
|
||||||
bool ArrayBuffer::is_resizable_array_buffer() const
|
|
||||||
{
|
|
||||||
// 1. Assert: Type(arrayBuffer) is Object and arrayBuffer has an [[ArrayBufferData]] internal slot.
|
|
||||||
|
|
||||||
// 2. If buffer has an [[ArrayBufferMaxByteLength]] internal slot, return true.
|
|
||||||
// 3. Return false.
|
|
||||||
return m_max_byte_length.has_value();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ArrayBuffer::visit_edges(Cell::Visitor& visitor)
|
void ArrayBuffer::visit_edges(Cell::Visitor& visitor)
|
||||||
{
|
{
|
||||||
Base::visit_edges(visitor);
|
Base::visit_edges(visitor);
|
||||||
@ -62,38 +51,22 @@ void ArrayBuffer::visit_edges(Cell::Visitor& visitor)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 25.1.2.1 AllocateArrayBuffer ( constructor, byteLength ), https://tc39.es/ecma262/#sec-allocatearraybuffer
|
// 25.1.2.1 AllocateArrayBuffer ( constructor, byteLength ), https://tc39.es/ecma262/#sec-allocatearraybuffer
|
||||||
// 1.1.2 AllocateArrayBuffer ( constructor, byteLength [, maxByteLength ] ), https://tc39.es/proposal-resizablearraybuffer/#sec-allocatearraybuffer
|
ThrowCompletionOr<ArrayBuffer*> allocate_array_buffer(GlobalObject& global_object, FunctionObject& constructor, size_t byte_length)
|
||||||
ThrowCompletionOr<ArrayBuffer*> allocate_array_buffer(GlobalObject& global_object, FunctionObject& constructor, size_t byte_length, Optional<size_t> max_byte_length)
|
|
||||||
{
|
{
|
||||||
// 1. Let slots be « [[ArrayBufferData]], [[ArrayBufferByteLength]], [[ArrayBufferDetachKey]] ».
|
// 1. Let obj be ? OrdinaryCreateFromConstructor(constructor, "%ArrayBuffer.prototype%", « [[ArrayBufferData]], [[ArrayBufferByteLength]], [[ArrayBufferDetachKey]] »).
|
||||||
// 2. If maxByteLength is present, append [[ArrayBufferMaxByteLength]] to slots.
|
|
||||||
|
|
||||||
// 3. Let obj be ? OrdinaryCreateFromConstructor(constructor, "%ArrayBuffer.prototype%", slots).
|
|
||||||
auto* obj = TRY(ordinary_create_from_constructor<ArrayBuffer>(global_object, constructor, &GlobalObject::array_buffer_prototype, nullptr));
|
auto* obj = TRY(ordinary_create_from_constructor<ArrayBuffer>(global_object, constructor, &GlobalObject::array_buffer_prototype, nullptr));
|
||||||
|
|
||||||
// 4. Let block be ? CreateByteDataBlock(byteLength).
|
// 2. Let block be ? CreateByteDataBlock(byteLength).
|
||||||
auto block = ByteBuffer::create_zeroed(byte_length);
|
auto block = ByteBuffer::create_zeroed(byte_length);
|
||||||
if (block.is_error())
|
if (block.is_error())
|
||||||
return global_object.vm().throw_completion<RangeError>(global_object, ErrorType::NotEnoughMemoryToAllocate, byte_length);
|
return global_object.vm().throw_completion<RangeError>(global_object, ErrorType::NotEnoughMemoryToAllocate, byte_length);
|
||||||
|
|
||||||
// 5. Set obj.[[ArrayBufferData]] to block.
|
// 3. Set obj.[[ArrayBufferData]] to block.
|
||||||
obj->set_buffer(block.release_value());
|
obj->set_buffer(block.release_value());
|
||||||
|
|
||||||
// 6. Set obj.[[ArrayBufferByteLength]] to byteLength.
|
// 4. Set obj.[[ArrayBufferByteLength]] to byteLength.
|
||||||
|
|
||||||
// 7. If maxByteLength is present, then
|
// 5. Return obj.
|
||||||
if (max_byte_length.has_value()) {
|
|
||||||
// a. Assert: byteLength ≤ maxByteLength.
|
|
||||||
VERIFY(byte_length <= *max_byte_length);
|
|
||||||
|
|
||||||
// b. If it is not possible to create a Data Block block consisting of maxByteLength bytes, throw a RangeError exception.
|
|
||||||
// c. NOTE: Resizable ArrayBuffers are designed to be implementable with in-place growth. Implementations reserve the right to throw if, for example, virtual memory cannot be reserved up front.
|
|
||||||
|
|
||||||
// d. Set obj.[[ArrayBufferMaxByteLength]] to maxByteLength.
|
|
||||||
obj->set_max_byte_length(*max_byte_length);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 8. Return obj.
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,13 +34,11 @@ public:
|
|||||||
virtual ~ArrayBuffer() override = default;
|
virtual ~ArrayBuffer() override = default;
|
||||||
|
|
||||||
size_t byte_length() const { return buffer_impl().size(); }
|
size_t byte_length() const { return buffer_impl().size(); }
|
||||||
size_t max_byte_length() const { return m_max_byte_length.value(); } // Will VERIFY() that it has value
|
|
||||||
ByteBuffer& buffer() { return buffer_impl(); }
|
ByteBuffer& buffer() { return buffer_impl(); }
|
||||||
ByteBuffer const& buffer() const { return buffer_impl(); }
|
ByteBuffer const& buffer() const { return buffer_impl(); }
|
||||||
|
|
||||||
// Used by allocate_array_buffer() to attach the data block after construction
|
// Used by allocate_array_buffer() to attach the data block after construction
|
||||||
void set_buffer(ByteBuffer buffer) { m_buffer = move(buffer); }
|
void set_buffer(ByteBuffer buffer) { m_buffer = move(buffer); }
|
||||||
void set_max_byte_length(size_t max_byte_length) { m_max_byte_length = max_byte_length; }
|
|
||||||
|
|
||||||
Value detach_key() const { return m_detach_key; }
|
Value detach_key() const { return m_detach_key; }
|
||||||
void set_detach_key(Value detach_key) { m_detach_key = detach_key; }
|
void set_detach_key(Value detach_key) { m_detach_key = detach_key; }
|
||||||
@ -48,8 +46,6 @@ public:
|
|||||||
void detach_buffer() { m_buffer = Empty {}; }
|
void detach_buffer() { m_buffer = Empty {}; }
|
||||||
bool is_detached() const { return m_buffer.has<Empty>(); }
|
bool is_detached() const { return m_buffer.has<Empty>(); }
|
||||||
|
|
||||||
bool is_resizable_array_buffer() const;
|
|
||||||
|
|
||||||
enum Order {
|
enum Order {
|
||||||
SeqCst,
|
SeqCst,
|
||||||
Unordered
|
Unordered
|
||||||
@ -77,10 +73,9 @@ private:
|
|||||||
// The various detach related members of ArrayBuffer are not used by any ECMA262 functionality,
|
// The various detach related members of ArrayBuffer are not used by any ECMA262 functionality,
|
||||||
// but are required to be available for the use of various harnesses like the Test262 test runner.
|
// but are required to be available for the use of various harnesses like the Test262 test runner.
|
||||||
Value m_detach_key;
|
Value m_detach_key;
|
||||||
Optional<size_t> m_max_byte_length;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ThrowCompletionOr<ArrayBuffer*> allocate_array_buffer(GlobalObject&, FunctionObject& constructor, size_t byte_length, Optional<size_t> max_byte_length = {});
|
ThrowCompletionOr<ArrayBuffer*> allocate_array_buffer(GlobalObject&, FunctionObject& constructor, size_t byte_length);
|
||||||
ThrowCompletionOr<void> detach_array_buffer(GlobalObject&, ArrayBuffer& array_buffer, Optional<Value> key = {});
|
ThrowCompletionOr<void> detach_array_buffer(GlobalObject&, ArrayBuffer& array_buffer, Optional<Value> key = {});
|
||||||
ThrowCompletionOr<ArrayBuffer*> clone_array_buffer(GlobalObject&, ArrayBuffer& source_buffer, size_t source_byte_offset, size_t source_length);
|
ThrowCompletionOr<ArrayBuffer*> clone_array_buffer(GlobalObject&, ArrayBuffer& source_buffer, size_t source_byte_offset, size_t source_length);
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020, Linus Groh <linusg@serenityos.org>
|
* Copyright (c) 2020, Linus Groh <linusg@serenityos.org>
|
||||||
* Copyright (c) 2022, the SerenityOS developers.
|
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
@ -43,63 +42,21 @@ ThrowCompletionOr<Value> ArrayBufferConstructor::call()
|
|||||||
return vm.throw_completion<TypeError>(global_object(), ErrorType::ConstructorWithoutNew, vm.names.ArrayBuffer);
|
return vm.throw_completion<TypeError>(global_object(), ErrorType::ConstructorWithoutNew, vm.names.ArrayBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1.1.6 GetArrayBufferMaxByteLengthOption ( options ), https://tc39.es/proposal-resizablearraybuffer/#sec-getarraybuffermaxbytelengthoption
|
|
||||||
static ThrowCompletionOr<Optional<size_t>> get_array_buffer_max_byte_length_option(VM& vm, GlobalObject& global_object, Value options)
|
|
||||||
{
|
|
||||||
// 1. If Type(options) is not Object, return empty.
|
|
||||||
if (!options.is_object())
|
|
||||||
return Optional<size_t>();
|
|
||||||
|
|
||||||
// 2. Let maxByteLength be ? Get(options, "maxByteLength").
|
|
||||||
auto max_byte_length = TRY(options.get(global_object, vm.names.maxByteLength));
|
|
||||||
|
|
||||||
// 3. If maxByteLength is undefined, return empty.
|
|
||||||
if (max_byte_length.is_undefined())
|
|
||||||
return Optional<size_t>();
|
|
||||||
|
|
||||||
// 4. Return ? ToIndex(maxByteLength).
|
|
||||||
return TRY(max_byte_length.to_index(global_object));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 25.1.3.1 ArrayBuffer ( length ), https://tc39.es/ecma262/#sec-arraybuffer-length
|
// 25.1.3.1 ArrayBuffer ( length ), https://tc39.es/ecma262/#sec-arraybuffer-length
|
||||||
// 1.2.1 ArrayBuffer ( length [, options ] ), https://tc39.es/proposal-resizablearraybuffer/#sec-arraybuffer-constructor
|
|
||||||
ThrowCompletionOr<Object*> ArrayBufferConstructor::construct(FunctionObject& new_target)
|
ThrowCompletionOr<Object*> ArrayBufferConstructor::construct(FunctionObject& new_target)
|
||||||
{
|
{
|
||||||
auto& global_object = this->global_object();
|
|
||||||
auto& vm = this->vm();
|
auto& vm = this->vm();
|
||||||
|
auto byte_length_or_error = vm.argument(0).to_index(global_object());
|
||||||
|
|
||||||
// 1. If NewTarget is undefined, throw a TypeError exception.
|
|
||||||
// NOTE: See `ArrayBufferConstructor::call()`
|
|
||||||
|
|
||||||
// 2. Let byteLength be ? ToIndex(length).
|
|
||||||
auto byte_length_or_error = vm.argument(0).to_index(global_object);
|
|
||||||
if (byte_length_or_error.is_error()) {
|
if (byte_length_or_error.is_error()) {
|
||||||
auto error = byte_length_or_error.release_error();
|
auto error = byte_length_or_error.release_error();
|
||||||
if (error.value()->is_object() && is<RangeError>(error.value()->as_object())) {
|
if (error.value()->is_object() && is<RangeError>(error.value()->as_object())) {
|
||||||
// Re-throw more specific RangeError
|
// Re-throw more specific RangeError
|
||||||
return vm.throw_completion<RangeError>(global_object, ErrorType::InvalidLength, "array buffer");
|
return vm.throw_completion<RangeError>(global_object(), ErrorType::InvalidLength, "array buffer");
|
||||||
}
|
}
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
auto byte_length = byte_length_or_error.release_value();
|
return TRY(allocate_array_buffer(global_object(), new_target, byte_length_or_error.release_value()));
|
||||||
|
|
||||||
// 3. Let requestedMaxByteLength be ? GetArrayBufferMaxByteLengthOption(options).
|
|
||||||
auto options = vm.argument(1);
|
|
||||||
auto requested_max_byte_length_value = TRY(get_array_buffer_max_byte_length_option(vm, global_object, options));
|
|
||||||
|
|
||||||
// 4. If requestedMaxByteLength is empty, then
|
|
||||||
if (!requested_max_byte_length_value.has_value()) {
|
|
||||||
// a. Return ? AllocateArrayBuffer(NewTarget, byteLength).
|
|
||||||
return TRY(allocate_array_buffer(global_object, new_target, byte_length));
|
|
||||||
}
|
|
||||||
auto requested_max_byte_length = requested_max_byte_length_value.release_value();
|
|
||||||
|
|
||||||
// 5. If byteLength > requestedMaxByteLength, throw a RangeError exception.
|
|
||||||
if (byte_length > requested_max_byte_length)
|
|
||||||
return vm.throw_completion<RangeError>(global_object, ErrorType::ByteLengthBeyondRequestedMax);
|
|
||||||
|
|
||||||
// 6. Return ? AllocateArrayBuffer(NewTarget, byteLength, requestedMaxByteLength).
|
|
||||||
return TRY(allocate_array_buffer(global_object, new_target, byte_length, requested_max_byte_length));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 25.1.4.1 ArrayBuffer.isView ( arg ), https://tc39.es/ecma262/#sec-arraybuffer.isview
|
// 25.1.4.1 ArrayBuffer.isView ( arg ), https://tc39.es/ecma262/#sec-arraybuffer.isview
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
* Copyright (c) 2020, Linus Groh <linusg@serenityos.org>
|
* Copyright (c) 2020, Linus Groh <linusg@serenityos.org>
|
||||||
* Copyright (c) 2021-2022, Jamie Mansfield <jmansfield@cadixdev.org>
|
* Copyright (c) 2021-2022, Jamie Mansfield <jmansfield@cadixdev.org>
|
||||||
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||||
* Copyright (c) 2022, the SerenityOS developers.
|
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
@ -26,10 +25,7 @@ void ArrayBufferPrototype::initialize(GlobalObject& global_object)
|
|||||||
Object::initialize(global_object);
|
Object::initialize(global_object);
|
||||||
u8 attr = Attribute::Writable | Attribute::Configurable;
|
u8 attr = Attribute::Writable | Attribute::Configurable;
|
||||||
define_native_function(vm.names.slice, slice, 2, attr);
|
define_native_function(vm.names.slice, slice, 2, attr);
|
||||||
define_native_function(vm.names.resize, resize, 1, attr);
|
|
||||||
define_native_accessor(vm.names.byteLength, byte_length_getter, {}, Attribute::Configurable);
|
define_native_accessor(vm.names.byteLength, byte_length_getter, {}, Attribute::Configurable);
|
||||||
define_native_accessor(vm.names.maxByteLength, max_byte_length_getter, {}, Attribute::Configurable);
|
|
||||||
define_native_accessor(vm.names.resizable, resizable_getter, {}, Attribute::Configurable);
|
|
||||||
|
|
||||||
// 25.1.5.4 ArrayBuffer.prototype [ @@toStringTag ], https://tc39.es/ecma262/#sec-arraybuffer.prototype-@@tostringtag
|
// 25.1.5.4 ArrayBuffer.prototype [ @@toStringTag ], https://tc39.es/ecma262/#sec-arraybuffer.prototype-@@tostringtag
|
||||||
define_direct_property(*vm.well_known_symbol_to_string_tag(), js_string(vm, vm.names.ArrayBuffer.as_string()), Attribute::Configurable);
|
define_direct_property(*vm.well_known_symbol_to_string_tag(), js_string(vm, vm.names.ArrayBuffer.as_string()), Attribute::Configurable);
|
||||||
@ -124,60 +120,6 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::slice)
|
|||||||
return new_array_buffer_object;
|
return new_array_buffer_object;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1.3.5 ArrayBuffer.prototype.resize ( newLength ), https://tc39.es/proposal-resizablearraybuffer/#sec-arraybuffer.prototype.resize
|
|
||||||
JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::resize)
|
|
||||||
{
|
|
||||||
// 1. Let O be the this value.
|
|
||||||
auto* array_buffer_object = TRY(typed_this_value(global_object));
|
|
||||||
|
|
||||||
// 2. Perform ? RequireInternalSlot(O, [[ArrayBufferMaxByteLength]]).
|
|
||||||
if (!array_buffer_object->is_resizable_array_buffer())
|
|
||||||
return vm.throw_completion<TypeError>(global_object, ErrorType::NotAResizableArrayBuffer);
|
|
||||||
|
|
||||||
// 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.
|
|
||||||
// FIXME: Check for shared buffer
|
|
||||||
|
|
||||||
// 4. If IsDetachedBuffer(O) is true, throw a TypeError exception.
|
|
||||||
if (array_buffer_object->is_detached())
|
|
||||||
return vm.throw_completion<TypeError>(global_object, ErrorType::DetachedArrayBuffer);
|
|
||||||
|
|
||||||
// 5. Let newByteLength be ? ToIntegerOrInfinity(newLength).
|
|
||||||
auto new_byte_length = TRY(vm.argument(0).to_integer_or_infinity(global_object));
|
|
||||||
|
|
||||||
// 6. If newByteLength < 0 or newByteLength > O.[[ArrayBufferMaxByteLength]], throw a RangeError exception.
|
|
||||||
if (new_byte_length < 0 || new_byte_length > array_buffer_object->max_byte_length())
|
|
||||||
return vm.throw_completion<RangeError>(global_object, ErrorType::NewByteLengthOutOfRange);
|
|
||||||
|
|
||||||
// 7. Let hostHandled be ? HostResizeArrayBuffer(O, newByteLength).
|
|
||||||
auto host_handled = TRY(vm.host_resize_array_buffer(global_object, (size_t)new_byte_length));
|
|
||||||
|
|
||||||
// 8. If hostHandled is handled, return undefined.
|
|
||||||
if (host_handled == VM::HostResizeArrayBufferResult::Handled)
|
|
||||||
return js_undefined();
|
|
||||||
|
|
||||||
// 9. Let oldBlock be O.[[ArrayBufferData]].
|
|
||||||
// 10. Let newBlock be ? CreateByteDataBlock(newByteLength).
|
|
||||||
// 11. Let copyLength be min(newByteLength, O.[[ArrayBufferByteLength]]).
|
|
||||||
// 12. Perform CopyDataBlockBytes(newBlock, 0, oldBlock, 0, copyLength).
|
|
||||||
// 13. NOTE: Neither creation of the new Data Block nor copying from the old Data Block are observable. Implementations reserve the right to implement this method as in-place growth or shrinkage.
|
|
||||||
// 14. Set O.[[ArrayBufferData]] to newBlock.
|
|
||||||
auto old_byte_length = array_buffer_object->byte_length();
|
|
||||||
if (array_buffer_object->buffer().try_resize((size_t)new_byte_length).is_error())
|
|
||||||
return global_object.vm().throw_completion<RangeError>(global_object, ErrorType::NotEnoughMemoryToAllocate, new_byte_length);
|
|
||||||
|
|
||||||
// Resizing an `AK::ByteBuffer` does not zero initialize any new capacity, but we want it to be anyway
|
|
||||||
if (new_byte_length > old_byte_length) {
|
|
||||||
// This performs bounds checking whereas a raw `memset` call would not be
|
|
||||||
array_buffer_object->buffer().bytes().slice(old_byte_length).fill(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 15. Set O.[[ArrayBufferByteLength]] to newLength.
|
|
||||||
// This value is managed by the `AK::ByteBuffer` internally, it already has its new value
|
|
||||||
|
|
||||||
// 16. Return undefined.
|
|
||||||
return js_undefined();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 25.1.5.1 get ArrayBuffer.prototype.byteLength, https://tc39.es/ecma262/#sec-get-arraybuffer.prototype.bytelength
|
// 25.1.5.1 get ArrayBuffer.prototype.byteLength, https://tc39.es/ecma262/#sec-get-arraybuffer.prototype.bytelength
|
||||||
JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::byte_length_getter)
|
JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::byte_length_getter)
|
||||||
{
|
{
|
||||||
@ -199,44 +141,4 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::byte_length_getter)
|
|||||||
return Value(length);
|
return Value(length);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1.3.2 get ArrayBuffer.prototype.maxByteLength, https://tc39.es/proposal-resizablearraybuffer/#sec-get-arraybuffer.prototype.maxbytelength
|
|
||||||
JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::max_byte_length_getter)
|
|
||||||
{
|
|
||||||
// 1. Let O be the this value.
|
|
||||||
// 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
|
|
||||||
auto* array_buffer_object = TRY(typed_this_value(global_object));
|
|
||||||
|
|
||||||
// 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.
|
|
||||||
// FIXME: Check for shared buffer
|
|
||||||
|
|
||||||
// 4. If IsDetachedBuffer(O) is true, return +0𝔽.
|
|
||||||
if (array_buffer_object->is_detached())
|
|
||||||
return Value(0);
|
|
||||||
|
|
||||||
// 5. If IsResizableArrayBuffer(O) is true, then
|
|
||||||
if (array_buffer_object->is_resizable_array_buffer()) {
|
|
||||||
// a. Let length be O.[[ArrayBufferMaxByteLength]].
|
|
||||||
return array_buffer_object->max_byte_length();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Else
|
|
||||||
// a. Let length be O.[[ArrayBufferByteLength]].
|
|
||||||
// 7. Return 𝔽(length).
|
|
||||||
return array_buffer_object->byte_length();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1.3.3 get ArrayBuffer.prototype.resizable, https://tc39.es/proposal-resizablearraybuffer/#sec-get-arraybuffer.prototype.resizable
|
|
||||||
JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::resizable_getter)
|
|
||||||
{
|
|
||||||
// 1. Let O be the this value.
|
|
||||||
// 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
|
|
||||||
auto* array_buffer_object = TRY(typed_this_value(global_object));
|
|
||||||
|
|
||||||
// 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.
|
|
||||||
// FIXME: Check for shared buffer
|
|
||||||
|
|
||||||
// 4. Return IsResizableArrayBuffer(O).
|
|
||||||
return array_buffer_object->is_resizable_array_buffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,10 +21,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
JS_DECLARE_NATIVE_FUNCTION(slice);
|
JS_DECLARE_NATIVE_FUNCTION(slice);
|
||||||
JS_DECLARE_NATIVE_FUNCTION(resize);
|
|
||||||
JS_DECLARE_NATIVE_FUNCTION(byte_length_getter);
|
JS_DECLARE_NATIVE_FUNCTION(byte_length_getter);
|
||||||
JS_DECLARE_NATIVE_FUNCTION(max_byte_length_getter);
|
|
||||||
JS_DECLARE_NATIVE_FUNCTION(resizable_getter);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -316,7 +316,6 @@ namespace JS {
|
|||||||
P(log10) \
|
P(log10) \
|
||||||
P(map) \
|
P(map) \
|
||||||
P(max) \
|
P(max) \
|
||||||
P(maxByteLength) \
|
|
||||||
P(maximize) \
|
P(maximize) \
|
||||||
P(mergeFields) \
|
P(mergeFields) \
|
||||||
P(message) \
|
P(message) \
|
||||||
@ -388,8 +387,6 @@ namespace JS {
|
|||||||
P(reject) \
|
P(reject) \
|
||||||
P(relativeTo) \
|
P(relativeTo) \
|
||||||
P(repeat) \
|
P(repeat) \
|
||||||
P(resize) \
|
|
||||||
P(resizable) \
|
|
||||||
P(resolve) \
|
P(resolve) \
|
||||||
P(resolvedOptions) \
|
P(resolvedOptions) \
|
||||||
P(reverse) \
|
P(reverse) \
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
M(BigIntFromNonIntegral, "Cannot convert non-integral number to BigInt") \
|
M(BigIntFromNonIntegral, "Cannot convert non-integral number to BigInt") \
|
||||||
M(BigIntInvalidValue, "Invalid value for BigInt: {}") \
|
M(BigIntInvalidValue, "Invalid value for BigInt: {}") \
|
||||||
M(BindingNotInitialized, "Binding {} is not initialized") \
|
M(BindingNotInitialized, "Binding {} is not initialized") \
|
||||||
M(ByteLengthBeyondRequestedMax, "Byte length exceeds maxByteLength option") \
|
|
||||||
M(CallStackSizeExceeded, "Call stack size limit exceeded") \
|
M(CallStackSizeExceeded, "Call stack size limit exceeded") \
|
||||||
M(CannotDeclareGlobalFunction, "Cannot declare global function of name '{}'") \
|
M(CannotDeclareGlobalFunction, "Cannot declare global function of name '{}'") \
|
||||||
M(CannotDeclareGlobalVariable, "Cannot declare global variable of name '{}'") \
|
M(CannotDeclareGlobalVariable, "Cannot declare global variable of name '{}'") \
|
||||||
@ -79,7 +78,6 @@
|
|||||||
M(ModuleNotFound, "Cannot find/open module: '{}'") \
|
M(ModuleNotFound, "Cannot find/open module: '{}'") \
|
||||||
M(ModuleNotFoundNoReferencingScript, "Cannot resolve module {} without any active script or module") \
|
M(ModuleNotFoundNoReferencingScript, "Cannot resolve module {} without any active script or module") \
|
||||||
M(NegativeExponent, "Exponent must be positive") \
|
M(NegativeExponent, "Exponent must be positive") \
|
||||||
M(NewByteLengthOutOfRange, "New byte length outside range supported by ArrayBuffer instance") \
|
|
||||||
M(NonExtensibleDefine, "Cannot define property {} on non-extensible object") \
|
M(NonExtensibleDefine, "Cannot define property {} on non-extensible object") \
|
||||||
M(NotAConstructor, "{} is not a constructor") \
|
M(NotAConstructor, "{} is not a constructor") \
|
||||||
M(NotAFunction, "{} is not a function") \
|
M(NotAFunction, "{} is not a function") \
|
||||||
@ -88,7 +86,6 @@
|
|||||||
M(NotAnObjectOfType, "Not an object of type {}") \
|
M(NotAnObjectOfType, "Not an object of type {}") \
|
||||||
M(NotAnObjectOrNull, "{} is neither an object nor null") \
|
M(NotAnObjectOrNull, "{} is neither an object nor null") \
|
||||||
M(NotAnObjectOrString, "{} is neither an object nor a string") \
|
M(NotAnObjectOrString, "{} is neither an object nor a string") \
|
||||||
M(NotAResizableArrayBuffer, "ArrayBuffer instance not resizable") \
|
|
||||||
M(NotAString, "{} is not a string") \
|
M(NotAString, "{} is not a string") \
|
||||||
M(NotASymbol, "{} is not a symbol") \
|
M(NotASymbol, "{} is not a symbol") \
|
||||||
M(NotImplemented, "TODO({} is not implemented in LibJS)") \
|
M(NotImplemented, "TODO({} is not implemented in LibJS)") \
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
#include <LibJS/Interpreter.h>
|
#include <LibJS/Interpreter.h>
|
||||||
#include <LibJS/Runtime/AbstractOperations.h>
|
#include <LibJS/Runtime/AbstractOperations.h>
|
||||||
#include <LibJS/Runtime/Array.h>
|
#include <LibJS/Runtime/Array.h>
|
||||||
#include <LibJS/Runtime/ArrayBuffer.h>
|
|
||||||
#include <LibJS/Runtime/BoundFunction.h>
|
#include <LibJS/Runtime/BoundFunction.h>
|
||||||
#include <LibJS/Runtime/Completion.h>
|
#include <LibJS/Runtime/Completion.h>
|
||||||
#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
|
#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
|
||||||
@ -114,23 +113,6 @@ VM::VM(OwnPtr<CustomData> custom_data)
|
|||||||
return Vector<String> { "type" };
|
return Vector<String> { "type" };
|
||||||
};
|
};
|
||||||
|
|
||||||
// 1.1.7 HostResizeArrayBuffer ( buffer, newByteLength ), https://tc39.es/proposal-resizablearraybuffer/#sec-hostresizearraybuffer
|
|
||||||
host_resize_array_buffer = [](GlobalObject& global_object, size_t new_byte_length) {
|
|
||||||
// The host-defined abstract operation HostResizeArrayBuffer takes arguments buffer (an ArrayBuffer) and newByteLength (a non-negative integer).
|
|
||||||
// The host-defined abstract operation HostResizeArrayBuffer takes arguments buffer (an ArrayBuffer object) and newByteLength.
|
|
||||||
// It gives the host an opportunity to perform implementation-defined resizing of buffer. If the host chooses not to handle resizing of buffer, it may return unhandled for the default behavior.
|
|
||||||
// The implementation of HostResizeArrayBuffer must conform to the following requirements:
|
|
||||||
// * The abstract operation must return either NormalCompletion(handled), NormalCompletion(unhandled), or an abrupt throw completion.
|
|
||||||
// * The abstract operation does not detach buffer.
|
|
||||||
// * If the abstract operation completes normally with handled, buffer.[[ArrayBufferByteLength]] is newByteLength.
|
|
||||||
// The default implementation of HostResizeArrayBuffer is to return unhandled.
|
|
||||||
|
|
||||||
(void)global_object;
|
|
||||||
(void)new_byte_length;
|
|
||||||
|
|
||||||
return HostResizeArrayBufferResult::Unhandled;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 19.2.1.2 HostEnsureCanCompileStrings ( callerRealm, calleeRealm ), https://tc39.es/ecma262/#sec-hostensurecancompilestrings
|
// 19.2.1.2 HostEnsureCanCompileStrings ( callerRealm, calleeRealm ), https://tc39.es/ecma262/#sec-hostensurecancompilestrings
|
||||||
host_ensure_can_compile_strings = [](Realm&) -> ThrowCompletionOr<void> {
|
host_ensure_can_compile_strings = [](Realm&) -> ThrowCompletionOr<void> {
|
||||||
// The host-defined abstract operation HostEnsureCanCompileStrings takes argument calleeRealm (a Realm Record)
|
// The host-defined abstract operation HostEnsureCanCompileStrings takes argument calleeRealm (a Realm Record)
|
||||||
|
@ -39,11 +39,6 @@ public:
|
|||||||
static NonnullRefPtr<VM> create(OwnPtr<CustomData> = {});
|
static NonnullRefPtr<VM> create(OwnPtr<CustomData> = {});
|
||||||
~VM() = default;
|
~VM() = default;
|
||||||
|
|
||||||
enum class HostResizeArrayBufferResult {
|
|
||||||
Unhandled,
|
|
||||||
Handled,
|
|
||||||
};
|
|
||||||
|
|
||||||
Heap& heap() { return m_heap; }
|
Heap& heap() { return m_heap; }
|
||||||
Heap const& heap() const { return m_heap; }
|
Heap const& heap() const { return m_heap; }
|
||||||
|
|
||||||
@ -233,7 +228,6 @@ public:
|
|||||||
Function<void(FinalizationRegistry&)> host_enqueue_finalization_registry_cleanup_job;
|
Function<void(FinalizationRegistry&)> host_enqueue_finalization_registry_cleanup_job;
|
||||||
Function<void(Function<ThrowCompletionOr<Value>()>, Realm*)> host_enqueue_promise_job;
|
Function<void(Function<ThrowCompletionOr<Value>()>, Realm*)> host_enqueue_promise_job;
|
||||||
Function<JobCallback(FunctionObject&)> host_make_job_callback;
|
Function<JobCallback(FunctionObject&)> host_make_job_callback;
|
||||||
Function<ThrowCompletionOr<HostResizeArrayBufferResult>(GlobalObject&, size_t)> host_resize_array_buffer;
|
|
||||||
Function<ThrowCompletionOr<void>(Realm&)> host_ensure_can_compile_strings;
|
Function<ThrowCompletionOr<void>(Realm&)> host_ensure_can_compile_strings;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -11,57 +11,3 @@ test("ArrayBuffer constructor must be invoked with 'new'", () => {
|
|||||||
ArrayBuffer();
|
ArrayBuffer();
|
||||||
}).toThrowWithMessage(TypeError, "ArrayBuffer constructor must be called with 'new'");
|
}).toThrowWithMessage(TypeError, "ArrayBuffer constructor must be called with 'new'");
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("resizable array buffer", () => {
|
|
||||||
test("construct with options", () => {
|
|
||||||
expect(new ArrayBuffer(5, { maxByteLength: 5 })).toBeInstanceOf(ArrayBuffer);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("resizable when provided max byte length", () => {
|
|
||||||
expect(new ArrayBuffer(1).resizable).toEqual(false);
|
|
||||||
expect(new ArrayBuffer(1, {}).resizable).toEqual(false);
|
|
||||||
expect(new ArrayBuffer(1, { maxByteLength: undefined }).resizable).toEqual(false);
|
|
||||||
expect(new ArrayBuffer(1, { maxByteLength: 1 }).resizable).toEqual(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("byte length must be shorter than max byte length", () => {
|
|
||||||
expect(() => {
|
|
||||||
new ArrayBuffer(1, { maxByteLength: 0 });
|
|
||||||
}).toThrowWithMessage(RangeError, "Byte length exceeds maxByteLength option");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("max byte length cannot be too large", () => {
|
|
||||||
expect(() => {
|
|
||||||
new ArrayBuffer(0, { maxByteLength: 9007199254740992 });
|
|
||||||
}).toThrowWithMessage(RangeError, "Index must be a positive integer");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("max byte length cannot be negative", () => {
|
|
||||||
expect(() => {
|
|
||||||
new ArrayBuffer(0, { maxByteLength: -1 });
|
|
||||||
}).toThrowWithMessage(RangeError, "Index must be a positive integer");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("invalid max byte length object", () => {
|
|
||||||
expect(() => {
|
|
||||||
new ArrayBuffer(0, {
|
|
||||||
maxByteLength: {
|
|
||||||
toString: function () {
|
|
||||||
return {};
|
|
||||||
},
|
|
||||||
valueOf: function () {
|
|
||||||
return {};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}).toThrowWithMessage(TypeError, "Cannot convert object to number");
|
|
||||||
|
|
||||||
expect(() => {
|
|
||||||
new ArrayBuffer(0, {
|
|
||||||
get maxByteLength() {
|
|
||||||
throw "Exception";
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}).toThrow();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
test("length is 1", () => {
|
|
||||||
expect(ArrayBuffer.prototype.resize).toHaveLength(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("resize up to max", () => {
|
|
||||||
let a = new ArrayBuffer(0, { maxByteLength: 10 });
|
|
||||||
a.resize(10);
|
|
||||||
expect(a.byteLength).toEqual(10);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("resize less than max", () => {
|
|
||||||
let a = new ArrayBuffer(10, { maxByteLength: 10 });
|
|
||||||
a.resize(5);
|
|
||||||
expect(a.byteLength).toEqual(5);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("resize with negative length", () => {
|
|
||||||
let a = new ArrayBuffer(10, { maxByteLength: 10 });
|
|
||||||
expect(() => {
|
|
||||||
a.resize(-1);
|
|
||||||
}).toThrowWithMessage(
|
|
||||||
RangeError,
|
|
||||||
"New byte length outside range supported by ArrayBuffer instance"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("resize past max length", () => {
|
|
||||||
let a = new ArrayBuffer(10, { maxByteLength: 10 });
|
|
||||||
expect(() => {
|
|
||||||
a.resize(11);
|
|
||||||
}).toThrowWithMessage(
|
|
||||||
RangeError,
|
|
||||||
"New byte length outside range supported by ArrayBuffer instance"
|
|
||||||
);
|
|
||||||
});
|
|
@ -469,10 +469,6 @@ static void print_array_buffer(JS::ArrayBuffer const& array_buffer, HashTable<JS
|
|||||||
print_type("ArrayBuffer");
|
print_type("ArrayBuffer");
|
||||||
js_out("\n byteLength: ");
|
js_out("\n byteLength: ");
|
||||||
print_value(JS::Value((double)byte_length), seen_objects);
|
print_value(JS::Value((double)byte_length), seen_objects);
|
||||||
if (array_buffer.is_resizable_array_buffer()) {
|
|
||||||
js_out("\n maxByteLength: ");
|
|
||||||
print_value(JS::Value((double)array_buffer.max_byte_length()), seen_objects);
|
|
||||||
}
|
|
||||||
if (!byte_length)
|
if (!byte_length)
|
||||||
return;
|
return;
|
||||||
js_outln();
|
js_outln();
|
||||||
|
Loading…
Reference in New Issue
Block a user