1
0
Fork 0
mirror of https://github.com/LadybirdBrowser/ladybird.git synced 2025-06-09 09:34:57 +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:
Timothy Flynn 2024-11-29 13:28:55 -05:00 committed by Andreas Kling
parent c6e684791f
commit 53a507303c
Notes: github-actions[bot] 2024-11-30 10:21:05 +00:00
3 changed files with 75 additions and 16 deletions

View file

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

View file

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

View file

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