mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-06-09 17:44:56 +09:00
LibJS: Prevent extensions of TypedArray exotic objects
This is a normative change in the ECMA-262 spec. See:
c1040ff
This commit is contained in:
parent
c6e684791f
commit
53a507303c
Notes:
github-actions[bot]
2024-11-30 10:21:05 +00:00
Author: https://github.com/trflynn89
Commit: 53a507303c
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2646
Reviewed-by: https://github.com/shannonbooth ✅
3 changed files with 75 additions and 16 deletions
|
@ -594,7 +594,7 @@ void TypedArrayBase::visit_edges(Visitor& visitor)
|
|||
JS_ENUMERATE_TYPED_ARRAYS
|
||||
#undef __JS_ENUMERATE
|
||||
|
||||
// 10.4.5.9 MakeTypedArrayWithBufferWitnessRecord ( obj, order ), https://tc39.es/ecma262/#sec-maketypedarraywithbufferwitnessrecord
|
||||
// 10.4.5.10 MakeTypedArrayWithBufferWitnessRecord ( obj, order ), https://tc39.es/ecma262/#sec-maketypedarraywithbufferwitnessrecord
|
||||
TypedArrayWithBufferWitness make_typed_array_with_buffer_witness_record(TypedArrayBase const& typed_array, ArrayBuffer::Order order)
|
||||
{
|
||||
// 1. Let buffer be obj.[[ViewedArrayBuffer]].
|
||||
|
@ -617,7 +617,7 @@ TypedArrayWithBufferWitness make_typed_array_with_buffer_witness_record(TypedArr
|
|||
return { .object = typed_array, .cached_buffer_byte_length = move(byte_length) };
|
||||
}
|
||||
|
||||
// 10.4.5.11 TypedArrayByteLength ( taRecord ), https://tc39.es/ecma262/#sec-typedarraybytelength
|
||||
// 10.4.5.12 TypedArrayByteLength ( taRecord ), https://tc39.es/ecma262/#sec-typedarraybytelength
|
||||
u32 typed_array_byte_length(TypedArrayWithBufferWitness const& typed_array_record)
|
||||
{
|
||||
// 1. If IsTypedArrayOutOfBounds(taRecord) is true, return 0.
|
||||
|
@ -645,7 +645,7 @@ u32 typed_array_byte_length(TypedArrayWithBufferWitness const& typed_array_recor
|
|||
return length * element_size;
|
||||
}
|
||||
|
||||
// 10.4.5.12 TypedArrayLength ( taRecord ), https://tc39.es/ecma262/#sec-typedarraylength
|
||||
// 10.4.5.13 TypedArrayLength ( taRecord ), https://tc39.es/ecma262/#sec-typedarraylength
|
||||
u32 typed_array_length(TypedArrayWithBufferWitness const& typed_array_record)
|
||||
{
|
||||
// 1. Assert: IsTypedArrayOutOfBounds(taRecord) is false.
|
||||
|
@ -677,7 +677,7 @@ u32 typed_array_length(TypedArrayWithBufferWitness const& typed_array_record)
|
|||
return (byte_length.length() - byte_offset) / element_size;
|
||||
}
|
||||
|
||||
// 10.4.5.13 IsTypedArrayOutOfBounds ( taRecord ), https://tc39.es/ecma262/#sec-istypedarrayoutofbounds
|
||||
// 10.4.5.14 IsTypedArrayOutOfBounds ( taRecord ), https://tc39.es/ecma262/#sec-istypedarrayoutofbounds
|
||||
bool is_typed_array_out_of_bounds(TypedArrayWithBufferWitness const& typed_array_record)
|
||||
{
|
||||
// 1. Let O be taRecord.[[Object]].
|
||||
|
@ -720,7 +720,25 @@ bool is_typed_array_out_of_bounds(TypedArrayWithBufferWitness const& typed_array
|
|||
return false;
|
||||
}
|
||||
|
||||
// 10.4.5.14 IsValidIntegerIndex ( O, index ), https://tc39.es/ecma262/#sec-isvalidintegerindex
|
||||
// 10.4.5.15 IsTypedArrayFixedLength ( O ), https://tc39.es/ecma262/#sec-istypedarrayfixedlength
|
||||
bool is_typed_array_fixed_length(TypedArrayBase const& typed_array)
|
||||
{
|
||||
// 1. If O.[[ArrayLength]] is AUTO, return false.
|
||||
if (typed_array.array_length().is_auto())
|
||||
return false;
|
||||
|
||||
// 2. Let buffer be O.[[ViewedArrayBuffer]].
|
||||
auto const* buffer = typed_array.viewed_array_buffer();
|
||||
|
||||
// 3. If IsFixedLengthArrayBuffer(buffer) is false and IsSharedArrayBuffer(buffer) is false, return false.
|
||||
if (!buffer->is_fixed_length() && !buffer->is_shared_array_buffer())
|
||||
return false;
|
||||
|
||||
// 4. Return true.
|
||||
return true;
|
||||
}
|
||||
|
||||
// 10.4.5.16 IsValidIntegerIndex ( O, index ), https://tc39.es/ecma262/#sec-isvalidintegerindex
|
||||
bool is_valid_integer_index_slow_case(TypedArrayBase const& typed_array, CanonicalIndex property_index)
|
||||
{
|
||||
// 4. Let taRecord be MakeTypedArrayWithBufferWitnessRecord(O, unordered).
|
||||
|
|
|
@ -88,7 +88,7 @@ private:
|
|||
virtual void visit_edges(Visitor&) override;
|
||||
};
|
||||
|
||||
// 10.4.5.8 TypedArray With Buffer Witness Records, https://tc39.es/ecma262/#sec-typedarray-with-buffer-witness-records
|
||||
// 10.4.5.9 TypedArray With Buffer Witness Records, https://tc39.es/ecma262/#sec-typedarray-with-buffer-witness-records
|
||||
struct TypedArrayWithBufferWitness {
|
||||
GC::Ref<TypedArrayBase const> object; // [[Object]]
|
||||
ByteLength cached_buffer_byte_length; // [[CachedBufferByteLength]]
|
||||
|
@ -98,9 +98,10 @@ TypedArrayWithBufferWitness make_typed_array_with_buffer_witness_record(TypedArr
|
|||
u32 typed_array_byte_length(TypedArrayWithBufferWitness const&);
|
||||
u32 typed_array_length(TypedArrayWithBufferWitness const&);
|
||||
bool is_typed_array_out_of_bounds(TypedArrayWithBufferWitness const&);
|
||||
bool is_typed_array_fixed_length(TypedArrayBase const&);
|
||||
bool is_valid_integer_index_slow_case(TypedArrayBase const&, CanonicalIndex property_index);
|
||||
|
||||
// 10.4.5.14 IsValidIntegerIndex ( O, index ), https://tc39.es/ecma262/#sec-isvalidintegerindex
|
||||
// 10.4.5.16 IsValidIntegerIndex ( O, index ), https://tc39.es/ecma262/#sec-isvalidintegerindex
|
||||
inline bool is_valid_integer_index(TypedArrayBase const& typed_array, CanonicalIndex property_index)
|
||||
{
|
||||
// 1. If IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is true, return false.
|
||||
|
@ -127,7 +128,7 @@ inline bool is_valid_integer_index(TypedArrayBase const& typed_array, CanonicalI
|
|||
return is_valid_integer_index_slow_case(typed_array, property_index);
|
||||
}
|
||||
|
||||
// 10.4.5.15 TypedArrayGetElement ( O, index ), https://tc39.es/ecma262/#sec-typedarraygetelement
|
||||
// 10.4.5.17 TypedArrayGetElement ( O, index ), https://tc39.es/ecma262/#sec-typedarraygetelement
|
||||
template<typename T>
|
||||
inline ThrowCompletionOr<Value> typed_array_get_element(TypedArrayBase const& typed_array, CanonicalIndex property_index)
|
||||
{
|
||||
|
@ -155,7 +156,7 @@ inline ThrowCompletionOr<Value> typed_array_get_element(TypedArrayBase const& ty
|
|||
return typed_array.viewed_array_buffer()->template get_value<T>(byte_index_in_buffer.value(), true, ArrayBuffer::Order::Unordered);
|
||||
}
|
||||
|
||||
// 10.4.5.16 TypedArraySetElement ( O, index, value ), https://tc39.es/ecma262/#sec-typedarraysetelement
|
||||
// 10.4.5.18 TypedArraySetElement ( O, index, value ), https://tc39.es/ecma262/#sec-typedarraysetelement
|
||||
// NOTE: In error cases, the function will return as if it succeeded.
|
||||
template<typename T>
|
||||
inline ThrowCompletionOr<void> typed_array_set_element(TypedArrayBase& typed_array, CanonicalIndex property_index, Value value)
|
||||
|
@ -207,7 +208,22 @@ class TypedArray : public TypedArrayBase {
|
|||
using UnderlyingBufferDataType = Conditional<IsSame<ClampedU8, T>, u8, T>;
|
||||
|
||||
public:
|
||||
// 10.4.5.1 [[GetOwnProperty]] ( P ), https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-getownproperty-p
|
||||
// 10.4.5.1 [[PreventExtensions]] ( ), https://tc39.es/ecma262/#sec-typedarray-preventextensions
|
||||
virtual ThrowCompletionOr<bool> internal_prevent_extensions() override
|
||||
{
|
||||
// 1. NOTE: The extensibility-related invariants specified in 6.1.7.3 do not allow this method to return true
|
||||
// when O can gain (or lose and then regain) properties, which might occur for properties with integer index
|
||||
// names when its underlying buffer is resized.
|
||||
|
||||
// 2. If IsTypedArrayFixedLength(O) is false, return false.
|
||||
if (!is_typed_array_fixed_length(*this))
|
||||
return false;
|
||||
|
||||
// 3. Return OrdinaryPreventExtensions(O).
|
||||
return Object::internal_prevent_extensions();
|
||||
}
|
||||
|
||||
// 10.4.5.2 [[GetOwnProperty]] ( P ), https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-getownproperty-p
|
||||
virtual ThrowCompletionOr<Optional<PropertyDescriptor>> internal_get_own_property(PropertyKey const& property_key) const override
|
||||
{
|
||||
// NOTE: If the property name is a number type (An implementation-defined optimized
|
||||
|
@ -242,7 +258,7 @@ public:
|
|||
return Object::internal_get_own_property(property_key);
|
||||
}
|
||||
|
||||
// 10.4.5.2 [[HasProperty]] ( P ), https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-hasproperty-p
|
||||
// 10.4.5.3 [[HasProperty]] ( P ), https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-hasproperty-p
|
||||
virtual ThrowCompletionOr<bool> internal_has_property(PropertyKey const& property_key) const override
|
||||
{
|
||||
// NOTE: If the property name is a number type (An implementation-defined optimized
|
||||
|
@ -263,7 +279,7 @@ public:
|
|||
return Object::internal_has_property(property_key);
|
||||
}
|
||||
|
||||
// 10.4.5.3 [[DefineOwnProperty]] ( P, Desc ), https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-defineownproperty-p-desc
|
||||
// 10.4.5.4 [[DefineOwnProperty]] ( P, Desc ), https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-defineownproperty-p-desc
|
||||
virtual ThrowCompletionOr<bool> internal_define_own_property(PropertyKey const& property_key, PropertyDescriptor const& property_descriptor, Optional<PropertyDescriptor>* precomputed_get_own_property = nullptr) override
|
||||
{
|
||||
// NOTE: If the property name is a number type (An implementation-defined optimized
|
||||
|
@ -310,7 +326,7 @@ public:
|
|||
return Object::internal_define_own_property(property_key, property_descriptor, precomputed_get_own_property);
|
||||
}
|
||||
|
||||
// 10.4.5.4 [[Get]] ( P, Receiver ), https://tc39.es/ecma262/#sec-typedarray-get
|
||||
// 10.4.5.5 [[Get]] ( P, Receiver ), https://tc39.es/ecma262/#sec-typedarray-get
|
||||
virtual ThrowCompletionOr<Value> internal_get(PropertyKey const& property_key, Value receiver, CacheablePropertyMetadata* cacheable_metadata, PropertyLookupPhase phase) const override
|
||||
{
|
||||
VERIFY(!receiver.is_empty());
|
||||
|
@ -335,7 +351,7 @@ public:
|
|||
return Object::internal_get(property_key, receiver, cacheable_metadata, phase);
|
||||
}
|
||||
|
||||
// 10.4.5.5 [[Set]] ( P, V, Receiver ), https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-set-p-v-receiver
|
||||
// 10.4.5.6 [[Set]] ( P, V, Receiver ), https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-set-p-v-receiver
|
||||
virtual ThrowCompletionOr<bool> internal_set(PropertyKey const& property_key, Value value, Value receiver, CacheablePropertyMetadata*) override
|
||||
{
|
||||
VERIFY(!value.is_empty());
|
||||
|
@ -371,7 +387,7 @@ public:
|
|||
return Object::internal_set(property_key, value, receiver);
|
||||
}
|
||||
|
||||
// 10.4.5.6 [[Delete]] ( P ), https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-delete-p
|
||||
// 10.4.5.7 [[Delete]] ( P ), https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-delete-p
|
||||
virtual ThrowCompletionOr<bool> internal_delete(PropertyKey const& property_key) override
|
||||
{
|
||||
// NOTE: If the property name is a number type (An implementation-defined optimized
|
||||
|
@ -396,7 +412,7 @@ public:
|
|||
return Object::internal_delete(property_key);
|
||||
}
|
||||
|
||||
// 10.4.5.7 [[OwnPropertyKeys]] ( ), https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-ownpropertykeys
|
||||
// 10.4.5.8 [[OwnPropertyKeys]] ( ), https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-ownpropertykeys
|
||||
virtual ThrowCompletionOr<GC::MarkedVector<Value>> internal_own_property_keys() const override
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
|
|
|
@ -83,3 +83,28 @@ test("freeze with huge number of properties doesn't crash", () => {
|
|||
}
|
||||
Object.freeze(o);
|
||||
});
|
||||
|
||||
test("freeze with TypedArray", () => {
|
||||
const TYPED_ARRAYS = [
|
||||
Uint8Array,
|
||||
Uint8ClampedArray,
|
||||
Uint16Array,
|
||||
Uint32Array,
|
||||
Int8Array,
|
||||
Int16Array,
|
||||
Int32Array,
|
||||
Float16Array,
|
||||
Float32Array,
|
||||
Float64Array,
|
||||
];
|
||||
|
||||
const buffer = new ArrayBuffer(5, { maxByteLength: 10 });
|
||||
|
||||
TYPED_ARRAYS.forEach(T => {
|
||||
const typedArray = new T(buffer, 0, 0);
|
||||
|
||||
expect(() => {
|
||||
Object.freeze(typedArray);
|
||||
}).toThrowWithMessage(TypeError, "Could not freeze object");
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue