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:
Linus Groh 2022-07-06 14:19:20 +02:00
parent 69a385f559
commit 028a6b90b1
Notes: sideshowbarker 2024-07-17 09:39:20 +09:00
12 changed files with 10 additions and 309 deletions

View File

@ -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;
} }

View File

@ -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);

View File

@ -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

View File

@ -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();
}
} }

View File

@ -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);
}; };
} }

View File

@ -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) \

View File

@ -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)") \

View File

@ -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)

View File

@ -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:

View File

@ -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();
});
});

View File

@ -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"
);
});

View File

@ -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();