From 418092a71aa2e2366a9c5d5a40ac1a390ca68ea2 Mon Sep 17 00:00:00 2001 From: Linus Groh Date: Thu, 23 Apr 2020 00:33:13 +0100 Subject: [PATCH] LibJS: Implement Array length setter --- Libraries/LibJS/Runtime/Array.cpp | 30 +++++++++--- Libraries/LibJS/Runtime/Array.h | 3 +- Libraries/LibJS/Runtime/ArrayPrototype.cpp | 12 ----- Libraries/LibJS/Tests/array-length-setter.js | 48 ++++++++++++++++++++ 4 files changed, 74 insertions(+), 19 deletions(-) create mode 100644 Libraries/LibJS/Tests/array-length-setter.js diff --git a/Libraries/LibJS/Runtime/Array.cpp b/Libraries/LibJS/Runtime/Array.cpp index 4ade3791e3..2bf7f2ef33 100644 --- a/Libraries/LibJS/Runtime/Array.cpp +++ b/Libraries/LibJS/Runtime/Array.cpp @@ -49,19 +49,37 @@ Array::~Array() { } -Value Array::length_getter(Interpreter& interpreter) +Array* array_from(Interpreter& interpreter) { auto* this_object = interpreter.this_value().to_object(interpreter.heap()); if (!this_object) return {}; - if (!this_object->is_array()) - return interpreter.throw_exception("Not an array"); - return Value(static_cast(this_object)->length()); + if (!this_object->is_array()) { + interpreter.throw_exception("Not an Array"); + return nullptr; + } + return static_cast(this_object); } -void Array::length_setter(Interpreter&, Value) +Value Array::length_getter(Interpreter& interpreter) { - ASSERT_NOT_REACHED(); + auto* array = array_from(interpreter); + if (!array) + return {}; + return Value(array->length()); +} + +void Array::length_setter(Interpreter& interpreter, Value value) +{ + auto* array = array_from(interpreter); + if (!array) + return; + auto length = value.to_number(); + if (length.is_nan() || length.is_infinity() || length.as_double() < 0) { + interpreter.throw_exception("Invalid array length"); + return; + } + array->elements().resize(length.as_double()); } } diff --git a/Libraries/LibJS/Runtime/Array.h b/Libraries/LibJS/Runtime/Array.h index d27f407d3e..6b580b2889 100644 --- a/Libraries/LibJS/Runtime/Array.h +++ b/Libraries/LibJS/Runtime/Array.h @@ -30,6 +30,8 @@ namespace JS { +Array* array_from(Interpreter&); + class Array final : public Object { public: static Array* create(GlobalObject&); @@ -47,5 +49,4 @@ private: static void length_setter(Interpreter&, Value); }; - } diff --git a/Libraries/LibJS/Runtime/ArrayPrototype.cpp b/Libraries/LibJS/Runtime/ArrayPrototype.cpp index 67a5630bca..eeb69db11b 100644 --- a/Libraries/LibJS/Runtime/ArrayPrototype.cpp +++ b/Libraries/LibJS/Runtime/ArrayPrototype.cpp @@ -63,18 +63,6 @@ ArrayPrototype::~ArrayPrototype() { } -static Array* array_from(Interpreter& interpreter) -{ - auto* this_object = interpreter.this_value().to_object(interpreter.heap()); - if (!this_object) - return {}; - if (!this_object->is_array()) { - interpreter.throw_exception("Not an Array"); - return nullptr; - } - return static_cast(this_object); -} - static Function* callback_from_args(Interpreter& interpreter, const String& name) { if (interpreter.argument_count() < 1) { diff --git a/Libraries/LibJS/Tests/array-length-setter.js b/Libraries/LibJS/Tests/array-length-setter.js new file mode 100644 index 0000000000..d4c2d49460 --- /dev/null +++ b/Libraries/LibJS/Tests/array-length-setter.js @@ -0,0 +1,48 @@ +load("test-common.js"); + +try { + var a = [1, 2, 3]; + + assert(a.length === 3); + assert(a[0] === 1); + assert(a[1] === 2); + assert(a[2] === 3); + + a.length = 5; + assert(a.length === 5); + assert(a[0] === 1); + assert(a[1] === 2); + assert(a[2] === 3); + assert(a[3] === undefined); + assert(a[4] === undefined); + + a.length = 1; + assert(a.length === 1); + assert(a[0] === 1); + + a.length = 0; + assert(a.length === 0); + + a.length = "42"; + assert(a.length === 42); + + a.length = []; + assert(a.length === 0); + + a.length = true; + assert(a.length === 1); + + [undefined, "foo", -1, Infinity, -Infinity, NaN].forEach(value => { + assertThrowsError(() => { + a.length = value; + }, { + error: RangeError, + message: "Invalid array length" + }); + assert(a.length === 1); + }); + + console.log("PASS"); +} catch (e) { + console.log("FAIL: " + e); +}