mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-09-12 12:32:21 +00: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
|
JS_ENUMERATE_TYPED_ARRAYS
|
||||||
#undef __JS_ENUMERATE
|
#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)
|
TypedArrayWithBufferWitness make_typed_array_with_buffer_witness_record(TypedArrayBase const& typed_array, ArrayBuffer::Order order)
|
||||||
{
|
{
|
||||||
// 1. Let buffer be obj.[[ViewedArrayBuffer]].
|
// 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) };
|
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)
|
u32 typed_array_byte_length(TypedArrayWithBufferWitness const& typed_array_record)
|
||||||
{
|
{
|
||||||
// 1. If IsTypedArrayOutOfBounds(taRecord) is true, return 0.
|
// 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;
|
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)
|
u32 typed_array_length(TypedArrayWithBufferWitness const& typed_array_record)
|
||||||
{
|
{
|
||||||
// 1. Assert: IsTypedArrayOutOfBounds(taRecord) is false.
|
// 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;
|
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)
|
bool is_typed_array_out_of_bounds(TypedArrayWithBufferWitness const& typed_array_record)
|
||||||
{
|
{
|
||||||
// 1. Let O be taRecord.[[Object]].
|
// 1. Let O be taRecord.[[Object]].
|
||||||
|
@ -720,7 +720,25 @@ bool is_typed_array_out_of_bounds(TypedArrayWithBufferWitness const& typed_array
|
||||||
return false;
|
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)
|
bool is_valid_integer_index_slow_case(TypedArrayBase const& typed_array, CanonicalIndex property_index)
|
||||||
{
|
{
|
||||||
// 4. Let taRecord be MakeTypedArrayWithBufferWitnessRecord(O, unordered).
|
// 4. Let taRecord be MakeTypedArrayWithBufferWitnessRecord(O, unordered).
|
||||||
|
|
|
@ -88,7 +88,7 @@ private:
|
||||||
virtual void visit_edges(Visitor&) override;
|
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 {
|
struct TypedArrayWithBufferWitness {
|
||||||
GC::Ref<TypedArrayBase const> object; // [[Object]]
|
GC::Ref<TypedArrayBase const> object; // [[Object]]
|
||||||
ByteLength cached_buffer_byte_length; // [[CachedBufferByteLength]]
|
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_byte_length(TypedArrayWithBufferWitness const&);
|
||||||
u32 typed_array_length(TypedArrayWithBufferWitness const&);
|
u32 typed_array_length(TypedArrayWithBufferWitness const&);
|
||||||
bool is_typed_array_out_of_bounds(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);
|
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)
|
inline bool is_valid_integer_index(TypedArrayBase const& typed_array, CanonicalIndex property_index)
|
||||||
{
|
{
|
||||||
// 1. If IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is true, return false.
|
// 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);
|
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>
|
template<typename T>
|
||||||
inline ThrowCompletionOr<Value> typed_array_get_element(TypedArrayBase const& typed_array, CanonicalIndex property_index)
|
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);
|
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.
|
// NOTE: In error cases, the function will return as if it succeeded.
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline ThrowCompletionOr<void> typed_array_set_element(TypedArrayBase& typed_array, CanonicalIndex property_index, Value value)
|
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>;
|
using UnderlyingBufferDataType = Conditional<IsSame<ClampedU8, T>, u8, T>;
|
||||||
|
|
||||||
public:
|
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
|
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
|
// 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);
|
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
|
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
|
// 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);
|
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
|
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
|
// 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);
|
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
|
virtual ThrowCompletionOr<Value> internal_get(PropertyKey const& property_key, Value receiver, CacheablePropertyMetadata* cacheable_metadata, PropertyLookupPhase phase) const override
|
||||||
{
|
{
|
||||||
VERIFY(!receiver.is_empty());
|
VERIFY(!receiver.is_empty());
|
||||||
|
@ -335,7 +351,7 @@ public:
|
||||||
return Object::internal_get(property_key, receiver, cacheable_metadata, phase);
|
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
|
virtual ThrowCompletionOr<bool> internal_set(PropertyKey const& property_key, Value value, Value receiver, CacheablePropertyMetadata*) override
|
||||||
{
|
{
|
||||||
VERIFY(!value.is_empty());
|
VERIFY(!value.is_empty());
|
||||||
|
@ -371,7 +387,7 @@ public:
|
||||||
return Object::internal_set(property_key, value, receiver);
|
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
|
virtual ThrowCompletionOr<bool> internal_delete(PropertyKey const& property_key) override
|
||||||
{
|
{
|
||||||
// NOTE: If the property name is a number type (An implementation-defined optimized
|
// NOTE: If the property name is a number type (An implementation-defined optimized
|
||||||
|
@ -396,7 +412,7 @@ public:
|
||||||
return Object::internal_delete(property_key);
|
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
|
virtual ThrowCompletionOr<GC::MarkedVector<Value>> internal_own_property_keys() const override
|
||||||
{
|
{
|
||||||
auto& vm = this->vm();
|
auto& vm = this->vm();
|
||||||
|
|
|
@ -83,3 +83,28 @@ test("freeze with huge number of properties doesn't crash", () => {
|
||||||
}
|
}
|
||||||
Object.freeze(o);
|
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