summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Userland/Libraries/LibJS/Runtime/Array.cpp2
-rw-r--r--Userland/Libraries/LibJS/Runtime/IndexedProperties.cpp12
-rw-r--r--Userland/Libraries/LibJS/Runtime/Value.h11
-rw-r--r--Userland/Libraries/LibJS/Tests/builtins/Array/array-length-setter.js10
4 files changed, 34 insertions, 1 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/Array.cpp b/Userland/Libraries/LibJS/Runtime/Array.cpp
index dabf19f9fb..c04a65c1d8 100644
--- a/Userland/Libraries/LibJS/Runtime/Array.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Array.cpp
@@ -69,7 +69,7 @@ JS_DEFINE_NATIVE_GETTER(Array::length_getter)
auto* array = typed_this(vm, global_object);
if (!array)
return {};
- return Value(static_cast<i32>(array->indexed_properties().array_like_size()));
+ return Value(array->indexed_properties().array_like_size());
}
JS_DEFINE_NATIVE_SETTER(Array::length_setter)
diff --git a/Userland/Libraries/LibJS/Runtime/IndexedProperties.cpp b/Userland/Libraries/LibJS/Runtime/IndexedProperties.cpp
index 5f285838d8..4993c9cf8f 100644
--- a/Userland/Libraries/LibJS/Runtime/IndexedProperties.cpp
+++ b/Userland/Libraries/LibJS/Runtime/IndexedProperties.cpp
@@ -333,6 +333,18 @@ void IndexedProperties::append_all(Object* this_object, const IndexedProperties&
void IndexedProperties::set_array_like_size(size_t new_size)
{
+ constexpr size_t length_setter_generic_storage_threshold = 4 * MiB;
+ auto current_array_like_size = array_like_size();
+
+ // We can't use simple storage for lengths that don't fit in an i32.
+ // Also, to avoid gigantic unused storage allocations, let's put an (arbitrary) 4M cap on simple storage here.
+ // This prevents something like "a = []; a.length = 0x80000000;" from allocating 2G entries.
+ if (m_storage->is_simple_storage()
+ && (new_size > NumericLimits<i32>::max()
+ || (current_array_like_size < length_setter_generic_storage_threshold && new_size > length_setter_generic_storage_threshold))) {
+ switch_to_generic_storage();
+ }
+
m_storage->set_array_like_size(new_size);
}
diff --git a/Userland/Libraries/LibJS/Runtime/Value.h b/Userland/Libraries/LibJS/Runtime/Value.h
index d0e51bc2e1..e763cfa0bf 100644
--- a/Userland/Libraries/LibJS/Runtime/Value.h
+++ b/Userland/Libraries/LibJS/Runtime/Value.h
@@ -122,6 +122,17 @@ public:
}
}
+ explicit Value(unsigned long value)
+ {
+ if (value > NumericLimits<i32>::max()) {
+ m_value.as_double = static_cast<double>(value);
+ m_type = Type::Double;
+ } else {
+ m_value.as_i32 = static_cast<i32>(value);
+ m_type = Type::Int32;
+ }
+ }
+
explicit Value(unsigned value)
{
if (value > NumericLimits<i32>::max()) {
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 9a1043f5d4..662fd6e04e 100644
--- a/Userland/Libraries/LibJS/Tests/builtins/Array/array-length-setter.js
+++ b/Userland/Libraries/LibJS/Tests/builtins/Array/array-length-setter.js
@@ -34,4 +34,14 @@ describe("normal behavior", () => {
a.length = true;
expect(a).toHaveLength(1);
});
+
+ test("setting a huge array length", () => {
+ var a = [];
+ a.length = 0x80000000;
+ expect(a.length).toEqual(0x80000000);
+
+ var b = [];
+ b.length = 0x80000001;
+ expect(b.length).toEqual(0x80000001);
+ });
});