LibJS: Allow setting the length of an object with prototype Array

Before this it would always go through the native setter thus
 modifying the array but now you can set length to anything
This commit is contained in:
davidot 2021-06-25 16:57:12 +02:00 committed by Linus Groh
parent b38fb418f8
commit b1441a47b1
Notes: sideshowbarker 2024-07-18 11:28:44 +09:00
2 changed files with 92 additions and 7 deletions

View File

@ -65,25 +65,50 @@ Array* Array::typed_this(VM& vm, GlobalObject& global_object)
JS_DEFINE_NATIVE_GETTER(Array::length_getter)
{
auto* array = typed_this(vm, global_object);
if (!array)
auto* this_object = vm.this_value(global_object).to_object(global_object);
if (!this_object)
return {};
return Value(array->indexed_properties().array_like_size());
// TODO: could be incorrect if receiver/this_value is fixed or changed
if (!this_object->is_array()) {
Value val = this_object->get_own_property(vm.names.length.to_string_or_symbol(), this_object);
if (vm.exception())
return {};
return val;
}
return Value(this_object->indexed_properties().array_like_size());
}
JS_DEFINE_NATIVE_SETTER(Array::length_setter)
{
auto* array = typed_this(vm, global_object);
if (!array)
auto* this_object = vm.this_value(global_object).to_object(global_object);
if (!this_object)
return;
// TODO: could be incorrect if receiver/this_value is fixed or changed
if (!this_object->is_array()) {
this_object->define_property(vm.names.length.to_string_or_symbol(), value, default_attributes);
if (vm.exception())
return;
return;
}
auto length = value.to_number(global_object);
if (vm.exception())
return;
if (length.is_nan() || length.is_infinity() || length.as_double() < 0) {
u32 val = length.as_double();
if (val != length.as_double()
|| length.is_nan()
|| length.is_infinity()
|| length.as_double() < 0
|| length.as_double() > NumericLimits<u32>::max()) {
vm.throw_exception<RangeError>(global_object, ErrorType::InvalidLength, "array");
return;
}
array->indexed_properties().set_array_like_size(length.as_double());
this_object->indexed_properties().set_array_like_size(val);
}
}

View File

@ -55,3 +55,63 @@ describe("normal behavior", () => {
expect(a[1]).toEqual(2);
});
});
describe("behavior when obj has Array prototype", () => {
function ArrExtend() {}
ArrExtend.prototype = [10, 11, 12];
test("Has the properties from prototype", () => {
var arr = new ArrExtend();
expect(arr.length).toEqual(3);
expect(arr[0]).toEqual(10);
expect(arr[1]).toEqual(11);
expect(arr[2]).toEqual(12);
});
test("Can override length to any value", () => {
[null, "Hello friends :^)", -6, 0].forEach(value => {
var arr = new ArrExtend();
arr.length = value;
expect(arr.length).toEqual(value);
// should not wipe high values
expect(arr[0]).toEqual(10);
expect(arr[1]).toEqual(11);
expect(arr[2]).toEqual(12);
});
});
test("Can call array methods", () => {
var arr = new ArrExtend();
arr.push(1);
expect(arr.length).toEqual(4);
expect(arr[3]).toEqual(1);
});
test("If length overwritten uses that value", () => {
[null, "Hello friends :^)", -6, 0].forEach(value => {
var arr = new ArrExtend();
arr.length = value;
expect(arr.length).toEqual(value);
arr.push(99);
expect(arr.length).toEqual(1);
expect(arr[0]).toEqual(99);
// should not wipe higher value
expect(arr[1]).toEqual(11);
expect(arr[2]).toEqual(12);
arr.push(100);
expect(arr.length).toEqual(2);
expect(arr[1]).toEqual(100);
arr.length = 0;
// should not wipe values since we are not an array
expect(arr[0]).toEqual(99);
expect(arr[1]).toEqual(100);
expect(arr[2]).toEqual(12);
});
});
});