diff --git a/Userland/Libraries/LibJS/Runtime/Array.cpp b/Userland/Libraries/LibJS/Runtime/Array.cpp index 232a5a42e2f..314688df309 100644 --- a/Userland/Libraries/LibJS/Runtime/Array.cpp +++ b/Userland/Libraries/LibJS/Runtime/Array.cpp @@ -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::max()) { vm.throw_exception(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); } } diff --git a/Userland/Libraries/LibJS/Tests/builtins/Array/array-length-setter.js b/Userland/Libraries/LibJS/Tests/builtins/Array/array-length-setter.js index 14a8269110b..1ac49e125ba 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/Array/array-length-setter.js +++ b/Userland/Libraries/LibJS/Tests/builtins/Array/array-length-setter.js @@ -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); + }); + }); +});